ImagerSampleImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1994 by Xerox Corporation. All rights reserved.
Michael Plass, January 26, 1994 5:16 pm PST
Doug Wyatt, December 9, 1988 9:22:01 am PST
Russ Atkinson (RRA) January 25, 1994 6:47 pm PST
Bill Jackson (bj) December 5, 1988 5:04:14 pm PST
DIRECTORY
Basics USING [BITAND, BITLSHIFT, BITNOT, BITOR, BITRSHIFT, BITXOR, CopyBits, FillBits, FillWords, NonNegative, RaiseBoundsFault, RawBytes, RawWords, UnsafeBlock],
ImagerSample USING [BitAddress, BitsPerSample, EdgeAction, Function, maxBox, maxCount, maxSample, maxVec, nullFunction, ObjectGetProc, ObjectPutProc, ObjectSampleMap, RasterSampleMap, RawDescriptor, Sample, SampleBuffer, SampleBufferRep, SampleMap, SampleMapRep, zeroVec],
ImagerSamplePrivate USING [SampleMapClassRep],
ImagerSwitches USING [BoolValue, Define],
RasterOp USING [forwardOp, Unpack, Pack, WordPtr, Offset, OrBlt, AndCBlt, Word, tileOp],
RuntimeError USING [BoundsFault],
SafeStorage USING [GetUntracedZone],
ImagerScaled USING [Floor, PLUS, Value, zero],
SF USING [Box, BoxAction, BoxGenerator, Intersect, Size, Vec],
SFInline USING [Add, Displace, Empty, In, Intersect, Neg, Nonempty, Size, SizeF, SizeS, Sub];
ImagerSampleImpl: CEDAR MONITOR
IMPORTS Basics, ImagerSwitches, RasterOp, RuntimeError, SafeStorage, ImagerScaled, SF, SFInline
EXPORTS ImagerSample
~ BEGIN OPEN ImagerSample;
Options
useCharBlt: CHAR['a..'z]
~ ImagerSwitches.Define['c, $charblt, "Use CharBlt for characters", $true];
useUnsafeTile: CHAR['a..'z]
~ ImagerSwitches.Define['t, $unsafetile, "Use UnsafeTile for tiling", NIL];
Types
Box: TYPE ~ SF.Box;
BoxAction: TYPE ~ SF.BoxAction;
BoxGenerator: TYPE ~ SF.BoxGenerator;
RawBytesPtr: TYPE ~ POINTER TO Basics.RawBytes;
RawSamples: TYPE ~ Basics.RawWords;
RawSamplesPtr: TYPE ~ LONG POINTER TO RawSamples;
Vec: TYPE ~ SF.Vec;
SampleMapClass: TYPE ~ REF SampleMapClassRep;
SampleMapClassRep: PUBLIC TYPE =
ImagerSamplePrivate.SampleMapClassRep;
RECORD [
<<This could get considerably larger someday...>>
getSamples: ObjectGetProc,
putSamples: ObjectPutProc
];
MultipleReleaseOfScratch: PUBLIC ERROR ~ CODE;
Support
bpu: NAT = BITS[UNIT];
bpw: NAT = BITS[WORD];
upw: NAT = UNITS[WORD];
InlineSize: PROC [map: SampleMap] RETURNS [Vec] ~ INLINE {
-- Assumes there is no crossover.
RETURN [[s: map.box.max.s-map.box.min.s, f: map.box.max.f-map.box.min.f]]
};
CleanBox: PROC [box: Box] RETURNS [Box] ~ INLINE {
-- Eliminates crossover of the boundaries; we maintain this invariant to simplify the size calculations.
IF box.min.s >= box.max.s OR box.min.f >= box.max.f THEN box.max ¬ box.min;
RETURN [box];
};
IsPowerOfTwo: PROC [c: CARDINAL] RETURNS [BOOL] ~ INLINE {
RETURN [Basics.BITAND[c, c-1]=0];
};
Lg: PROC [a: NAT] RETURNS [lg: NAT ¬ 0] ~ {
WHILE a > 32 DO a ¬ (a + 31)/32; lg ¬ lg + 5; ENDLOOP;
lg ¬ lg + LgTable[a];
};
LgTable: PACKED ARRAY [0..32] OF BYTE = [
0, 0, 1, 2, 2, 3, 3, 3, 3, -- 0..8
4, 4, 4, 4, 4, 4, 4, 4,  -- 9..16
5, 5, 5, 5, 5, 5, 5, 5, -- 17..24
5, 5, 5, 5, 5, 5, 5, 5]; -- 25..32
MaskFromBPS: PROC [bitsPerSample: NAT] RETURNS [WORD] ~ INLINE {
RETURN [Basics.BITRSHIFT[LAST[WORD], Lo5[bpw-bitsPerSample]]]
};
WordsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE {
RETURN [(bitCount+(bpw-1))/bpw]
};
WordFloorUnitsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE {
RETURN [bitCount/bpw*upw]
};
IndexBit: PROC [addr: BitAddress, offset: CARD] RETURNS [BitAddress] ~ INLINE {
bitOffset: CARD = offset + addr.bit;
mod: CARDINAL [0..bpw) = bitOffset MOD bpw;
RETURN [[word: addr.word + (bitOffset - mod) / bpu, bit: mod]]
};
IndexWord: PROC [word: POINTER, bitOffset: CARD] RETURNS [POINTER] ~ INLINE {
mod: CARDINAL [0..bpw) = bitOffset MOD bpw;
RETURN [word + (bitOffset - mod) / bpu]
};
BoundsCheck: PROC [arg: INT, limit: NAT] RETURNS [NAT] ~ INLINE {
IF LOOPHOLE[arg, CARD] >= CARD[limit]
THEN ERROR RuntimeError.BoundsFault
ELSE RETURN [arg]
};
Check: PROC [value: INT, bound: NAT] RETURNS [NAT] ~ INLINE {
IF LOOPHOLE[value, CARD] > CARD[bound]
THEN ERROR RuntimeError.BoundsFault
ELSE RETURN [value]
};
Samples and SampleBuffers
NewSamples: PUBLIC PROC
[length: NAT, scratch: SampleBuffer ¬ NIL] RETURNS [SampleBuffer] ~ {
new: SampleBuffer ¬ scratch;
IF new=NIL OR new.maxLength<length THEN TRUSTED {
new ¬ SafeStorage.GetUntracedZone[].NEW[SampleBufferRep[length]];
};
new.length ¬ length;
RETURN [new];
};
nScratchSampleBuffers: NAT ~ 12;
scratchSampleBuffers: ARRAY [0..nScratchSampleBuffers) OF SampleBuffer ¬ ALL[NIL];
ObtainScratchSamples: PUBLIC ENTRY PROC [length: NAT] RETURNS [SampleBuffer] ~ {
buf: SampleBuffer ¬ NIL;
FOR i: NAT IN [0..nScratchSampleBuffers) DO
buf ¬ scratchSampleBuffers[i];
IF buf # NIL AND buf.maxLength >= length THEN {
scratchSampleBuffers[i] ¬ NIL;
buf.length ¬ length;
RETURN [buf];
};
ENDLOOP;
TRUSTED { buf ¬ SafeStorage.GetUntracedZone[].NEW[SampleBufferRep[length]] };
buf.length ¬ length;
RETURN [buf]
};
ReleaseScratchSamples: PUBLIC ENTRY PROC [buffer: SampleBuffer] ~ {
slot: NAT ¬ 0;
buffer.length ¬ 0;
FOR i: NAT IN [0..nScratchSampleBuffers) DO
t: SampleBuffer ¬ scratchSampleBuffers[i];
IF t = NIL THEN slot ¬ i
ELSE IF buffer = t THEN RETURN WITH ERROR MultipleReleaseOfScratch;
ENDLOOP;
scratchSampleBuffers[slot] ¬ buffer;
};
DoWithScratchSamples: PUBLIC PROC [length: NAT, action: PROC [SampleBuffer]] ~ {
scratch: SampleBuffer ~ ObtainScratchSamples[length];
action[scratch ! UNWIND => ReleaseScratchSamples[scratch]];
ReleaseScratchSamples[scratch];
};
InlinePointerToSamples: PROC [buffer: SampleBuffer, start: NAT, count: NAT]
RETURNS [RawSamplesPtr] ~ TRUSTED INLINE {
check: NAT ~ NAT[buffer.maxLength-start]-count;
RETURN[LOOPHOLE[buffer, LONG POINTER]+SIZE[SampleBufferRep[start]]];
};
PointerToSamples: PUBLIC PROC [buffer: SampleBuffer, start: NAT, count: NAT]
RETURNS [RawSamplesPtr] ~ {
RETURN [InlinePointerToSamples[buffer, start, count]]
};
FlipSamples: PUBLIC PROC [buffer: SampleBuffer, start, count: NAT] ~ {
size: NAT ~ MIN[buffer.length-start, count];
IF size > 0 THEN TRUSTED {
pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, size];
last: NAT ~ size-1;
FOR j: NAT IN [0..size/2) DO
k: NAT ~ last-j;
t: Sample ~ pointer[j];
pointer[j] ¬ pointer[k];
pointer[k] ¬ t;
ENDLOOP;
};
};
FillSamples: PUBLIC PROC [buffer: SampleBuffer, value: Sample, start, count: NAT] ~ {
size: NAT ~ MIN[buffer.length-start, count];
IF size > 0 THEN TRUSTED {
pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, size];
Basics.FillWords[dst: pointer, count: size, value: value];
};
};
CopySamples: PUBLIC PROC [dst, src: SampleBuffer, dstStart, srcStart, count: NAT] ~ {
size: NAT = MIN[NAT[dst.length-dstStart], NAT[src.length-srcStart], count];
IF size > 0 THEN TRUSTED {
dstPointer: RawSamplesPtr ¬ InlinePointerToSamples[dst, dstStart, size];
srcPointer: RawSamplesPtr ¬ InlinePointerToSamples[src, srcStart, size];
rem: INTEGER ¬ size;
WHILE rem >= 4 DO
dstPointer[0] ¬ srcPointer[0];
dstPointer[1] ¬ srcPointer[1];
dstPointer[2] ¬ srcPointer[2];
dstPointer[3] ¬ srcPointer[3];
dstPointer ¬ dstPointer + SIZE[WORD]*4;
srcPointer ¬ srcPointer + SIZE[WORD]*4;
rem ¬ rem - 4;
ENDLOOP;
IF rem > 0 THEN {
dstPointer[0] ¬ srcPointer[0];
IF rem > 1 THEN {
dstPointer[1] ¬ srcPointer[1];
IF rem > 2 THEN dstPointer[2] ¬ srcPointer[2];
};
};
};
};
ClearSamples: PUBLIC PROC [buffer: SampleBuffer, start, count: NAT] ~ {
size: NAT ~ MIN[buffer.length-start, count];
IF size > 0 THEN TRUSTED {
pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, size];
Basics.FillWords[dst: pointer, count: size, value: 0];
};
};
SampleMap creation
UnsafeNewSampleMap: PUBLIC UNSAFE PROC
[box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT, base: BitAddress, ref: REF, words: INT, scratchDescriptor: SampleMap ¬ NIL]
RETURNS [RasterSampleMap] ~ {
size: Vec ~ SF.Size[box];
bpl: CARD ~ bitsPerLine;
dataBitsPerLine: CARD ~ NAT[size.f]*LONG[bitsPerSample];
fillBits: CARD ~ INT[bitsPerLine]-INT[dataBitsPerLine]; -- check sufficient bitsPerLine
totalBits: CARD ~ (NAT[size.s]*bpl)+base.bit-fillBits;
totalWords: CARD ~ WordsForBits[totalBits];
spareWords: INT ~ Basics.NonNegative[words-INT[totalWords]]; -- check sufficient words
s: RasterSampleMap ¬ NIL;
WITH scratchDescriptor SELECT FROM
r: RasterSampleMap => s ¬ r
ENDCASE => s ¬ NEW[SampleMapRep.raster];
s­ ¬ [box: CleanBox[box], immutable: FALSE, bitsPerSample: bitsPerSample, v: raster[bitsPerLine: bpl, base: base, ref: ref]];
RETURN[s];
};
BitsForSamples: PUBLIC PROC [fSize: NAT, bitsPerSample: NAT] RETURNS [INT] ~ {
Returns required number of bits, rounded up to a whole number of words.
num: CARD ~ ((CARD[fSize]*CARD[bitsPerSample] + (bpw-1)) / bpw) * bpw;
RETURN[num];
};
WordsForLines: PUBLIC PROC [sSize: NAT, bitsPerLine: INT] RETURNS [INT] ~ {
RETURN[WordsForBits[sSize*CARD[bitsPerLine]]];
};
WordsForMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine: INT ¬ 0] RETURNS [INT] ~ {
IF bitsPerLine=0 THEN bitsPerLine ¬ BitsForSamples[size.f, bitsPerSample];
RETURN[WordsForLines[size.s, bitsPerLine]];
};
WordSeqRep: TYPE ~ RECORD [SEQUENCE nWords: NAT OF WORD];
PointerToWords: UNSAFE PROC [wordSeq: REF WordSeqRep] RETURNS [LONG POINTER] ~ UNCHECKED INLINE {
RETURN [LOOPHOLE[wordSeq, LONG POINTER]+SIZE[WordSeqRep[0]]]
};
NewSampleMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT ¬ 0] RETURNS [RasterSampleMap] ~ TRUSTED {
size: Vec ~ SF.Size[box];
actualBitsPerLine: INT ~ SELECT bitsPerLine FROM
0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine;
words: INT ~ WordsForLines[size.s, actualBitsPerLine];
wordSeq: REF WordSeqRep ~ SafeStorage.GetUntracedZone[].NEW[WordSeqRep[words]];
map: RasterSampleMap ~ UnsafeNewSampleMap[
box: box, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine,
base: [word: PointerToWords[wordSeq], bit: 0],
ref: wordSeq,
words: wordSeq.nWords
];
RETURN[map]
};
NewObjectSampleMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, getSamples: ObjectGetProc, putSamples: ObjectPutProc, data: REF] RETURNS [ObjectSampleMap] ~ {
new: ObjectSampleMap ~ NEW[SampleMapRep.object ¬ [box: CleanBox[box], immutable: putSamples=NIL, bitsPerSample: bitsPerSample, v: object[class: NEW[SampleMapClassRep ¬ [getSamples: getSamples, putSamples: putSamples]], data: data]]];
RETURN [new]
};
Copy: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [RasterSampleMap] ~ {
temp: SampleMap ¬ ReIndex[map, delta, box];
new: RasterSampleMap ~ NewSampleMap[box: temp.box, bitsPerSample: map.bitsPerSample];
Transfer[dst: new, src: temp];
ReleaseDescriptor[temp];
RETURN[new];
};
nScratchDescs: NAT ~ 15;
scratchDescsTop: [0..nScratchDescs] ¬ 0;
scratchDescs: ARRAY [0..nScratchDescs) OF RasterSampleMap ¬ ALL[NIL];
NewDescriptor: ENTRY PROC [map: RasterSampleMap ¬ NIL] RETURNS [new: RasterSampleMap ¬ NIL] ~ {
IF scratchDescsTop = 0
THEN new ¬ NEW[SampleMapRep.raster]
ELSE {
scratchDescsTop ¬ scratchDescsTop - 1;
new ¬ scratchDescs[scratchDescsTop];
scratchDescs[scratchDescsTop] ¬ NIL;
};
IF map # NIL THEN new­ ¬ map­;
};
ReleaseDescriptor: PUBLIC ENTRY PROC [map: SampleMap] ~ {
WITH map SELECT FROM
map: RasterSampleMap => {
FOR i: NAT IN [0..scratchDescsTop) DO
IF map = scratchDescs[i] THEN RETURN WITH ERROR MultipleReleaseOfScratch;
ENDLOOP;
IF scratchDescsTop < nScratchDescs THEN {
map.box ¬ [[0,0],[0,0]];
map.base.word ¬ NIL;
map.ref ¬ NIL;
scratchDescs[scratchDescsTop] ¬ map;
scratchDescsTop ¬ scratchDescsTop + 1;
};
};
ENDCASE => NULL;
};
nScratchMaps: NAT ~ 10;
scratchMaps: ARRAY [0..nScratchMaps) OF RasterSampleMap ¬ ALL[NIL];
ObtainScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT ¬ 0]
RETURNS [RasterSampleMap] ~ {
size: Vec ~ SF.Size[box];
actualBitsPerLine: INT ~ SELECT bitsPerLine FROM
0 => BitsForSamples[size.f, bitsPerSample],
ENDCASE => bitsPerLine;
nWords: INT ~ WordsForLines[sSize: size.s, bitsPerLine: actualBitsPerLine];
TryObtainScratch: ENTRY PROC [nw: INT] RETURNS [RasterSampleMap] ~ TRUSTED {
besti: INT ¬ -1;
smallestWaste: INT ¬ INT.LAST;
FOR i: NAT IN [0..nScratchMaps) DO
Look though all of the scratch maps to find the best fit.
s: RasterSampleMap = scratchMaps[i];
IF s # NIL THEN {
WITH s.ref SELECT FROM
wordSeq: REF WordSeqRep => {
IF nw <= wordSeq.nWords THEN {
waste: INT ~ wordSeq.nWords - nw;
IF waste < smallestWaste THEN {
besti ¬ i;
smallestWaste ¬ waste;
};
};
};
ENDCASE;
};
ENDLOOP;
IF besti >= 0 THEN {
s: RasterSampleMap = scratchMaps[besti];
scratchMaps[besti] ¬ NIL;
WITH s.ref SELECT FROM
wordSeq: REF WordSeqRep => {
ptr: POINTER ~ PointerToWords[wordSeq];
s.base ¬ [word: ptr, bit: 0];
};
ENDCASE => ERROR;
RETURN [s];
};
RETURN [NIL]
};
s: RasterSampleMap ¬ TryObtainScratch[nWords];
IF s = NIL
THEN
s ¬ NewSampleMap[box: box, bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine]
ELSE {
s.box ¬ CleanBox[box];
s.bitsPerSample ¬ bitsPerSample;
s.bitsPerLine ¬ actualBitsPerLine;
};
RETURN [s]
};
ReleaseScratchMap: PUBLIC ENTRY PROC [map: SampleMap] ~ {
IF map.immutable THEN RETURN;
WITH map SELECT FROM
map: RasterSampleMap => {
slot: INT ¬ -1;
WITH map.ref SELECT FROM
wordSeq: REF WordSeqRep => {};
ENDCASE => RETURN;
FOR i: NAT IN [0..nScratchMaps) DO
t: RasterSampleMap ~ scratchMaps[i];
SELECT TRUE FROM
t = NIL => slot ¬ i;
map = t OR map.ref = t.ref => RETURN WITH ERROR MultipleReleaseOfScratch;
ENDCASE;
ENDLOOP;
IF slot >= 0
THEN { scratchMaps[slot] ¬ map}
ELSE {
The scratch pool has filled up. Rather than trying to guess about a replacement policy, we'll just forget all the others.
scratchMaps[0] ¬ map;
FOR i: NAT IN (0..nScratchMaps) DO
scratchMaps[i] ¬ NIL;
ENDLOOP;
};
};
ENDCASE;
};
ClearScratchMaps: ENTRY PROC ~ {
FOR i: NAT IN (0..nScratchMaps) DO
scratchMaps[i] ¬ NIL;
ENDLOOP;
};
DoWithScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, action: PROC [RasterSampleMap]] ~ {
scratch: RasterSampleMap ~ ObtainScratchMap[box: box, bitsPerSample: bitsPerSample];
action[scratch ! UNWIND => ReleaseScratchMap[scratch]];
ReleaseScratchMap[scratch];
};
SampleMap descriptor operations
InPlaceClip: UNSAFE PROC [map: RasterSampleMap, box: Box] ~ {
newBox: Box ~ SFInline.Intersect[map.box, box];
skipBits: CARD ~
NAT[newBox.min.s-map.box.min.s]*CARD[map.bitsPerLine] +
NAT[newBox.min.f-map.box.min.f]*CARD[map.bitsPerSample];
map.box ¬ CleanBox[newBox];
map.base ¬ IndexBit[map.base, skipBits];
};
BoundedAdd: PROC [a, b: INTEGER] RETURNS [INTEGER] ~ INLINE {
IF a > 0
THEN RETURN [IF b < INTEGER.LAST-a THEN a+b ELSE INTEGER.LAST]
ELSE RETURN [IF b > INTEGER.FIRST-a THEN a+b ELSE INTEGER.FIRST];
};
BoundedVecAdd: PROC [a, b: Vec] RETURNS [Vec] ~ INLINE {
RETURN[[s: BoundedAdd[a.s, b.s], f: BoundedAdd[a.f, b.f]]]
};
InPlaceShift: UNSAFE PROC [map: RasterSampleMap, delta: Vec] ~ UNCHECKED {
newmin: SF.Vec ~ BoundedVecAdd[map.box.min, delta];
newmax: SF.Vec ~ BoundedVecAdd[map.box.max, delta];
chk: SF.Box ~ [min: SFInline.Sub[newmin, delta], max: SFInline.Sub[newmax, delta]];
IF chk # map.box THEN InPlaceClip[map, chk];
map.box ¬ CleanBox[[min: newmin, max: newmax]];
};
ObtainUnsafeDescriptor: PUBLIC UNSAFE PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine: INT, base: BitAddress, ref: REF, words: INT, rawMin, delta: Vec ¬ zeroVec] RETURNS [RasterSampleMap] ~ UNCHECKED {
new: RasterSampleMap ~ UnsafeNewSampleMap[box: [[0, 0], SFInline.Add[rawMin, size]], bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine, base: base, ref: ref, words: words, scratchDescriptor: NewDescriptor[]];
InPlaceShift[new, SFInline.Neg[rawMin]];
InPlaceClip[new, [[0, 0], size]];
InPlaceShift[new, delta];
RETURN [new];
};
CheckBuffer: PROC [map: SampleMap, sInitIndex: INTEGER, fInitIndex: INTEGER, sDelta: INTEGER, fDelta: INTEGER, count: NAT] ~ {
IF count # 0 THEN {
countMinus1: INTEGER = count-1;
s0: INTEGER ¬ sInitIndex;
s1: INTEGER ¬ sInitIndex + (IF sDelta = 0 THEN 0 ELSE sDelta*countMinus1);
f0: INTEGER ¬ fInitIndex;
f1: INTEGER ¬ fInitIndex + (IF fDelta = 1 THEN countMinus1 ELSE fDelta*countMinus1);
IF s0 > s1 THEN { t: INTEGER ¬ s0; s0 ¬ s1; s1 ¬ t };
IF f0 > f1 THEN { t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t };
IF s0 < map.box.min.s
OR f0 < map.box.min.f
OR s1 >= map.box.max.s
OR f1 >= map.box.max.f
THEN Basics.RaiseBoundsFault[];
};
};
ReIndexDataRep: TYPE ~ RECORD [delta: SF.Vec, base: SampleMap];
ReIndexGetSamples: PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT] ~ {
data: REF ReIndexDataRep ~ NARROW[self.data];
count ¬ MIN[buffer.length-start, count];
IF count > 0 THEN {
GetSamples[map: data.base, initIndex: SFInline.Sub[initIndex, data.delta], buffer: buffer, start: start, count: count];
};
};
ReIndexPutSamples: PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT, function: Function] ~ {
data: REF ReIndexDataRep ~ NARROW[self.data];
count ¬ MIN[buffer.length-start, count];
IF count > 0 THEN {
PutSamples[map: data.base, initIndex: SFInline.Sub[initIndex, data.delta], buffer: buffer, start: start, count: count, function: function];
};
};
ReIndex: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [SampleMap] ~ TRUSTED {
WITH map SELECT FROM
map: RasterSampleMap => {
new: RasterSampleMap ¬ NewDescriptor[map];
InPlaceShift[new, delta];
InPlaceClip[new, box];
RETURN [new]
};
map: ObjectSampleMap => {
data: REF ReIndexDataRep ~ NEW[ReIndexDataRep ¬ [delta: delta, base: map]];
newBox: SF.Box ~ SF.Intersect[box, SFInline.Displace[map.box, delta]];
new: ObjectSampleMap ~ NewObjectSampleMap[box: newBox, bitsPerSample: map.bitsPerSample, getSamples: ReIndexGetSamples, putSamples: IF map.immutable THEN NIL ELSE ReIndexPutSamples, data: data];
RETURN [new]
};
ENDCASE => RETURN [NIL];
};
Clip: PUBLIC PROC [map: SampleMap, box: Box] RETURNS [SampleMap] ~ {
RETURN[ReIndex[map: map, delta: zeroVec, box: box]];
};
Shift: PUBLIC PROC [map: SampleMap, delta: Vec] RETURNS [SampleMap] ~ {
RETURN[ReIndex[map: map, delta: delta, box: maxBox]];
};
ZeroOrigin: PUBLIC PROC [map: SampleMap] RETURNS [SampleMap] ~ {
RETURN[ReIndex[map: map, delta: SFInline.Neg[map.box.min], box: maxBox]];
};
SampleMap selectors
GetUnsafeBlock: PUBLIC PROC [map: RasterSampleMap] RETURNS [Basics.UnsafeBlock] ~ {
size: Vec ~ InlineSize[map];
bitsPerLine: CARD ~ map.bitsPerLine;
dataBitsPerLine: CARD ~ CARD[map.bitsPerSample]*CARD[size.f];
fillBitsPerLine: [0..0] ~ bitsPerLine - dataBitsPerLine;
padStart: [0..0] ~ map.base.bit MOD 8;
nBytes: NAT ~ (CARD[size.s]*bitsPerLine + 7)/8;
RETURN [[base: LOOPHOLE[map.base.word], startIndex: map.base.bit/8, count: nBytes]]
};
SampleMap sample access
ApplyFunction: PUBLIC PROC [function: Function, dstVal, srcVal: Sample] RETURNS [Sample] ~ {
IF function.srcFunc = complement THEN srcVal ¬ Basics.BITNOT[srcVal];
SELECT function.dstFunc FROM
null => RETURN [srcVal];
and => RETURN [Basics.BITAND[dstVal, srcVal]];
or => RETURN [Basics.BITOR[dstVal, srcVal]];
xor => RETURN [Basics.BITXOR[dstVal, srcVal]];
ENDCASE => ERROR;
};
ApplyToSamples: PUBLIC PROC [function: Function, dst, src: SampleBuffer, start, count: NAT] ~ {
clippedCount: CARD ¬ MIN[count, NAT[dst.length-start], NAT[src.length-start]];
d: RawSamplesPtr ¬ PointerToSamples[dst, start, clippedCount];
s: RawSamplesPtr ¬ PointerToSamples[src, start, clippedCount];
IF clippedCount # 0 THEN TRUSTED {
bits: CARD ~ clippedCount * CARD[BITS[WORD]];
RasterOp.forwardOp[function.dstFunc][function.srcFunc][
dst: [word: LOOPHOLE[d], bit: 0],
src: [word: LOOPHOLE[s], bit: 0],
dstBpl: bits,
srcBpl: bits,
sSize: 1,
fSize: bits
];
};
};
Lo5: PROC [i: INTEGER] RETURNS [[0..bpw)] = INLINE {
RETURN [LOOPHOLE[i, CARDINAL] MOD bpw]
};
Get: PUBLIC PROC [map: SampleMap, index: Vec] RETURNS [Sample] ~ TRUSTED {
WITH map SELECT FROM
map: RasterSampleMap => {
size: Vec ~ InlineSize[map];
v: Vec ~ SFInline.Sub[index, map.box.min];
bitsPerSample: NAT ~ map.bitsPerSample;
bitIndex: CARD ~ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample]+map.base.bit;
pointer: RawSamplesPtr ~ IndexWord[map.base.word, bitIndex];
shiftAmt: INTEGER ~ Lo5[bitIndex] + bitsPerSample - bpw;
mask: WORD ~ MaskFromBPS[bitsPerSample];
shiftedBits: WORD ~ IF shiftAmt <= 0
THEN Basics.BITRSHIFT[pointer[0], Lo5[-shiftAmt]]
ELSE Basics.BITLSHIFT[pointer[0], Lo5[shiftAmt]] + Basics.BITRSHIFT[pointer[1], Lo5[bpw-shiftAmt]];
RETURN[Basics.BITAND[shiftedBits, mask]];
};
map: ObjectSampleMap => {
buffer: SampleBuffer ~ ObtainScratchSamples[1];
sample: Sample ¬ 0;
true: BOOL[TRUE..TRUE] ~ SFInline.In[index, map.box];
class: SampleMapClass ~ map.class;
class.getSamples[self: map, initIndex: index, buffer: buffer, start: 0, count: 1];
sample ¬ buffer[0];
ReleaseScratchSamples[buffer];
RETURN [sample]
};
ENDCASE => ERROR;
};
Put: PUBLIC PROC [map: SampleMap, index: Vec, value: Sample, function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
size: Vec ~ InlineSize[map];
v: Vec ~ SFInline.Sub[index, map.box.min];
bitsPerSample: NAT ~ map.bitsPerSample;
bitIndex: CARD ~ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample]+map.base.bit;
Basics.CopyBits[
dstBase: LOOPHOLE[map.base.word],
dstStart: bitIndex,
srcBase: LOOPHOLE[@value],
srcStart: Lo5[bpw - bitsPerSample],
count: bitsPerSample
];
};
map: ObjectSampleMap => {
buffer: SampleBuffer ~ ObtainScratchSamples[1];
true: BOOL[TRUE..TRUE] ~ SFInline.In[index, map.box];
class: SampleMapClass ~ map.class;
buffer[0] ¬ value;
class.putSamples[self: map, initIndex: index, buffer: buffer, start: 0, count: 1, function: function];
ReleaseScratchSamples[buffer];
};
ENDCASE => NULL;
};
GetSamples: PUBLIC PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1],
buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount] ~ {
size: Vec ~ InlineSize[map];
count ¬ MIN[buffer.length-start, count];
CheckBuffer[map: map, sInitIndex: initIndex.s, fInitIndex: initIndex.f, sDelta: delta.s, fDelta: delta.f, count: count];
IF delta.s = 0 AND delta.f = 1 AND count # 0
THEN {
WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
v0: Vec ~ SFInline.Sub[initIndex, map.box.min];
srcBitIndex: CARD ~
BoundsCheck[v0.s, size.s]*CARD[map.bitsPerLine] +
BoundsCheck[v0.f, size.f]*CARD[map.bitsPerSample];
RasterOp.Unpack[dst: InlinePointerToSamples[buffer, start, count], src: IndexBit[map.base, srcBitIndex], bitsPerSample: map.bitsPerSample, nSamples: count];
};
map: ObjectSampleMap => {
class: SampleMapClass ~ map.class;
class.getSamples[self: map, initIndex: initIndex, buffer: buffer, start: start, count: count];
};
ENDCASE => ERROR;
}
ELSE {
WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
v: Vec ~ SFInline.Sub[initIndex, map.box.min];
bitsPerSample: NAT ~ map.bitsPerSample;
bitIndex: CARD ¬ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample] + map.base.bit;
bitIndexDelta: INT ~ INT[map.bitsPerLine]*delta.s + INT[map.bitsPerSample]*delta.f;
SELECT TRUE FROM
bitsPerSample = BITS[BYTE]
AND (bitIndex MOD BITS[BYTE]) = 0
AND (LOOPHOLE[bitIndexDelta, CARD] MOD BITS[BYTE]) = 0 => {
Byte-aligned, so faster!
step: INT = bitIndexDelta / BITS[BYTE];
bx: CARDINAL ¬ bitIndex / BITS[BYTE];
src: RawBytesPtr = LOOPHOLE[map.base.word, RawBytesPtr];
dst: RawSamplesPtr ¬ LOOPHOLE[InlinePointerToSamples[buffer, start, count]];
lim: RawSamplesPtr = dst + count*SIZE[WORD];
lim4: RawSamplesPtr = lim - (count MOD 4)*SIZE[WORD];
WHILE dst # lim4 DO
dst[0] ¬ src[bx]; bx ¬ bx + step;
dst[1] ¬ src[bx]; bx ¬ bx + step;
dst[2] ¬ src[bx]; bx ¬ bx + step;
dst[3] ¬ src[bx]; bx ¬ bx + step;
dst ¬ dst + SIZE[WORD]*4;
ENDLOOP;
WHILE dst # lim DO
dst[0] ¬ src[bx];
dst ¬ dst + SIZE[WORD];
bx ¬ bx + step;
ENDLOOP;
};
ENDCASE => {
mask: WORD ~ MaskFromBPS[bitsPerSample];
FOR i: NAT IN [start..start+count) DO
buffer[i] ← Get[map, index];
pointer: RawSamplesPtr ~ IndexWord[map.base.word, bitIndex];
shiftAmt: INTEGER ~ Lo5[bitIndex] + bitsPerSample - bpw;
shiftedBits: WORD ~ IF shiftAmt <= 0
THEN Basics.BITRSHIFT[pointer[0], Lo5[-shiftAmt]]
ELSE Basics.BITLSHIFT[pointer[0], Lo5[shiftAmt]] + Basics.BITRSHIFT[pointer[1], Lo5[bpw-shiftAmt]];
buffer[i] ¬ Basics.BITAND[shiftedBits, mask];
bitIndex ¬ bitIndex + bitIndexDelta;
ENDLOOP;
};
};
map: ObjectSampleMap => {
index: Vec ¬ initIndex;
class: SampleMapClass ~ map.class;
FOR i: NAT IN [start..start+count) DO
class.getSamples[self: map, initIndex: index, buffer: buffer, start: i, count: 1];
index ¬ SFInline.Add[index, delta];
ENDLOOP;
};
ENDCASE => ERROR;
};
};
PutSamples: PUBLIC PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1],
buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount,
function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
size: Vec ~ InlineSize[map];
sDelta: INTEGER = delta.s;
fDelta: INTEGER = delta.f;
count ¬ MIN[buffer.length-start, count];
CheckBuffer[map: map, sInitIndex: initIndex.s, fInitIndex: initIndex.f, sDelta: sDelta, fDelta: fDelta, count: count];
IF count = 0 THEN RETURN;
IF (sDelta = 0 AND fDelta = 1) THEN {
srcBuffer: SampleBuffer ¬ buffer;
IF function # nullFunction THEN {
tmpBuffer: SampleBuffer ~ ObtainScratchSamples[length: start+count];
GetSamples[map: map, initIndex: initIndex, buffer: tmpBuffer, start: start, count: count];
ApplyToSamples[function: function, dst: tmpBuffer, src: buffer, start: start, count: count];
srcBuffer ¬ tmpBuffer;
};
WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
v0: Vec ~ SFInline.Sub[initIndex, map.box.min];
dstBitIndex: CARD ~
BoundsCheck[v0.s, size.s]*CARD[map.bitsPerLine] +
BoundsCheck[v0.f, size.f]*CARD[map.bitsPerSample];
RasterOp.Pack[dst: IndexBit[map.base, dstBitIndex], src: InlinePointerToSamples[srcBuffer, start, count], bitsPerSample: map.bitsPerSample, nSamples: count];
};
map: ObjectSampleMap => {
class: SampleMapClass ~ map.class;
class.putSamples[self: map, initIndex: initIndex, buffer: srcBuffer, start: start, count: count, function: function];
};
ENDCASE => ERROR;
IF srcBuffer # buffer THEN ReleaseScratchSamples[buffer: srcBuffer];
RETURN;
};
IF map.bitsPerSample = 8 AND function=nullFunction THEN WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
IF map.bitsPerLine MOD 8 = 0 AND map.base.bit MOD 8 = 0 THEN {
dst: POINTER TO Basics.RawBytes ¬ LOOPHOLE[map.base.word];
byteIndex: INT ¬ (map.base.bit/8) + initIndex.s * (map.bitsPerLine/8) + initIndex.f;
byteDelta: INT = sDelta * (map.bitsPerLine/8) + fDelta;
src: POINTER TO Basics.RawWords = InlinePointerToSamples[buffer, start, count];
FOR i: CARDINAL IN [0..count) DO
dst[byteIndex] ¬ src[i];
byteIndex ¬ byteIndex + byteDelta;
ENDLOOP;
RETURN;
};
};
ENDCASE;
CHECKED {
Should maybe improve this case someday.
index: Vec ¬ initIndex;
FOR i: NAT IN [start..start+count) DO
PutSamples[map: map, initIndex: index, buffer: buffer, start: i, count: 1, function: function];
index ¬ SFInline.Add[index, delta];
ENDLOOP;
};
};
SampleMap block operations
Clear: PUBLIC PROC [map: SampleMap] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
WITH map SELECT FROM
map: RasterSampleMap => TRUSTED {
sSize: CARDINAL ~ LOOPHOLE[map.box.max.s - map.box.min.s, CARDINAL];
fSize: CARDINAL ~ LOOPHOLE[map.box.max.f - map.box.min.f, CARDINAL];
bitsPerLine: CARD ~ map.bitsPerLine;
dataBitsPerLine: CARD ~ CARD[map.bitsPerSample]*CARD[fSize];
pointer: LONG POINTER ¬ map.base.word;
bit: CARD ¬ map.base.bit;
IF sSize = 0 THEN RETURN;
IF dataBitsPerLine+bpw > bitsPerLine
THEN {
We get to clear the whole thing in one shot.
Basics.FillWords[dst: pointer, count: WordsForBits[bit+bitsPerLine*CARD[sSize]-(bitsPerLine-dataBitsPerLine)], value: 0];
}
ELSE {
Clear each scanline.
FOR i: CARDINAL IN [0..sSize) DO
Basics.FillWords[dst: pointer, count: WordsForBits[bit+dataBitsPerLine], value: 0];
bit ¬ bit + bitsPerLine;
pointer ¬ pointer + WordFloorUnitsForBits[bit];
bit ¬ bit MOD bpw;
ENDLOOP;
};
};
map: ObjectSampleMap => Fill[map, map.box, 0];
ENDCASE => NULL;
};
BufferTransfer: PROC [dst: SampleMap, src: SampleMap,
dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec, size: Vec ¬ maxVec,
function: Function ¬ nullFunction] ~ {
dstSize: Vec ~ InlineSize[dst];
srcSize: Vec ~ InlineSize[src];
sDst0: NAT ~ dstMin.s - dst.box.min.s;
fDst0: NAT ~ dstMin.f - dst.box.min.f;
sSrc0: NAT ~ srcMin.s - src.box.min.s;
fSrc0: NAT ~ srcMin.f - src.box.min.f;
sSize: NAT ~ Check[Check[size.s, dstSize.s-sDst0], srcSize.s-sSrc0];
fSize: NAT ~ Check[Check[size.f, dstSize.f-fDst0], srcSize.f-fSrc0];
transferAction: PROC [buffer: SampleBuffer] ~ {
FOR s: NAT IN [0..sSize) DO
GetSamples[map: src, initIndex: [s: srcMin.s+s, f: srcMin.f], buffer: buffer];
PutSamples[map: dst, initIndex: [s: dstMin.s+s, f: dstMin.f], buffer: buffer,
function: function];
ENDLOOP;
};
DoWithScratchSamples[length: fSize, action: transferAction];
};
BasicTransfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec, size: Vec, function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ dst.immutable;
bitsPerSample: NAT ~ dst.bitsPerSample;
WITH dst SELECT FROM dst: RasterSampleMap => WITH src SELECT FROM src: RasterSampleMap => IF src.bitsPerSample=bitsPerSample THEN TRUSTED {
dstSize: Vec ~ InlineSize[dst];
srcSize: Vec ~ InlineSize[src];
sDst0: NAT ~ dstMin.s - dst.box.min.s;
fDst0: NAT ~ dstMin.f - dst.box.min.f;
sSrc0: NAT ~ srcMin.s - src.box.min.s;
fSrc0: NAT ~ srcMin.f - src.box.min.f;
sSize: NAT ~ Check[Check[size.s, dstSize.s-sDst0], srcSize.s-sSrc0];
fSize: NAT ~ Check[Check[size.f, dstSize.f-fDst0], srcSize.f-fSrc0];
IF sSize#0 AND fSize#0 THEN RasterOp.forwardOp[function.dstFunc][function.srcFunc][
dst: IndexBit[dst.base, CARD[sDst0]*dst.bitsPerLine + CARD[fDst0]*bitsPerSample],
src: IndexBit[src.base, CARD[sSrc0]*src.bitsPerLine + CARD[fSrc0]*bitsPerSample],
dstBpl: dst.bitsPerLine,
srcBpl: src.bitsPerLine,
sSize: sSize,
fSize: CARD[fSize]*bitsPerSample
];
RETURN;
} ENDCASE ENDCASE;
BufferTransfer[dst, src, dstMin, srcMin, size, function];
};
RawListTransfer: PUBLIC PROC [dst: RasterSampleMap, src: LIST OF RawDescriptor, function: Function ¬ nullFunction] ~ TRUSTED {
false: BOOL[FALSE..FALSE] ~ dst.immutable;
bitsPerSample: [1..1] ~ dst.bitsPerSample;
dstPtr: RasterOp.WordPtr = LOOPHOLE[dst.base.word];
dstOff: NAT = dst.base.bit;
IF ImagerSwitches.BoolValue[useCharBlt]
AND (function = [or, null] OR function = [and, complement])
AND dst.bitsPerLine MOD BITS[RasterOp.Word] = 0
THEN {
The fast case
bitsPerUnit: NAT = BITS[UNIT];
dstUnitsPerLine: NAT = dst.bitsPerLine / bitsPerUnit;
op: UNSAFE PROC [src: RasterOp.WordPtr, dst: RasterOp.WordPtr, offset: RasterOp.Offset, bpc: NAT, upl: INT, lines: INT] ~ (IF function = [or, null] THEN RasterOp.OrBlt ELSE RasterOp.AndCBlt);
WHILE src # NIL DO
sCheck: NAT ~ dst.box.max.s-src.first.box.max.s;
fCheck: NAT ~ dst.box.max.f-src.first.box.max.f;
sSize: INTEGER = src.first.box.max.s-src.first.box.min.s;
fSize: INTEGER = src.first.box.max.f-src.first.box.min.f;
IF sSize > 0 AND fSize > 0 THEN {
dstBitIndex: CARD ~ dstOff
+ CARD[src.first.box.min.s-dst.box.min.s]*dst.bitsPerLine
+ CARD[src.first.box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1)
mod: RasterOp.Offset = dstBitIndex MOD BITS[RasterOp.Word];
op[
src: LOOPHOLE[src.first.basePointer, RasterOp.WordPtr],
dst: dstPtr + (dstBitIndex-mod) / bitsPerUnit,
offset: mod,
bpc: fSize,
upl: dstUnitsPerLine,
lines: sSize];
We assume that the character width needs the same number of words as the src bitsPerLine. If this is not true, and we want to take advantage of it, then SmallCharBlt needs to have some rewriting.
};
src ¬ src.rest;
ENDLOOP;
}
ELSE {
The general case
op: UNSAFE PROC [dst: BitAddress, src: BitAddress, dstBpl, srcBpl, sSize, fSize: CARDINAL] ~ RasterOp.forwardOp[function.dstFunc][function.srcFunc];
WHILE src # NIL DO
box: SF.Box ~ [min: src.first.box.min, max: src.first.box.max];
sCheck: NAT ~ dst.box.max.s-box.max.s;
fCheck: NAT ~ dst.box.max.f-box.max.f;
dstBitIndex: CARD ~ CARD[NAT[box.min.s-dst.box.min.s]]*dst.bitsPerLine
+ NAT[box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1)
sSize: NAT = SFInline.SizeS[box];
fSize: NAT = SFInline.SizeF[box]; -- (know bitsPerSample = 1)
dstBA: BitAddress = IndexBit[dst.base, dstBitIndex];
IF sSize > 0 AND fSize > 0 THEN
op[
dst: dstBA,
src: [word: src.first.basePointer, bit: 0],
dstBpl: dst.bitsPerLine,
srcBpl: src.first.bitsPerLine,
sSize: sSize,
fSize: fSize
];
src ¬ src.rest;
ENDLOOP;
};
};
Transfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec, function: Function ¬ nullFunction] ~ {
box: Box ~ SFInline.Intersect[dst.box, SFInline.Displace[src.box, delta]];
IF SFInline.Nonempty[box] THEN BasicTransfer[dst: dst, src: src, dstMin: box.min, srcMin: SFInline.Sub[box.min, delta], size: SFInline.Size[box], function: function];
};
TransferBoxes: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec,
boxes: BoxGenerator, function: Function ¬ nullFunction] ~ {
bitsPerSample: NAT ~ dst.bitsPerSample;
false: BOOL[FALSE..FALSE] ~ dst.immutable;
WITH dst SELECT FROM dst: RasterSampleMap => WITH src SELECT FROM src: RasterSampleMap => IF src.bitsPerSample=bitsPerSample THEN TRUSTED {
dstBox: Box ~ dst.box;
srcBox: Box ~ SFInline.Displace[src.box, delta];
bounds: Box ~ SFInline.Intersect[dstBox, srcBox];
dstBPL: NAT ~ dst.bitsPerLine;
srcBPL: NAT ~ src.bitsPerLine;
op: UNSAFE PROC [dst: BitAddress, src: BitAddress, dstBpl, srcBpl, sSize, fSize: CARDINAL] ~ RasterOp.forwardOp[function.dstFunc][function.srcFunc];
FastTransfer: BoxAction ~ TRUSTED {
sSize: INTEGER = box.max.s-box.min.s;
fSize: INTEGER = box.max.f-box.min.f;
IF sSize > 0 AND fSize > 0 THEN {
sSpaceLo: NAT ~ box.min.s-bounds.min.s; -- for bounds check
fSpaceLo: NAT ~ box.min.f-bounds.min.f;
sSpaceHi: NAT ~ bounds.max.s-box.max.s;
fSpaceHi: NAT ~ bounds.max.f-box.max.f;
dstBitIndex: CARD ~
CARD[NAT[box.min.s-dstBox.min.s]]*dstBPL +
CARD[NAT[box.min.f-dstBox.min.f]]*bitsPerSample;
srcBitIndex: CARD ~
CARD[NAT[box.min.s-srcBox.min.s]]*srcBPL +
CARD[NAT[box.min.f-srcBox.min.f]]*bitsPerSample;
dstBA: BitAddress = IndexBit[dst.base, dstBitIndex];
srcBA: BitAddress = IndexBit[src.base, srcBitIndex];
op[
dst: dstBA,
src: srcBA,
dstBpl: dstBPL,
srcBpl: srcBPL,
sSize: sSize,
fSize: CARD[NAT[fSize]]*bitsPerSample
];
};
};
boxes[FastTransfer];
RETURN;
} ENDCASE ENDCASE;
-- slow case -- {
SlowTransfer: BoxAction ~ {
dstMin: Vec ~ box.min;
srcMin: Vec ~ SFInline.Sub[box.min, delta];
BufferTransfer[dst, src, dstMin, srcMin, SFInline.Sub[box.max, box.min], function];
};
boxes[SlowTransfer];
};
};
Move: PUBLIC PROC [map: SampleMap, dstMin, srcMin, size: Vec, function: Function] ~ TRUSTED {
false: BOOL[FALSE..FALSE] ~ map.immutable;
selfSize: Vec ~ InlineSize[map];
sDst0: NAT ~ dstMin.s - map.box.min.s;
fDst0: NAT ~ dstMin.f - map.box.min.f;
sSrc0: NAT ~ srcMin.s - map.box.min.s;
fSrc0: NAT ~ srcMin.f - map.box.min.f;
sSize: NAT ~ Check[Check[size.s, selfSize.s-sDst0], selfSize.s-sSrc0];
fSize: NAT ~ Check[Check[size.f, selfSize.f-fDst0], selfSize.f-fSrc0];
IF sSize=0 OR fSize = 0 THEN RETURN;
WITH map SELECT FROM
map: RasterSampleMap => {
bitsPerSample: NAT ~ map.bitsPerSample;
bitsPerLine: NAT ~ map.bitsPerLine;
dstBitIndex: CARD ¬ CARD[sDst0]*bitsPerLine + CARD[fDst0]*bitsPerSample;
srcBitIndex: CARD ¬ CARD[sSrc0]*bitsPerLine + CARD[fSrc0]*bitsPerSample;
IF (fSrc0+fSize) > fDst0 AND (fDst0+fSize) > fSrc0 AND
(sSrc0+sSize) > sDst0 AND (sDst0+sSize) > sSrc0 AND
(sDst0>sSrc0 OR (sDst0=sSrc0 AND fDst0>fSrc0))
THEN {
-- the rectangles overlap in a bad way for forward moves
delta: CARD ~ CARD[NAT[sSize-1]]*bitsPerLine;
dstBitIndex ← dstBitIndex + delta;
srcBitIndex ← srcBitIndex + delta;
RasterOp.backwardOp[function.dstFunc][function.srcFunc][
dst: IndexBit[map.base, dstBitIndex],
src: IndexBit[map.base, srcBitIndex],
dstBpl: bitsPerLine,
srcBpl: bitsPerLine,
sSize: sSize,
fSize: CARD[fSize]*bitsPerSample
];
-- Until RasterOp.backwardOp exists, we will make do with another way.
IF srcMin.s < dstMin.s
THEN {
Do it a band at a time.
bandSize: NAT ~ dstMin.s - srcMin.s;
FOR sEnd: INTEGER ¬ size.s, sEnd-bandSize UNTIL sEnd <= 0 DO
sBegin: INTEGER ¬ MAX[sEnd-bandSize, 0];
BasicTransfer[
dst: map, src: map,
dstMin: [dstMin.s+sBegin, dstMin.f],
srcMin: [srcMin.s+sBegin, srcMin.f],
size: [sEnd-sBegin, size.f],
function: function
];
ENDLOOP;
}
ELSE {
Desparate case: make a copy.
temp: SampleMap ~ ObtainScratchMap[box: [max: size], bitsPerSample: map.bitsPerSample];
BasicTransfer[dst: temp, src: map, srcMin: srcMin, size: size, function: [null, null]];
BasicTransfer[dst: map, src: temp, dstMin: dstMin, size: size, function: function];
ReleaseScratchMap[temp];
};
}
ELSE {
RasterOp.forwardOp[function.dstFunc][function.srcFunc][
dst: IndexBit[map.base, dstBitIndex],
src: IndexBit[map.base, srcBitIndex],
dstBpl: bitsPerLine,
srcBpl: bitsPerLine,
sSize: sSize,
fSize: CARD[fSize]*bitsPerSample
];
};
};
map: ObjectSampleMap => {
buffer: SampleBuffer ~ ObtainScratchSamples[fSize];
class: SampleMapClass ~ map.class;
IF sDst0 <= sSrc0
THEN {
FOR s: NAT IN [0..sSize) DO
class.getSamples[self: map, initIndex: [s+srcMin.s, srcMin.f], buffer: buffer, start: 0, count: fSize];
class.putSamples[self: map, initIndex: [s+dstMin.s, dstMin.f], buffer: buffer, start: 0, count: fSize, function: nullFunction];
ENDLOOP;
}
ELSE {
FOR s: NAT DECREASING IN [0..sSize) DO
class.getSamples[self: map, initIndex: [s+srcMin.s, srcMin.f], buffer: buffer, start: 0, count: fSize];
class.putSamples[self: map, initIndex: [s+dstMin.s, dstMin.f], buffer: buffer, start: 0, count: fSize, function: nullFunction];
ENDLOOP;
};
ReleaseScratchSamples[buffer];
};
ENDCASE => NULL;
};
BufferFill: PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample, function: Function ¬ nullFunction] ~ {
box ¬ SF.Intersect[map.box, box];
IF SFInline.Nonempty[box] THEN {
buffer: SampleBuffer ~ ObtainScratchSamples[box.max.f-box.min.f];
FOR i: NAT IN [0..buffer.length) DO buffer[i] ¬ value ENDLOOP;
FOR s: NAT IN [box.min.s..box.max.s) DO
PutSamples[map: map, initIndex: [s, box.min.f], buffer: buffer, function: function];
ENDLOOP;
ReleaseScratchSamples[buffer];
};
};
replicator: ARRAY [0..5] OF Sample ~ [WORD.LAST, WORD.LAST/3, WORD.LAST/15, WORD.LAST/255, WORD.LAST/65535, 1];
Fill: PUBLIC PROC [map: SampleMap, box: Box, value: Sample, function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
bitsPerSample: NAT ~ map.bitsPerSample;
WITH map SELECT FROM map: RasterSampleMap =>
IF IsPowerOfTwo[bitsPerSample] THEN TRUSTED {
bitsPerLine: NAT ~ map.bitsPerLine;
actualBox: Box ~ SFInline.Intersect[map.box, box];
sSize: CARDINAL ~ SFInline.SizeS[actualBox];
fSize: CARDINAL ~ SFInline.SizeF[actualBox];
dstBitIndex: CARD ~
CARD[NAT[actualBox.min.s-map.box.min.s]]*bitsPerLine +
CARD[NAT[actualBox.min.f-map.box.min.f]]*bitsPerSample;
bitMask: WORD ~ MaskFromBPS[bitsPerSample];
replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[Lg[bitsPerSample]];
RasterOp.tileOp[function.dstFunc][function.srcFunc][
dst: IndexBit[map.base, dstBitIndex],
src: LOOPHOLE[@replicatedPixel],
dstBpl: bitsPerLine,
src0: 0,
sSizeTile: 1,
sSize: sSize,
fSize: CARD[fSize]*bitsPerSample
];
RETURN;
};
ENDCASE;
BufferFill[map: map, box: box, value: value, function: function];
};
FillBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator,
value: Sample ¬ maxSample, function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
bitsPerSample: NAT ~ map.bitsPerSample;
WITH map SELECT FROM map: RasterSampleMap =>
IF IsPowerOfTwo[bitsPerSample] THEN TRUSTED {
lgBitsPerSample: [0..5] ~ Lg[bitsPerSample];
bitMask: WORD ~ Basics.BITLSHIFT[1, bitsPerSample]-1;
replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample];
op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc];
FastFill: BoxAction ~ TRUSTED {
sMin: INTEGER ~ box.min.s;
fMin: INTEGER ~ box.min.f;
sMax: INTEGER ~ box.max.s;
fMax: INTEGER ~ box.max.f;
IF sMax > sMin AND fMax > fMin THEN {
m: RasterSampleMap ~ map;
bitsPerLine: CARDINAL ~ m.bitsPerLine;
sDst: CARDINAL ~ LOOPHOLE[sMin-m.box.min.s];
fDst: CARDINAL ~ LOOPHOLE[fMin-m.box.min.f];
lgBPS: [0..5] ~ lgBitsPerSample;
check: NAT ~ Basics.BITOR[Basics.BITOR[sDst, fDst], Basics.BITOR[LOOPHOLE[m.box.max.s-sMax, CARDINAL], LOOPHOLE[m.box.max.f-fMax, CARDINAL]]]; -- for bounds check
bitDst: CARD ~ sDst*bitsPerLine + Basics.BITLSHIFT[fDst, lgBPS] + m.base.bit;
fBitCount: CARD ~ Basics.BITLSHIFT[LOOPHOLE[fMax-fMin, CARDINAL], lgBPS];
mod: CARDINAL [0..bpw) = bitDst MOD bpw;
wordAddress: POINTER ~ m.base.word + (bitDst - mod) / bpu;
sSize: CARDINAL ~ LOOPHOLE[sMax-sMin, CARDINAL];
IF function = nullFunction THEN SELECT TRUE FROM
(fBitCount=bitsPerLine AND mod=0 AND (fBitCount MOD bpw)=0) => {
Basics.FillWords[dst: wordAddress, count: (fBitCount/bpw)*sSize, value: replicatedPixel];
RETURN;
};
(sSize=1) => {
Basics.FillBits[dstBase: wordAddress, dstStart: mod, count: fBitCount, value: replicatedPixel];
RETURN;
};
ENDCASE;
op[
dst: [word: wordAddress, bit: mod],
src: LOOPHOLE[@replicatedPixel],
dstBpl: bitsPerLine,
src0: 0,
sSizeTile: 1,
sSize: sSize,
fSize: fBitCount
];
};
};
boxes[FastFill];
RETURN;
};
ENDCASE;
-- slow case -- {
SlowFill: BoxAction ~ { BufferFill[map, box, value, function] };
boxes[SlowFill];
};
};
RegionFill: PUBLIC PROC [dst: RasterSampleMap, edgeGenerator: PROC [EdgeAction], value: Sample, function: Function] ~ TRUSTED {
false: BOOL[FALSE..FALSE] ~ dst.immutable;
Edge: TYPE ~ RECORD [sMin: INTEGER, sMax: INTEGER, f0: ImagerScaled.Value, df: ImagerScaled.Value];
edges: ARRAY [0..1] OF Edge ¬ ALL[[0, 0, ImagerScaled.zero, ImagerScaled.zero]];
bitsPerSample: NAT ~ dst.bitsPerSample;
zero: [0..0] ~ bpw MOD bitsPerSample; -- not implemented for strange bps . . .
lgBitsPerSample: [0..5] ~ Lg[bitsPerSample]; -- so we can shift rather than multiply
op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc];
edgeAction: SAFE PROC [which: [0..1], sMin: INTEGER, sCount: NAT, f0, df: ImagerScaled.Value] ~ TRUSTED {
lgbps: [0..5] ~ lgBitsPerSample;
Ix: SAFE PROC [f: INTEGER] RETURNS [CARDINAL] ~ CHECKED INLINE {
RETURN [Basics.BITLSHIFT[LOOPHOLE[f, CARDINAL], lgbps]]
};
e: ARRAY [0..1] OF Edge ¬ [
IF which = 0 THEN [sMin: sMin, sMax: sMin+sCount, f0: f0, df: df] ELSE edges[0],
IF which = 1 THEN [sMin: sMin, sMax: sMin+sCount, f0: f0, df: df] ELSE edges[1]
];
IF (e[0].sMin < e[0].sMax) AND (e[1].sMin < e[1].sMax) AND (e[0].sMin < dst.box.max.s) THEN {
s: INTEGER ¬ e[0].sMin;
zero: [0..0] ~ s - e[1].sMin;
box: Box ~ dst.box;
sMax: INTEGER ~ MIN[e[0].sMax, e[1].sMax, box.max.s];
currentBox: SF.Box ¬ [];
bitsPerLine: NAT ~ dst.bitsPerLine;
line: BitAddress ¬ IndexBit[dst.base, CARD[NAT[s-box.min.s]]*bitsPerLine];
IF e[0].df = ImagerScaled.zero AND e[1].df = ImagerScaled.zero AND s >= box.min.s
THEN {
This is just a rectangle - do it with one BITBLT
f0: INTEGER ¬ ImagerScaled.Floor[e[0].f0];
f1: INTEGER ¬ ImagerScaled.Floor[e[1].f0];
IF f0 > f1 THEN {t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t};
IF f0 < box.min.f THEN f0 ¬ box.min.f;
IF f1 > box.max.f THEN f1 ¬ box.max.f;
IF f0 < f1 THEN {
offset: CARDINAL ~ line.bit+Ix[f0-box.min.f];
op[
dst: [word: LOOPHOLE[line.word+SIZE[RawSamples[offset/bpw]]], bit: offset MOD bpw],
src: LOOPHOLE[@replicatedPixel],
dstBpl: bitsPerLine,
src0: 0,
sSizeTile: 1,
sSize: sMax-s,
fSize: Ix[f1-f0]
];
};
s ¬ sMax;
}
ELSE {
WHILE s < sMax DO
f0: INTEGER ¬ ImagerScaled.Floor[e[0].f0];
f1: INTEGER ¬ ImagerScaled.Floor[e[1].f0];
IF f0 > f1 THEN {t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t};
IF f0 < box.min.f THEN f0 ¬ box.min.f;
IF f1 > box.max.f THEN f1 ¬ box.max.f;
IF s >= box.min.s AND f0 < f1 THEN {
offset: CARDINAL ~ line.bit+Ix[f0-box.min.f];
op[
dst: [word: line.word+SIZE[RawSamples[offset/bpw]], bit: offset MOD bpw],
src: LOOPHOLE[@replicatedPixel],
dstBpl: bitsPerLine,
src0: 0,
sSizeTile: 1,
sSize: 1,
fSize: Ix[f1-f0]
];
};
e[0].f0 ¬ ImagerScaled.PLUS[e[0].f0, e[0].df];
e[1].f0 ¬ ImagerScaled.PLUS[e[1].f0, e[1].df];
{offset: CARDINAL ~ line.bit+bitsPerLine;
line.word ¬ line.word + SIZE[RawSamples[offset/bpw]];
line.bit ¬ offset MOD bpw;
};
s ¬ s + 1;
ENDLOOP;
};
e[0].sMin ¬ e[1].sMin ¬ s;
};
edges ¬ e;
};
bitMask: WORD ~ MaskFromBPS[bitsPerSample];
replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample];
edgeGenerator[edgeAction];
};
Tile operations
tileFromStippleRepl: NAT ¬ 32;
Replication factor in the f direction for the hard case of TileFromStipple
TileFromStipple: PUBLIC PROC [stipple: WORD, bitsPerSample: BitsPerSample, value0: Sample, value1: Sample, scratch: SampleMap] RETURNS [SampleMap] ~ {
This attempts to create a tile pattern that will be able to take advantage of the tile case of BITBLT, if possible.
easy: BOOL ~ (bpw MOD (bitsPerSample*4) = 0);
size: Vec ~ IF easy THEN [s: 4, f: bpw/bitsPerSample] ELSE [s: 8, f: tileFromStippleRepl*4];
temp: SampleMap ~ ObtainScratchMap[[[0, 0], [4, 4]], bitsPerSample];
tile: SampleMap ¬ scratch;
IF tile = NIL OR NOT (tile.bitsPerSample = bitsPerSample AND tile.box = [zeroVec, size])
THEN { tile ¬ NewSampleMap[box: [zeroVec, size], bitsPerSample: bitsPerSample] };
Clear[tile];
FOR s: NAT DECREASING IN [0..4) DO
FOR f: NAT DECREASING IN [0..4) DO
Put[temp, [s: s, f: f], (IF (stipple MOD 2)=0 THEN value0 ELSE value1)];
stipple ¬ stipple/2;
ENDLOOP;
ENDLOOP;
GeneralTile[map: tile, tile: temp];
ReleaseScratchMap[temp];
RETURN[tile];
};
QR: TYPE ~ RECORD [quotient: INTEGER, remainder: NAT];
DivMod: PROC [n: INT, d: NAT] RETURNS [qr: QR] ~ {
Number-theoretic: 0 <= remainder < d, n = quotient*d + remainder
qr.remainder ¬ Mod[n, d];
qr.quotient ¬ (n-qr.remainder) / d;
};
Mod: PROC [n: INT, d: NAT] RETURNS [NAT] ~ {
Number-theoretic: 0 <= remainder < d
IF d#1
THEN {
IF n >= 0
THEN RETURN [CARD[n] MOD d]
ELSE {
remainder: INT ¬ n MOD d;
WHILE remainder < 0 DO remainder ¬ remainder + d ENDLOOP;
RETURN [remainder]
};
}
ELSE RETURN [0];
};
QMul: PROC [a: INTEGER, b: CARDINAL] RETURNS [INT] ~ INLINE {
RETURN [INT[a]*b]
};
GeneralTile: PROC [map: SampleMap, box: Box ¬ maxBox, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ {
boxes: BoxGenerator ~ { boxAction[SF.Intersect[map.box, box]] };
GeneralTileBoxes[map, boxes, tile, phase, function];
};
GetTileSamples: PUBLIC PROC [tile: SampleMap, phase: NAT, initIndex: SF.Vec, buffer: SampleBuffer] ~ {
s0: INT ~ tile.box.min.s;
f0: INT ~ tile.box.min.f;
sOffset: INT ~ initIndex.s-s0;
tileSize: Vec ~ SFInline.Size[tile.box];
qr: QR ~ DivMod[sOffset, tileSize.s];
fOffset: INT ~ initIndex.f-(f0+QMul[qr.quotient, phase]);
srcIndexRel: Vec ~ [s: qr.remainder, f: Mod[fOffset, tileSize.f]];
srcIndex: Vec ~ SFInline.Add[srcIndexRel, tile.box.min];
count1: NAT ~ MIN[buffer.length, tileSize.f-srcIndexRel.f];
count2: NAT ~ MIN[buffer.length-count1, srcIndexRel.f];
count3: NAT ~ buffer.length-count1-count2;
GetSamples[map: tile, initIndex: srcIndex, delta: [0, 1], buffer: buffer, start: 0, count: count1];
IF count2 # 0 THEN GetSamples[map: tile, initIndex: [srcIndex.s, tile.box.min.f], delta: [0, 1], buffer: buffer, start: count1, count: count2];
IF count3 # 0 THEN CopySamples[dst: buffer, src: buffer, dstStart: count1+count2, srcStart: 0, count: count3];
};
GeneralTileBoxes: PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ {
GeneralTile: BoxAction ~ {
s0: INT ~ tile.box.min.s;
f0: INT ~ tile.box.min.f;
actualBox: Box ~ SFInline.Intersect[map.box, box];
sOffset: INT ~ actualBox.min.s-s0;
tileSize: Vec ~ SFInline.Size[tile.box];
qr: QR ~ DivMod[sOffset, tileSize.s];
fOffset: INT ~ actualBox.min.f-(f0+QMul[qr.quotient, phase]);
dstRow: Vec ¬ actualBox.min;
srcRow: Vec ¬ [s: qr.remainder, f: Mod[fOffset, tileSize.f]];
WHILE dstRow.s < actualBox.max.s DO
sSize: NAT ~ MIN[tileSize.s-srcRow.s, actualBox.max.s-dstRow.s];
dstCol: Vec ¬ dstRow;
srcCol: Vec ¬ srcRow;
WHILE dstCol.f < actualBox.max.f DO
fSize: NAT ~ MIN[tileSize.f-srcCol.f, actualBox.max.f-dstCol.f];
BasicTransfer[dst: map, src: tile, dstMin: dstCol, srcMin: SFInline.Add[tile.box.min, srcCol], size: [s: sSize, f: fSize], function: function];
dstCol.f ¬ dstCol.f+fSize;
srcCol.f ¬ 0;
ENDLOOP;
dstRow.s ¬ dstRow.s+sSize;
srcRow.s ¬ 0;
srcRow.f ¬ IF srcRow.f<phase THEN srcRow.f+(tileSize.f-phase) ELSE srcRow.f-phase;
ENDLOOP;
};
tileSizeF: NAT ~ SFInline.SizeF[tile.box];
IF phase>=tileSizeF THEN phase ¬ phase MOD tileSizeF;
boxes[GeneralTile];
};
Tile: PUBLIC PROC [map: SampleMap, box: Box ¬ maxBox, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ {
boxes: BoxGenerator ~ { boxAction[SF.Intersect[box, map.box]] };
TileBoxes[map, boxes, tile, phase, function];
};
smallWidth: NAT ¬ 256;
Tiles smaller than this many bits in the f direction will get replicated into a macro tile of at least this many bits in the f direction (if they don't hit a fast case first)
TileBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ {
false: BOOL[FALSE..FALSE] ~ map.immutable;
bitsPerSample: NAT ~ map.bitsPerSample;
sSizeTile: NAT ~ tile.box.max.s-tile.box.min.s;
fSizeTile: NAT ~ tile.box.max.f-tile.box.min.f;
fSizeSelf: NAT ~ map.box.max.f-map.box.min.f;
WITH map SELECT FROM map: RasterSampleMap => WITH tile SELECT FROM tile: RasterSampleMap => {
IF ImagerSwitches.BoolValue[useUnsafeTile] AND bitsPerSample=1 AND tile.bitsPerSample=1 THEN TRUSTED {
arg: UnsafeTile.ArgRecord;
UnsafeFastTile: BoxAction ~ TRUSTED {
IF box.max.s > box.min.s AND box.max.f > box.min.f THEN {
m: RasterSampleMap ~ map;
sDst: NAT ~ box.min.s - m.box.min.s;
fDst: NAT ~ box.min.f - m.box.min.f;
sSpace: NAT ~ m.box.max.s - box.max.s; -- for bounds check
fSpace: NAT ~ m.box.max.f - box.max.f;
qr: QR ~ DivMod[(box.min.s-tile.box.min.s), sSizeTile];
[[word: arg.dstWord, bit: arg.dstBit]] ¬ IndexBit[map.base, sDst*map.bitsPerLine+fDst];
arg.sSize ¬ box.max.s-box.min.s;
arg.fSize ¬ box.max.f-box.min.f;
arg.firstBit ¬ Mod[((box.min.f-tile.box.min.f)-(qr.quotient*phase)), fSizeTile];
arg.firstLine ¬ qr.remainder;
UnsafeTile.Op[@arg];
};
};
arg.dstBpl ¬ map.bitsPerLine;
arg.srcBpl ¬ tile.bitsPerLine;
arg.sSizeTile ¬ sSizeTile;
arg.fSizeTile ¬ fSizeTile*bitsPerSample;
arg.srcWord ¬ tile.base.word;
arg.srcBit ¬ tile.base.bit;
arg.phase ¬ phase MOD fSizeTile;
arg.srcInvert ¬ IF function.srcFunc = complement THEN WORD.LAST ELSE 0;
arg.dstFunc ¬ function.dstFunc;
boxes[UnsafeFastTile];
RETURN;
};
IF phase=0 AND tile.bitsPerSample=bitsPerSample AND tile.bitsPerLine=bpw AND tile.base.bit=0 AND map.base.bit = 0 AND fSizeTile*bitsPerSample=bpw AND IsPowerOfTwo[sSizeTile] AND IsPowerOfTwo[bitsPerSample] THEN TRUSTED {
sTileMask: CARDINAL ~ sSizeTile-1; -- sSizeTile is a power of 2
sOffset: CARDINAL ~ Basics.BITAND[LOOPHOLE[-tile.box.min.s, CARDINAL], sTileMask];
lgBitsPerSample: [0..5] ~ Lg[bitsPerSample];
op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc];
FastTile: BoxAction ~ TRUSTED {
sMin: INTEGER ~ box.min.s;
fMin: INTEGER ~ box.min.f;
sMax: INTEGER ~ box.max.s;
fMax: INTEGER ~ box.max.f;
IF sMax > sMin AND fMax > fMin THEN {
m: RasterSampleMap ~ map;
bitsPerLine: CARDINAL ~ m.bitsPerLine;
sDst: CARDINAL ~ LOOPHOLE[sMin-m.box.min.s];
fDst: CARDINAL ~ LOOPHOLE[fMin-m.box.min.f];
lgBPS: [0..5] ~ lgBitsPerSample;
check: NAT ~ Basics.BITOR[Basics.BITOR[sDst, fDst], Basics.BITOR[LOOPHOLE[m.box.max.s-sMax], LOOPHOLE[m.box.max.f-fMax]]]; -- for bounds check
bitDst: CARD ~ sDst*bitsPerLine + Basics.BITLSHIFT[fDst, lgBPS] + m.base.bit;
fBitCount: CARD ~ Basics.BITLSHIFT[LOOPHOLE[fMax-fMin], lgBPS];
mod: CARDINAL [0..bpw) = bitDst MOD bpw;
yOffset: CARDINAL ~ Basics.BITAND[sOffset+LOOPHOLE[sMin, CARDINAL], sTileMask];
op[
dst: [word: m.base.word + (bitDst - mod) / bpu, bit: mod],
src: LOOPHOLE[tile.base.word],
dstBpl: bitsPerLine,
src0: yOffset,
sSizeTile: sSizeTile,
sSize: LOOPHOLE[sMax-sMin],
fSize: LOOPHOLE[fBitCount]
];
};
};
boxes[FastTile];
RETURN;
};
IF tile.bitsPerSample = map.bitsPerSample AND (
(fSizeTile*bitsPerSample >= MIN[fSizeSelf*bitsPerSample, smallWidth]) -- not worthwhile to build macro tile
) THEN {
GeneralTileBoxes[map: map, boxes: boxes, tile: tile, phase: phase, function: function];
RETURN;
};
} ENDCASE ENDCASE;
-- General case -- {
Make a "macro tile" that is composed of several copies of the original tile, to cover more area with each BITBLT
ns: CARDINAL ~ 1;
nf: CARDINAL ~ ((smallWidth+fSizeTile-1)/fSizeTile+bitsPerSample-1)/bitsPerSample;
macroTileSize: Vec ~ [s: ns*sSizeTile, f: nf*fSizeTile];
macroTile: RasterSampleMap ~ ObtainScratchMap[[tile.box.min, SFInline.Add[tile.box.min, macroTileSize]], bitsPerSample];
GeneralTile[map: macroTile, tile: tile, phase: phase];
GeneralTileBoxes[map: map, boxes: boxes, tile: macroTile, phase: ns*phase, function: function];
ReleaseScratchMap[macroTile];
};
};
BoxesFromBitmap: PUBLIC PROC [map: SampleMap, boxAction: BoxAction] ~ {
WITH map SELECT FROM
map: RasterSampleMap => {
IF map.bitsPerSample = 1 THEN TRUSTED {
sMin: INTEGER ~ map.box.min.s;
fMin: INTEGER ~ map.box.min.f;
fMax: INTEGER ~ map.box.max.f;
lineStart: BitAddress ¬ map.base;
IF map.box.min.f>=map.box.max.f THEN RETURN;
FOR sRel: NAT IN [0..map.box.max.s-map.box.min.s) DO
f: INTEGER ¬ map.box.min.f;
wordPtr: LONG POINTER TO CARDINAL ¬ lineStart.word;
wordsWorth: CARDINAL ¬ Basics.BITLSHIFT[wordPtr­, lineStart.bit];
remainingBitsInWord: INTEGER ¬ bpw-lineStart.bit;
ScanFor: UNSAFE PROC [bit: [0..1]] ~ UNCHECKED INLINE {
At exit, either f>=fMax or (high bit of wordsWorth) = bit.
skip: CARDINAL ~ IF bit = 0 THEN LAST[CARDINAL] ELSE 0;
hiBitMask: CARDINAL ~ LAST[CARDINAL]/2+1;
bitAtHigh: CARDINAL ~ IF bit = 0 THEN 0 ELSE hiBitMask;
WHILE f < fMax -- or EXIT below -- DO
IF remainingBitsInWord = bpw AND wordsWorth = skip
THEN { f ¬ f + remainingBitsInWord }
ELSE {
f ¬ f + remainingBitsInWord;
WHILE remainingBitsInWord > 0 AND Basics.BITAND[wordsWorth, hiBitMask] # bitAtHigh DO
remainingBitsInWord ¬ remainingBitsInWord-1;
wordsWorth ¬ wordsWorth*2;
ENDLOOP;
f ¬ f - remainingBitsInWord;
IF remainingBitsInWord > 0 THEN EXIT;
remainingBitsInWord ¬ bpw;
};
wordPtr ¬ wordPtr + SIZE[WORD];
IF f < fMax THEN wordsWorth ¬ wordPtr­;
ENDLOOP;
};
WHILE f < fMax DO
fStart: INTEGER;
ScanFor[1];
fStart ¬ f;
ScanFor[0];
f ¬ MIN[f, fMax];
IF f > fStart THEN boxAction[[
min: [s: sMin+sRel, f: fStart],
max: [s: sMin+sRel+1, f: f]
]];
ENDLOOP;
lineStart ¬ IndexBit[lineStart, map.bitsPerLine];
ENDLOOP;
RETURN;
};
};
ENDCASE => NULL;
-- hard case -- {
FOR s: NAT IN [map.box.min.s..map.box.max.s) DO
buffer: RasterSampleMap ~ ObtainScratchMap[box: [min: [s, map.box.min.f], max: [s+1, map.box.max.f]], bitsPerSample: 1];
Transfer[dst: buffer, src: map];
BoxesFromBitmap[buffer, boxAction];
ReleaseScratchMap[buffer];
ENDLOOP;
};
ERROR; -- Only need 1 bpp case for the Imager
};
Halftone: PUBLIC PROC [map: SampleMap, min: Vec, sampleBuffer, thresholdBuffer: SampleBuffer, function: Function] ~ TRUSTED {
Note; although the number 16 appears here, we don't rely on having 16 bits per word, only that the wordsize is a multiple of 16; we do, however, assume big-endian layout.
false: BOOL[FALSE..FALSE] ~ map.immutable;
unitsPerBuffer: NAT ~ 8*16;
bitsPerBuffer: NAT ~ unitsPerBuffer*16;
BitBuffer: TYPE ~ PACKED ARRAY [0..unitsPerBuffer) OF [0..65535];
bitBuffer: BitBuffer;
bitBufferPtr: LONG POINTER ~ @bitBuffer;
bitBufferMap: SampleMap ~ ObtainUnsafeDescriptor[size: [s: 1, f: bitsPerBuffer], bitsPerSample: 1, bitsPerLine: bitsPerBuffer, base: [word: LOOPHOLE[bitBufferPtr], bit: 0], ref: NIL, words: WORDS[BitBuffer]];
count: NAT ~ MIN[sampleBuffer.length, thresholdBuffer.length];
index: SF.Vec ¬ min;
residual: NAT ¬ count;
s: LONG POINTER TO ARRAY [0..16) OF WORD ¬ LOOPHOLE[InlinePointerToSamples[buffer: sampleBuffer, start: 0, count: count]];
b: LONG POINTER TO ARRAY [0..16) OF WORD ¬ LOOPHOLE[InlinePointerToSamples[buffer: thresholdBuffer, start: 0, count: count]];
Bit: UNSAFE PROC [j: NAT] RETURNS [WORD] ~ UNCHECKED INLINE {RETURN [Basics.BITRSHIFT[s[j]-b[j]-1, bpw-1]]};
WHILE residual # 0 DO
chunkSize: NAT ~ MIN[bitsPerBuffer, residual];
bitBufferIndex: NAT ¬ 0;
chunkResidual: NAT ¬ chunkSize;
WHILE chunkResidual >= 16 DO
bitBuffer[bitBufferIndex] ¬ ((((((((((((((Bit[0]*2+Bit[1])*2+Bit[2])*2+Bit[3])*2+Bit[4])*2+Bit[5])*2+Bit[6])*2+Bit[7])*2+Bit[8])*2+Bit[9])*2+Bit[10])*2+Bit[11])*2+Bit[12])*2+Bit[13])*2+Bit[14])*2+Bit[15];
bitBufferIndex ¬ bitBufferIndex + 1;
chunkResidual ¬ chunkResidual - 16;
s ¬ s + SIZE[ARRAY [0..16) OF CARDINAL];
b ¬ b + SIZE[ARRAY [0..16) OF CARDINAL];
ENDLOOP;
IF chunkResidual # 0 THEN {
w: CARDINAL ¬ 0;
IF chunkSize # residual THEN ERROR; -- must be last time through
FOR i: NAT IN [0..chunkResidual) DO w ¬ w*2 + Bit[i] ENDLOOP;
bitBuffer[bitBufferIndex] ¬ Basics.BITLSHIFT[w, Lo5[16-chunkResidual]];
};
BasicTransfer[dst: map, src: bitBufferMap, dstMin: index, size: [s: 1, f: chunkSize], function: function];
index.f ¬ index.f + chunkSize;
residual ¬ residual - chunkSize;
ENDLOOP;
ReleaseDescriptor[bitBufferMap];
};
Zeros: PROC [pointer: LONG POINTER, count: NAT] RETURNS [BOOL] ~ TRUSTED {
-- Checks for (pointer+i)^ = 0 for i IN [0..count) --
p: LONG POINTER TO ARRAY [0..8) OF WORD ¬ LOOPHOLE[pointer];
WHILE count >= 8 DO
IF Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[p[0], p[1]], p[2]], p[3]], p[4]], p[5]], p[6]], p[7]] # 0 THEN RETURN [FALSE];
p ¬ p + SIZE[ARRAY [0..8) OF WORD];
count ¬ count - 8;
ENDLOOP;
WHILE count > 0 DO
IF p[0] # 0 THEN RETURN [FALSE];
p ¬ p + SIZE[WORD];
count ¬ count - 1;
ENDLOOP;
RETURN [TRUE];
};
Equal: PUBLIC PROC [s1, s2: SampleMap] RETURNS [equal: BOOL ¬ TRUE] ~ {
BandAction: PROC [band: RasterSampleMap] ~ {
boxSize: NAT ~ SFInline.SizeS[s1.box];
bandSize: NAT ~ SFInline.SizeS[band.box];
bandWords: NAT ~ WordsForLines[bandSize, band.bitsPerLine];
Clear[band];
FOR s: INTEGER ¬ 0, s+bandSize WHILE s < boxSize DO
Transfer[dst: band, src: s1, delta: [s: -s, f: 0]];
Transfer[dst: band, src: s2, delta: [s: -s, f: 0], function: [xor, null]];
IF NOT Zeros[LOOPHOLE[band.base.word], bandWords] THEN { equal ¬ FALSE; EXIT };
ENDLOOP;
};
IF SFInline.Empty[s1.box] AND SFInline.Empty[s2.box] THEN NULL
ELSE IF s1.box # s2.box THEN equal ¬ FALSE
ELSE {
bandBitsPerSample: NAT ~ MAX[s1.bitsPerSample, s2.bitsPerSample];
bandLines: NAT ~ MIN[MAX[4096/SFInline.SizeF[s1.box], 1], SFInline.SizeS[s1.box]];
bandBox: Box ¬ s1.box;
bandBox.max.s ¬ bandBox.min.s + bandLines;
DoWithScratchMap[bandBox, bandBitsPerSample, BandAction];
};
};
IsAll: PUBLIC PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample ¬ 0]
RETURNS [equal: BOOL ¬ TRUE] ~ {
clipped: SampleMap ~ Clip[map, box];
IF SFInline.Nonempty[clipped.box] THEN {
BandAction: PROC [band: RasterSampleMap] ~ {
boxSize: NAT ~ SFInline.SizeS[clipped.box];
bandSize: NAT ~ SFInline.SizeS[band.box];
bandWords: NAT ~ WordsForLines[bandSize, band.bitsPerLine];
Clear[band];
FOR s: INTEGER ¬ 0, s+bandSize WHILE s < boxSize DO
Transfer[dst: band, src: clipped, delta: [s: -s, f: 0]];
IF value # 0 THEN Fill[map: band, box: maxBox, value: value, function: [xor, null]];
IF NOT Zeros[LOOPHOLE[band.base.word], bandWords] THEN { equal ¬ FALSE; EXIT };
ENDLOOP;
};
bandLines: NAT ~ MIN[MAX[4096/SFInline.SizeF[clipped.box], 1], SFInline.SizeS[clipped.box]];
bandBox: Box ¬ clipped.box;
bandBox.max.s ¬ bandBox.min.s + bandLines;
DoWithScratchMap[bandBox, clipped.bitsPerSample, BandAction];
};
ReleaseDescriptor[clipped];
};
Trim: PUBLIC PROC [map: SampleMap, box: Box, background: Sample] RETURNS [Box] ~ {
actualBox: Box ~ CleanBox[SF.Intersect[map.box, box]];
min: Vec ¬ actualBox.min;
max: Vec ¬ actualBox.max;
BandAction: PROC [band: RasterSampleMap] ~ {
bandWords: NAT ~ WordsForLines[band.box.max.s, band.bitsPerLine];
Clear[band];
WHILE min.s < max.s -- OR EXIT below -- DO
BasicTransfer[dst: band, src: map, srcMin: [s: max.s-1, f: min.f], size: band.box.max];
IF background#0 THEN Fill[map: band, box: band.box, value: background, function: [xor, null]];
IF Zeros[LOOPHOLE[band.base.word], bandWords] THEN max.s ¬ max.s-1 ELSE EXIT;
ENDLOOP;
WHILE min.s < max.s -- OR EXIT below -- DO
BasicTransfer[dst: band, src: map, srcMin: [s: min.s, f: min.f], size: band.box.max];
IF background#0 THEN Fill[map: band, box: band.box, value: background, function: [xor, null]];
IF Zeros[LOOPHOLE[band.base.word], bandWords] THEN min.s ¬ min.s+1 ELSE EXIT;
ENDLOOP;
};
DoWithScratchMap[box: [zeroVec, [s: 1, f: SFInline.SizeF[actualBox]]], bitsPerSample: map.bitsPerSample, action: BandAction];
IF min.s=max.s
THEN max.f ¬ min.f
ELSE {
FOR delta: NAT ¬ 16, delta/4 UNTIL delta=0 DO
WHILE min.f+delta <= max.f -- OR EXIT below -- DO
margin: Box ~ [min: [s: min.s, f: max.f-delta], max: max];
IF IsAll[map, margin, background] THEN max.f ¬ max.f-delta ELSE EXIT;
ENDLOOP;
WHILE min.f+delta <= max.f -- OR EXIT below -- DO
margin: Box ~ [min: min, max: [s: max.s, f: min.f+delta]];
IF IsAll[map, margin, background] THEN min.f ¬ min.f+delta ELSE EXIT;
ENDLOOP;
ENDLOOP;
};
RETURN [CleanBox[[min: min, max: max]]];
};
Testing aids
tb: Box ← [[0, 0], [100, 100]];
TestBoxes: BoxGenerator ~ {
boxAction[[[tb.min.s, (tb.min.f+tb.max.f)/2], [(tb.min.s+tb.max.s)/2, tb.max.f]]];
FOR i: NAT IN [0..tb.SizeS) DO
boxAction[[[tb.min.s+i, tb.min.f], [tb.min.s+i+1, MIN[tb.max.f, tb.min.f+i+1]]]]
ENDLOOP;
};
DumbBasicTransfer: PROC [dst: SampleMap, src: SampleMap,
dstMin: Vec ← zeroVec, srcMin: Vec ← zeroVec, size: Vec ← maxVec,
function: Function ← nullFunction] ~ {
FOR s: INTEGER IN [0..size.s) DO
FOR f: INTEGER IN [0..size.f) DO
p: Vec ~ [s: s, f: f];
value: Sample ~ Get[src, p.Add[srcMin]];
Put[dst, p.Add[dstMin], value, function];
ENDLOOP;
ENDLOOP;
};
DumbFill: PROC [map: SampleMap, box: Box ← maxBox,
value: Sample, function: Function ← nullFunction] ~ {
actualBox: Box ~ SF.Intersect[map.box, box];
FOR s: NAT IN [actualBox.min.s..actualBox.max.s) DO
FOR f: NAT IN [actualBox.min.f..actualBox.max.f) DO
Put[map, [s, f], value, function];
ENDLOOP;
ENDLOOP;
};
DumbGetTileSamples: PROC [tile: SampleMap, phase: NAT, initIndex: SF.Vec, buffer: SampleBuffer] ~ {
tempBox: Box ~ [min: initIndex, max: initIndex.Add[[1, buffer.length]]];
temp: SampleMap ~ NewSampleMap[box: tempBox, bitsPerSample: tile.bitsPerSample];
Tile[map: temp, tile: tile, phase: phase];
GetSamples[map: temp, initIndex: initIndex, buffer: buffer]
};
DumbTile: PROC [map: SampleMap, box: Box ← maxBox, tile: SampleMap, phase: NAT ← 0, function: Function ← nullFunction] ~ {
actualBox: Box ~ SF.Intersect[map.box, box];
tileSize: Vec ~ SF.Size[tile.box];
s0: INT ~ tile.box.min.s;
f0: INT ~ tile.box.min.f;
FOR s: NAT IN [actualBox.min.s..actualBox.max.s) DO
sTile: INT ~ s-s0;
qr: QR ~ DivMod[sTile, tileSize.s];
fTile: INT ~ actualBox.min.f-(f0+QMul[qr.quotient, phase]);
fOffset: NAT ← Mod[fTile, tileSize.f];
FOR f: NAT IN [actualBox.min.f..actualBox.max.f) DO
value: Sample ~ Get[tile, [s0 + qr.remainder, f0 + fOffset]];
Put[map, [s, f], value, function];
fOffset ← fOffset+1;
IF fOffset=tileSize.f THEN fOffset ← 0;
ENDLOOP;
ENDLOOP;
};
TestObjectRep: TYPE ~ ARRAY [0..1024) OF Sample;
TestObjectGet: ObjectGetProc ~ {
data: REF TestObjectRep ~ NARROW[self.data];
size: Vec ~ InlineSize[self];
i0: NAT ~ NAT[initIndex.s-self.box.min.s]*NAT[size.f]+NAT[initIndex.f-self.box.min.f];
FOR i: NAT IN [0..count) DO
buffer[start+i] ← data^[i0+i];
ENDLOOP;
};
TestObjectPut: ObjectPutProc ~ {
data: REF TestObjectRep ~ NARROW[self.data];
size: Vec ~ InlineSize[self];
i0: NAT ~ NAT[initIndex.s-self.box.min.s]*NAT[size.f]+NAT[initIndex.f-self.box.min.f];
FOR i: NAT IN [0..count) DO
data^[i0+i] ← ApplyFunction[function, data^[i0+i], buffer[start+i]];
ENDLOOP;
};
TestObjectMake: PROC [box: Box, bitsPerSample: NAT] RETURNS [SampleMap] ~ {
elements: [0..1024] ~ CARD[SF.SizeS[box]]*SF.SizeF[box];
RETURN [NewObjectSampleMap[box, bitsPerSample, TestObjectGet, TestObjectPut, NEW[TestObjectRep]]]
};
END.