ImagerSmoothImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, October 7, 1985 1:40:30 pm PDT
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;
Must use a version of ImagerRasterImpl that allows MaskBoxes and MaskBits to be NIL.
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: BOOLFALSE, -- 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: BOOLTRUE, surfaceUnitsPerPixel: NAT ← 5, blackBackground: BOOLFALSE] 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: REFNIL, cacheFonts: BOOLTRUE, 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 => {
stipples will show as grays.
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];
s.y corresponds to the smallest amount by which a source pixel gets magnified.
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 };
IF x IN[0..max] THEN RETURN[x] ELSE ERROR RuntimeError.BoundsFault
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: BOOLTRUE;
interpolate: BOOLTRUE;
MaskRunsInternal: PROC[data: Data, bounds: DeviceBox, runs: PROC[RunProc]] ~ {
sampBuffer: SampleBuffer ~ data.sampBuffer;
sampler: ImagerSample.Sampler ~ data.sampler;
lineBuffer: SampleBuffer ~ data.lineBuffer;
start: NATNAT.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]~ {
bounds are in device coordinates.
rect is in pixel coordinates.
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<s DO
sSource.lc ← sSource.lc+ssDelta.lc;
fSource.lc ← fSource.lc+fsDelta.lc;
IF NOT sSource.hi<sSize THEN sSource.hi ← sSource.hi-sSize;
IF NOT fSource.hi<fSize THEN fSource.hi ← fSource.hi-fSize;
sDest ← sDest+1;
ENDLOOP;
sampler.sSource ← sSource;
sampler.fSource ← fSource;
sampler.sDest ← sDest;
WHILE fDest<f DO
sSource.lc ← sSource.lc+sfDelta.lc;
fSource.lc ← fSource.lc+ffDelta.lc;
IF NOT sSource.hi<sSize THEN sSource.hi ← sSource.hi-sSize;
IF NOT fSource.hi<fSize THEN fSource.hi ← fSource.hi-fSize;
fDest ← fDest+1;
ENDLOOP;
IF s#sDest OR f#fDest THEN ERROR;
IF sSource.lc < half THEN sSource.hi ← sSource.hi+sSize;
sSource.lc ← sSource.lc - half;
IF fSource.lc < half THEN fSource.hi ← fSource.hi+fSize;
fSource.lc ← fSource.lc - half;
sSource.hi ← sSource.hi+sMin;
fSource.hi ← fSource.hi+fMin;
FOR jj: NAT IN [0..count) DO
k: CARDINAL ~ fSource.hi;
k1: CARDINAL ~ IF k+1 < fMax THEN k+1 ELSE fMin;
a: CARDINAL ~ sSource.lo;
b: CARDINAL ~ fSource.lo;
ab: CARDINAL ~ Basics.HighHalf[Basics.LongMult[a, b]];
v00, v01, v10, v11: CARDINAL;
IF sLine#sSource.hi THEN line ← base+Basics.LongMult[sLine ← sSource.hi, wordsPerLine];
SELECT lgBitsPerSample FROM
0 => {
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<sMax THEN sSource.hi ← sSource.hi-sSize;
IF NOT fSource.hi<fMax THEN fSource.hi ← fSource.hi-fSize;
ENDLOOP;
};
END.