<> <> <> <<>> DIRECTORY Basics, FunctionCache, Imager, ImagerCache, ImagerColorDefs, ImagerColorOperator, ImagerPixelArray, ImagerPixelArrayDefs, ImagerPixelMap, ImagerRaster, ImagerSample, ImagerSmooth, ImagerTransformation, PrincOps, PrincOpsUtils, Real, Vector2, ImagerColorPrivate, ImagerDevice, PixelMapOps, ImagerRasterPrivate, ImagerOps; ImagerSmoothImpl: CEDAR PROGRAM IMPORTS Basics, FunctionCache, Imager, ImagerCache, ImagerColorOperator, ImagerPixelArray, ImagerPixelMap, ImagerRaster, ImagerSample, ImagerTransformation, PrincOpsUtils, Real, PixelMapOps, ImagerOps EXPORTS ImagerSmooth, ImagerColorDefs, ImagerSample ~ BEGIN VEC: TYPE ~ Vector2.VEC; Transformation: TYPE ~ ImagerTransformation.Transformation; Color: TYPE ~ ImagerColorDefs.Color; ConstantColor: TYPE ~ ImagerColorDefs.ConstantColor; SampledColor: TYPE ~ ImagerColorDefs.SampledColor; ColorOperator: TYPE ~ ImagerColorDefs.ColorOperator; PixelArray: TYPE ~ ImagerPixelArrayDefs.PixelArray; PixelMap: TYPE ~ ImagerPixelMap.PixelMap; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Sample: TYPE ~ ImagerSample.Sample; Sampler: TYPE ~ ImagerSample.Sampler; SampleBuffer: TYPE ~ ImagerSample.SampleBuffer; UnsafeSamples: TYPE ~ ImagerSample.UnsafeSamples; DeviceBox: TYPE ~ ImagerDevice.DeviceBox; Rectangle: TYPE ~ ImagerTransformation.Rectangle; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; RunProc: TYPE ~ ImagerDevice.RunProc; BoxProc: TYPE ~ ImagerDevice.BoxProc; ConstantColorImpl: TYPE ~ ImagerColorPrivate.ConstantColorImpl; ConstantColorImplRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorImplRep; ConstantColorClass: TYPE ~ ImagerColorPrivate.ConstantColorClass; ConstantColorClassRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorClassRep; bitsPerWord: NAT ~ Basics.bitsPerWord; <> class: ImagerDevice.Class ~ NEW[ImagerDevice.ClassRep _ [ type: $Smooth, SetColor: SmoothSetColor, SetPriority: SmoothSetPriority, SetHalftone: SmoothSetHalftone, MaskRuns: SmoothMaskRuns, MaskBoxes: NIL, MaskBits: NIL, MoveBoxes: SmoothMoveBoxes ]]; Lg: PROC [n: NAT] RETURNS [NAT] ~ { RETURN[SELECT n FROM 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, ENDCASE => ERROR] }; fontCacheID: ATOM ~ $Smooth; fontCacheSize: NAT _ 4000; fontRastWeight: REAL _ 0.0; Case: TYPE ~ {nil, constant, stipple, sampled}; StippleArray: TYPE ~ PACKED ARRAY [0..16) OF WORD; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD[ component: ATOM, pixelMap: ImagerPixelMap.PixelMap, surfaceUnitsPerPixel: NAT, maxValue: Sample, sOrigin, fOrigin: INTEGER, -- used for providing d to change proc. deviceToPixel: Transformation, change: PROC[changeData: REF, d: DeviceRectangle, action: PROC], changeData: REF, case: Case _ nil, -- what type of color zerosAreClear: BOOL _ FALSE, -- special case for sampled black clear. constant: Sample _ 0, -- for case = constant stipple: StippleArray _ ALL[0], -- stipple pattern fTileOrg: NAT _ 0, sTileOrg: NAT _ 0, sampledColor: ImagerColorDefs.SampledColor _ NIL, -- sampled color sampledColorData: SampledColorData _ NIL, -- cached data associated with sampledColor paToPixel: Transformation _ NIL, -- transformation from pa coords to pixelMap coords sampBuffer: ImagerSample.SampleBuffer _, -- scan line buffer for samples from sampledColor lineBuffer: ImagerSample.SampleBuffer _, -- for ops that cannot go directly to frame sampler: ImagerSample.Sampler _ -- sampler information ]; SampledColorData: TYPE ~ REF SampledColorDataRep; SampledColorDataRep: TYPE ~ RECORD [ source: ImagerPixelMap.PixelMap, key: SampledColorKey ]; MakePixelArray: PUBLIC PROC [action: PROC [Context], sSize, fSize: NAT, components: LIST OF ATOM, viewToPixel: Transformation _ NIL, initialScale: REAL _ 1.0, lgBitsPerSample: NAT _ 3, cacheFonts: BOOL _ TRUE, surfaceUnitsPerPixel: NAT _ 5, blackBackground: BOOL _ FALSE] RETURNS [PixelArray] ~ { pixelMap: PixelMap ~ ImagerPixelMap.Create[lgBitsPerSample, [0, 0, sSize, fSize]]; c: Context ~ Create[pixelMap: pixelMap, component: $Intensity, viewToPixel: viewToPixel, initialScale: initialScale, change: NIL, changeData: NIL, cacheFonts: cacheFonts, surfaceUnitsPerPixel: surfaceUnitsPerPixel]; proc: PROC ~ {action[c]}; list: LIST OF PixelMap _ NIL; last: LIST OF PixelMap _ NIL; FOR a: LIST OF ATOM _ components, a.rest UNTIL a=NIL DO pm: PixelMap; IF blackBackground THEN ImagerPixelMap.Clear[pixelMap] ELSE ImagerPixelMap.Fill[pixelMap, [0, 0, sSize, fSize], CARDINAL.LAST]; SetComponent[c, a.first]; Imager.DoSaveAll[c, proc]; IF a.rest = NIL THEN pm _ pixelMap ELSE pm _ ImagerPixelMap.Copy[pixelMap]; IF last = NIL THEN {list _ last _ LIST[pm]} ELSE {last.rest _ LIST[pm]; last _ last.rest}; ENDLOOP; RETURN [ImagerOps.PixelArrayFromPixelMaps[list, ImagerTransformation.Invert[viewToPixel]]] }; SetComponent: PUBLIC PROC [context: Context, component: ATOM] ~ { WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { WITH rasterData.device.data SELECT FROM data: Data => { data.component _ component; }; ENDCASE => NULL; }; ENDCASE => NULL; }; LikeScreen: PUBLIC PROC [sSize: NAT] RETURNS [Transformation] ~ { m: Transformation ~ ImagerTransformation.Translate[[sSize, 0]]; m.ApplyPreRotate[90]; RETURN [m]; }; Create: PUBLIC PROC [pixelMap: PixelMap, component: ATOM, viewToPixel: Transformation _ NIL, initialScale: REAL _ 1.0, change: PROC[changeData: REF, d: DeviceRectangle, action: PROC] _ NIL, changeData: REF _ NIL, cacheFonts: BOOL _ TRUE, surfaceUnitsPerPixel: NAT _ 5] RETURNS [context: Context] ~ { device: ImagerDevice.Device ~ DeviceFromPixelMap[pixelMap, component, viewToPixel, change, changeData, surfaceUnitsPerPixel]; fontCache: ImagerCache.Ref ~ IF cacheFonts THEN ImagerCache.GetNamedCache[atom: fontCacheID, createSize: fontCacheSize] ELSE NIL; context _ ImagerRaster.Create[device: device, pixelUnits: TRUE, fontCache: fontCache, rastWeight: fontRastWeight]; Imager.ScaleT[context, initialScale]; }; DeviceBoxFromRectangle: PROC [r: Rectangle] RETURNS [DeviceBox] ~ { smin: CARDINAL _ Real.RoundC[r.x]; fmin: CARDINAL _ Real.RoundC[r.y]; smax: CARDINAL _ Real.RoundC[r.x+r.w]; fmax: CARDINAL _ Real.RoundC[r.y+r.h]; IF smin > smax THEN {t: CARDINAL _ smin; smin _ smax; smax _ t}; IF fmin > fmax THEN {t: CARDINAL _ fmin; fmin _ fmax; fmax _ t}; RETURN [[smin: smin, fmin: fmin, smax: smax, fmax: fmax]] }; DBFromDR: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ { RETURN [[smin: r.sMin, fmin: r.fMin, smax: r.sMin+r.sSize, fmax: r.fMin+r.fSize]] }; DeviceFromPixelMap: PROC [pixelMap: PixelMap, component: ATOM, viewToPixel: Transformation, change: PROC[changeData: REF, d: DeviceRectangle, action: PROC], changeData: REF, surfaceUnitsPerPixel: NAT _ 5] RETURNS [ImagerDevice.Device] ~ { w: ImagerPixelMap.DeviceRectangle ~ ImagerPixelMap.BoundedWindow[pixelMap]; pm: ImagerPixelMap.PixelMap ~ pixelMap.Clip[w].ShiftMap[-w.sMin, -w.fMin]; maxValue: CARDINAL ~ Basics.BITSHIFT[1, Basics.BITSHIFT[1, pm.refRep.lgBitsPerPixel]]-1; sampBuffer: SampleBuffer ~ ImagerSample.NewBuffer[1, pm.fSize+2]; lineBuffer: SampleBuffer ~ ImagerSample.NewBuffer[1, pm.fSize+2]; sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep _ []]; pixelToDevice: Transformation ~ ImagerTransformation.Translate[[w.sMin, w.fMin]].PostScale[surfaceUnitsPerPixel]; surfaceToDevice: Transformation ~ viewToPixel.Concat[pixelToDevice]; surfaceRectangle: Rectangle ~ ImagerTransformation.TransformRectangle[pixelToDevice, [pm.sMin, pm.fMin, pm.sSize, pm.fSize]]; surface: DeviceBox ~ DeviceBoxFromRectangle[surfaceRectangle]; data: Data ~ NEW[DataRep _ [ component: component, pixelMap: pm, surfaceUnitsPerPixel: surfaceUnitsPerPixel, maxValue: maxValue, sOrigin: w.sMin, fOrigin: w.fMin, deviceToPixel: ImagerTransformation.Invert[pixelToDevice], change: change, changeData: changeData, sampBuffer: sampBuffer, lineBuffer: lineBuffer, sampler: sampler ]]; RETURN [NEW[ImagerDevice.DeviceRep _ [class: class, box: surface, surfaceToDevice: surfaceToDevice, surfaceUnitsPerInch: [surfaceUnitsPerPixel*72, surfaceUnitsPerPixel*72], surfaceUnitsPerPixel: surfaceUnitsPerPixel, data: data]]] }; SmoothSetPriority: PROC [device: Device, priorityImportant: BOOL] ~ { }; SmoothSetHalftone: PROC [device: Device, halftone: ImagerDevice.HalftoneParameters] ~ { }; PixelFromIntensity: PROC[i: REAL, maxValue: CARDINAL] RETURNS[CARDINAL] ~ { IF i<=0.0 THEN RETURN[0]; IF i>=1.0 THEN RETURN[maxValue]; RETURN[Real.RoundC[i*maxValue]]; }; SmoothSetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ { data: Data ~ NARROW[device.data]; data.case _ nil; data.sampledColor _ NIL; data.sampledColorData _ NIL; IF data.component = $Alpha THEN { data.case _ constant; data.constant _ data.maxValue; RETURN; }; WITH color SELECT FROM color: ConstantColor => { impl: ConstantColorImpl ~ color.impl; c: REAL; WITH impl: impl SELECT FROM rgb => { SELECT data.component FROM $Intensity => c _ impl.Y; $Red => c _ impl.val.R; $Green => c _ impl.val.G; $Blue => c _ impl.val.B; ENDCASE => ERROR Imager.Error[[$unimplemented, "Unknown component name"]]; }; ENDCASE => { <> c _ impl.Y; }; data.case _ constant; data.constant _ PixelFromIntensity[c, data.maxValue]; }; color: SampledColor => { pa: PixelArray ~ color.pa; um: Transformation ~ color.um; colorOperator: ColorOperator ~ color.colorOperator; data.sampledColor _ color; data.case _ sampled; data.zerosAreClear _ ImagerColorOperator.GetColorOperatorClass[colorOperator] = $SampledBlackClear; data.paToPixel _ ImagerTransformation.Cat[pa.m, color.um, viewToDevice, data.deviceToPixel]; SetUpSampledColorData[data]; }; ENDCASE => ERROR; -- unknown color variant }; me: REF TEXT ~ "Smth"; -- a globally unique REF for use as a clientID in the global cache. SampledColorKey: TYPE ~ RECORD [pa: PixelArray, component: ATOM, filterDiameter: NAT]; SetUpSampledColorData: PROC [data: Data] ~ { cache: FunctionCache.Cache _ FunctionCache.GlobalCache[]; color: SampledColor ~ data.sampledColor; pa: PixelArray ~ color.pa; component: ATOM ~ data.component; s: VEC ~ ImagerTransformation.SingularValues[data.paToPixel]; <> largerPaSize: NAT ~ MIN[pa.sSize, pa.fSize] + 1; filterDiameter: NAT ~ MIN[MAX[Real.Round[1.0/MAX[s.y, 1.0/largerPaSize]], 1], 255]; key: SampledColorKey ~ [data.sampledColor.pa, data.component, IF filter THEN filterDiameter ELSE 1]; compare: FunctionCache.CompareProc ~ {RETURN [ WITH argument SELECT FROM p: REF SampledColorKey => (p^=key), ENDCASE => FALSE ]}; scd: SampledColorData _ NARROW[FunctionCache.Lookup[cache, compare, me].value]; IF color = NIL THEN ERROR; IF scd = NIL THEN TRUSTED { colorOperator: ColorOperator ~ color.colorOperator; samplesPerPixel: NAT ~ pa.samplesPerPixel; sSize: NAT ~ pa.sSize; fSize: NAT ~ pa.fSize; maxIn: Sample ~ ImagerPixelArray.MaxSampleValue[pa, 0]; pixels: SampleBuffer ~ ImagerSample.NewBuffer[samplesPerPixel, fSize]; buffer: SampleBuffer ~ ImagerSample.NewBuffer[1, fSize]; bufferPointer: UnsafeSamples ~ buffer.GetPointer[0, 0, fSize]; mapper: ImagerColorOperator.Mapper ~ ImagerColorOperator.NewMapper[op: colorOperator, component: data.component, maxIn: maxIn, maxOut: data.maxValue]; t: ImagerPixelMap.PixelMap ~ ImagerPixelMap.Create[data.pixelMap.refRep.lgBitsPerPixel, [0, 0, sSize, fSize]]; line: LONG POINTER TO Basics.RawWords _ t.refRep.pointer; rast: NAT ~ t.refRep.rast; bps: NAT ~ Basics.BITSHIFT[1, t.refRep.lgBitsPerPixel]; sampledColorDataSize: INT _ t.refRep.words+SIZE[ImagerPixelMap.PixelMap]+SIZE[ImagerPixelMap.PixelMapRep]; scd _ NEW[SampledColorDataRep _ [t, key]]; FOR s: NAT IN[0..sSize) DO ImagerPixelArray.GetPixels[pa: pa, s: s, f: 0, buffer: pixels, count: fSize]; ImagerColorOperator.MapPixels[mapper: mapper, pixels: pixels, buffer: buffer, bi: 0, count: fSize]; ImagerSample.UnsafePutF[samples: bufferPointer, count: fSize, s: s, f: 0, base: t.refRep.pointer, wordsPerLine: rast, bitsPerSample: bps]; ENDLOOP; IF key.filterDiameter > 1 THEN { PixelMapOps.BoxFilter[t, key.filterDiameter, key.filterDiameter, TRUE]; }; FunctionCache.Insert[cache, NEW[SampledColorKey _ key], scd, sampledColorDataSize, me]; }; data.sampledColorData _ scd; }; Mod: PROC [n: INTEGER, d: NAT] RETURNS [NAT] ~ { nn: Basics.LongNumber _ [li[n]]; IF nn.li < 0 THEN nn.highbits _ nn.highbits + d; RETURN [Basics.LongDivMod[nn.lc, d].remainder]; }; Check: PROC[x: CARDINAL, max: NAT] RETURNS[NAT] ~ TRUSTED MACHINE CODE { PrincOps.zINC; PrincOps.zBNDCK }; <> ClearSampleBuffer: PROC[buffer: SampleBuffer, i, j: NAT _ 0, count: NAT] ~ TRUSTED { pointer: LONG POINTER ~ ImagerSample.GetPointer[buffer, i, j, count]; PrincOpsUtils.LongZero[where: pointer, nwords: count*SIZE[Sample]]; }; filter: BOOL _ TRUE; interpolate: BOOL _ TRUE; MaskRunsInternal: PROC[data: Data, bounds: DeviceBox, runs: PROC[RunProc]] ~ { sampBuffer: SampleBuffer ~ data.sampBuffer; sampler: ImagerSample.Sampler ~ data.sampler; lineBuffer: SampleBuffer ~ data.lineBuffer; start: NAT _ NAT.LAST; -- index of first touched pixel on scan line end: NAT _ 0; -- index after last touched pixel on scan line supp: NAT ~ data.surfaceUnitsPerPixel; squpp: NAT ~ supp*supp; sPixel: CARDINAL _ bounds.smin/supp; s: NAT _ sPixel*supp; sModSupp: NAT _ 0; fPixelLimit: NAT ~ data.pixelMap.fOrigin + data.pixelMap.fMin + data.pixelMap.fSize; DoLine: PROC ~ { sum: NAT _ 0; p: NAT _ start; case: Case _ data.case; IF end > start THEN { ImagerSample.AddSamples[samples: lineBuffer, si: 0, sj: start, buffer: lineBuffer, bi: 0, bj: start+1, count: end-start-1]; -- ripple add! }; IF case = sampled THEN { ImagerSample.SetSamplerPosition[sampler, data.paToPixel, sPixel, start]; IF interpolate THEN GetInterpolatedSamples[sampler: sampler, s: sPixel, f: start, buffer: sampBuffer, i: 0, j: start, count: end-start] ELSE ImagerSample.GetPointSamples[sampler: sampler, s: sPixel, f: start, buffer: sampBuffer, bi: 0, bj: start, count: end-start]; IF data.zerosAreClear THEN TRUSTED { maxVal: CARDINAL ~ Basics.BITSHIFT[1, Basics.BITSHIFT[1, data.pixelMap.refRep.lgBitsPerPixel]]-1; line: UnsafeSamples ~ lineBuffer.GetPointer[0, start, end-start]; samp: UnsafeSamples ~ sampBuffer.GetPointer[0, start, end-start]; case _ constant; data.constant _ 0; FOR j: NAT IN [0..end-start) DO line[j] _ Basics.LongDiv[Basics.LongMult[line[j], maxVal-samp[j]]+maxVal/2, maxVal]; ENDLOOP; }; }; lineBuffer[end] _ LAST[NAT]; WHILE p < end DO bltStart: NAT; WHILE lineBuffer[p] = 0 DO p _ p + 1 ENDLOOP; bltStart _ p; WHILE lineBuffer[p] = squpp DO p _ p + 1 ENDLOOP; IF p > bltStart THEN { SELECT case FROM constant => ImagerPixelMap.Fill[data.pixelMap, [sPixel, bltStart, 1, p-bltStart], data.constant]; sampled => PixelMapOps.PutF[pixelMap: data.pixelMap, s: sPixel, f: bltStart, buffer: sampBuffer, bi: 0, bj: bltStart, count: p-bltStart]; ENDCASE => ERROR; }; IF p < end AND p < fPixelLimit THEN { oldValue: CARDINAL ~ ImagerPixelMap.GetPixel[data.pixelMap, sPixel, p]; newValue: CARDINAL ~ SELECT case FROM constant => data.constant, sampled => sampBuffer[p], ENDCASE => ERROR; mix: CARDINAL ~ lineBuffer[p]; mixedValue: CARDINAL ~ Basics.LongDiv[ Basics.LongMult[oldValue, squpp-mix]+Basics.LongMult[newValue, mix], squpp ]; ImagerPixelMap.PutPixel[data.pixelMap, sPixel, p, mixedValue]; }; IF p < end THEN { p _ p + 1; }; ENDLOOP; ClearSampleBuffer[buffer: lineBuffer, j: start, count: end-start+1]; start _ NAT.LAST; end _ 0; }; Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { f0: CARDINAL _ fMin; f1: CARDINAL _ fMin+fSize; WHILE s < sMin DO s _ s + 1; sModSupp _ sModSupp + 1; IF sModSupp = supp THEN { IF start < end THEN DoLine[]; sModSupp _ 0; sPixel _ sPixel + 1; }; ENDLOOP; IF fSize > 0 THEN { q0, r0, q1, r1: CARDINAL; [q0, r0] _ Basics.LongDivMod[f0, supp]; [q1, r1] _ Basics.LongDivMod[f1, supp]; IF q0 < start THEN start _ q0; IF q1 >= end THEN end _ q1+1; lineBuffer[q0] _ lineBuffer[q0] + supp - r0; lineBuffer[q0+1] _ lineBuffer[q0+1] + r0; lineBuffer[q1] _ lineBuffer[q1] + r1 - supp; lineBuffer[q1+1] _ lineBuffer[q1+1] - r1; }; }; ClearSampleBuffer[buffer: lineBuffer, count: lineBuffer.jSize]; IF data.case = sampled THEN { t: PixelMap _ data.sampledColorData.source; sampler.base _ t.refRep.pointer; sampler.wordsPerLine _ t.refRep.rast; sampler.bitsPerSample _ Basics.BITSHIFT[1, t.refRep.lgBitsPerPixel]; sampler.sMin _ t.sMin; sampler.fMin _ t.fMin; sampler.sSize _ t.sSize; sampler.fSize _ t.fSize; ImagerSample.SetSamplerIncrements[sampler, data.paToPixel]; }; runs[Run]; IF start < end THEN DoLine[]; }; RectFromDeviceBox: PROC[data: Data, bounds: DeviceBox] RETURNS [rect: DeviceRectangle]~ { <> <> supp: NAT ~ data.surfaceUnitsPerPixel; smin: CARDINAL ~ bounds.smin/supp; smax: CARDINAL ~ (bounds.smax+(supp-1))/supp; fmin: CARDINAL ~ bounds.fmin/supp; fmax: CARDINAL ~ (bounds.fmax+(supp-1))/supp; rect.sMin _ data.sOrigin + smin; rect.fMin _ data.fOrigin + fmin; rect.sSize _ smax - smin; rect.fSize _ fmax - fmin; }; SmoothMaskRuns: PROC[device: Device, bounds: DeviceBox, runs: PROC[RunProc]] ~ { data: Data ~ NARROW[device.data]; smoothMaskRunsAction: PROC ~ { MaskRunsInternal[data, bounds, runs] }; IF data.change = NIL THEN MaskRunsInternal[data, bounds, runs] ELSE data.change[data.changeData, RectFromDeviceBox[data, bounds], smoothMaskRunsAction]; }; SmoothMoveBoxes: PROC [device: Device, ts, tf: INTEGER, boxes: PROC[BoxProc]] ~ { ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]]; }; Sample0: TYPE ~ CARDINAL[0..000001B]; -- 1 bit Sample1: TYPE ~ CARDINAL[0..000003B]; -- 2 bits Sample2: TYPE ~ CARDINAL[0..000017B]; -- 4 bits Sample3: TYPE ~ CARDINAL[0..000377B]; -- 8 bits Sample4: TYPE ~ CARDINAL[0..177777B]; -- 16 bits Sequence0: TYPE ~ RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF Sample0]; Sequence1: TYPE ~ RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF Sample1]; Sequence2: TYPE ~ RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF Sample2]; Sequence3: TYPE ~ RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF Sample3]; Sequence4: TYPE ~ RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF Sample4]; Pointer0: TYPE ~ LONG POINTER TO Sequence0; Pointer1: TYPE ~ LONG POINTER TO Sequence1; Pointer2: TYPE ~ LONG POINTER TO Sequence2; Pointer3: TYPE ~ LONG POINTER TO Sequence3; Pointer4: TYPE ~ LONG POINTER TO Sequence4; GetInterpolatedSamples: PUBLIC PROC [sampler: Sampler, s, f: CARDINAL, buffer: SampleBuffer, i, j: NAT _ 0, count: NAT] ~ TRUSTED { lgBitsPerSample: [0..4] ~ Lg[sampler.bitsPerSample]; samples: UnsafeSamples ~ buffer.GetPointer[i, j, count]; base: LONG POINTER ~ sampler.base; wordsPerLine: CARDINAL ~ sampler.wordsPerLine; line: LONG POINTER _ base; sLine: CARDINAL _ 0; sSize: CARDINAL ~ sampler.sSize; fSize: CARDINAL ~ sampler.fSize; sMin: CARDINAL ~ sampler.sMin; fMin: CARDINAL ~ sampler.fMin; sMax: CARDINAL ~ sMin+sSize; fMax: CARDINAL ~ fMin+fSize; ssDelta: Basics.LongNumber ~ sampler.ssDelta; fsDelta: Basics.LongNumber ~ sampler.fsDelta; sfDelta: Basics.LongNumber ~ sampler.sfDelta; ffDelta: Basics.LongNumber ~ sampler.ffDelta; sSource: Basics.LongNumber _ sampler.sSource; fSource: Basics.LongNumber _ sampler.fSource; sDest: CARDINAL _ sampler.sDest; fDest: CARDINAL _ sampler.fDest; half: LONG CARDINAL ~ LONG[LAST[WORD]/2]+1; minLine: LONG POINTER ~ base+Basics.LongMult[sMin, wordsPerLine]; WHILE sDest { p: Pointer0 _ LOOPHOLE[line]; v00 _ p[k]; v01 _ p[k1]; p _ IF sSource.hi+1 < sMax THEN p + wordsPerLine ELSE minLine; v10 _ p[k]; v11 _ p[k1]; }; 1 => { p: Pointer1 _ LOOPHOLE[line]; v00 _ p[k]; v01 _ p[k1]; p _ IF sSource.hi+1 < sMax THEN p + wordsPerLine ELSE minLine; v10 _ p[k]; v11 _ p[k1]; }; 2 => { p: Pointer2 _ LOOPHOLE[line]; v00 _ p[k]; v01 _ p[k1]; p _ IF sSource.hi+1 < sMax THEN p + wordsPerLine ELSE minLine; v10 _ p[k]; v11 _ p[k1]; }; 3 => { p: Pointer3 _ LOOPHOLE[line]; v00 _ p[k]; v01 _ p[k1]; p _ IF sSource.hi+1 < sMax THEN p + wordsPerLine ELSE minLine; v10 _ p[k]; v11 _ p[k1]; }; 4 => { p: Pointer4 _ LOOPHOLE[line]; v00 _ p[k]; v01 _ p[k1]; p _ IF sSource.hi+1 < sMax THEN p + wordsPerLine ELSE minLine; v10 _ p[k]; v11 _ p[k1]; }; ENDCASE => ERROR; samples[jj] _ IF a=0 AND b=0 THEN v00 -- this is needed because the normal case overflows 16 bits. ELSE (Basics.HighHalf[ Basics.LongMult[CARDINAL.LAST-a+ab-b+1, v00] + Basics.LongMult[b-ab, v01] + Basics.LongMult[a-ab, v10] + Basics.LongMult[ab, v11] ]); sSource.lc _ sSource.lc+sfDelta.lc; fSource.lc _ fSource.lc+ffDelta.lc; IF NOT sSource.hi> END.