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];
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 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]]]
};