IISampleImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, December 12, 1986 2:26:47 pm PST
Doug Wyatt, March 6, 1986 4:09:53 pm PST
DIRECTORY
Basics USING [BITAND, BITSHIFT, bitsPerWord, BoundsCheck, CARD, DoubleShiftRight, logBitsPerWord, LongDivMod, LongMult, LongNumber, NonNegative, RawWords, ShortNumber],
CountedVM USING [Handle, SimpleAllocate],
FunctionCache USING [Cache, CacheInfo, GetInfo, GlobalCache, SetLimits],
IISample USING [BitsPerSample, EdgeAction, Function, maxBox, maxCount, maxSample, maxVec, nullFunction, RawAction, RawArray, rawArraySize, RawDescriptor, RawGenerator, RawSamples, Sample, SampleBuffer, SampleBufferRep, SampleMap, SampleMapRep, zeroVec],
PrincOps USING [BBptr, BBTableSpace, BitAddress, BitBltFlags, BitBltTable],
PrincOpsUtils USING [AlignedBBTable, BITBLT, LongCopy, LongZero],
Scaled USING [Value, PLUS, zero],
SF USING [Add, Box, BoxAction, BoxGenerator, Displace, Empty, Intersect, Neg, Nonempty, Size, SizeF, SizeS, Sub, Vec],
Terminal USING [FrameBuffer],
VM USING [CantAllocate, wordsPerPage];
IISampleImpl: CEDAR MONITOR
IMPORTS Basics, CountedVM, FunctionCache, PrincOpsUtils, Scaled, SF, VM
EXPORTS IISample
~ BEGIN OPEN IISample;
Box: TYPE ~ SF.Box;
BoxAction: TYPE ~ SF.BoxAction;
BoxGenerator: TYPE ~ SF.BoxGenerator;
Vec: TYPE ~ SF.Vec;
MultipleReleaseOfScratch: PUBLIC ERROR ~ CODE;
NewSamples: PUBLIC PROC [length: NAT, scratch: SampleBuffer ← NIL] RETURNS [SampleBuffer] ~ {
new: SampleBuffer ← scratch;
IF new=NIL OR new.maxLength<length THEN new ← NEW[SampleBufferRep[length]];
new.length ← length;
RETURN[new];
};
nScratchSampleBuffers: NAT ~ 6;
scratchSampleBuffers: ARRAY [0..nScratchSampleBuffers) OF SampleBuffer ← ALL[NIL];
ObtainScratchSamples: PUBLIC ENTRY PROC [length: NAT] RETURNS [SampleBuffer] ~ {
FOR i: NAT IN [0..nScratchSampleBuffers) DO
buf: SampleBuffer ~ scratchSampleBuffers[i];
IF buf # NIL AND buf.maxLength >= length THEN {
scratchSampleBuffers[i] ← NIL;
buf.length ← length;
RETURN [buf];
};
ENDLOOP;
RETURN [NEW[SampleBufferRep[length] ← [length: length, samples:]]]
};
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
[LONG POINTER TO RawSamples] ~ 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
[LONG POINTER TO RawSamples] ~ {
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: LONG POINTER TO RawSamples ~ 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: LONG POINTER TO RawSamples ~ InlinePointerToSamples[buffer, start, size];
pointer[0] ← value;
PrincOpsUtils.LongCopy[from: pointer, nwords: size-1, to: pointer+1];
};
};
CopySamples: PUBLIC PROC [dst, src: SampleBuffer, dstStart, srcStart, count: NAT] ~ {
size: NAT ~ MIN[NAT[dst.length-dstStart], NAT[src.length-dstStart], count];
IF size > 0 THEN TRUSTED {
dstPointer: LONG POINTER TO RawSamples ~ InlinePointerToSamples[dst, dstStart, size];
srcPointer: LONG POINTER TO RawSamples ~ InlinePointerToSamples[src, srcStart, size];
PrincOpsUtils.LongCopy[from: srcPointer, nwords: size, to: dstPointer];
};
};
ClearSamples: PUBLIC PROC [buffer: SampleBuffer, start, count: NAT] ~ {
size: NAT ~ MIN[buffer.length-start, count];
IF size>0 THEN TRUSTED {
pointer: LONG POINTER TO RawSamples ~ InlinePointerToSamples[buffer, start, size];
PrincOpsUtils.LongZero[where: pointer, nwords: size];
};
};
bitsPerWord: NAT ~ Basics.bitsPerWord;
logBitsPerWord: NAT ~ Basics.logBitsPerWord;
nullBitBltTable: PrincOps.BitBltTable ~ [
dst: [word: NIL, bit: 0], dstBpl: 0,
src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: []
];
IsPowerOfTwo: PROC [c: CARDINAL] RETURNS [BOOL] ~ INLINE {
RETURN[Basics.BITAND[c, c-1]=0];
};
WordsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE {
RETURN[Basics.DoubleShiftRight[[lc[bitCount+(bitsPerWord-1)]], logBitsPerWord].lc]
};
Lg: PROC [a: NAT] RETURNS [lg: NAT ← 0] ~ {
b: CARDINAL ← 1;
UNTIL b >= a DO
lg ← lg + 1;
b ← b*2;
ENDLOOP;
};
IndexBit: PROC [addr: PrincOps.BitAddress, offset: CARD]
RETURNS [PrincOps.BitAddress] ~ TRUSTED INLINE {
Addr: TYPE ~ MACHINE DEPENDENT RECORD [pointer: LONG POINTER, offset: CARDINAL];
This record is to designed to avoid the need for partial-word access to the bit offset field, since the BITBLT format has carefully made this field word-aligned, and PrincOps.BitAddress has just as carefully made it hard to take advantage of this fact.
pointer: LONG POINTER ~ LOOPHOLE[addr, Addr].pointer;
index: Basics.LongNumber ~ [lc[LOOPHOLE[addr, Addr].offset+offset]];
RETURN[LOOPHOLE[Addr[
pointer: pointer+Basics.DoubleShiftRight[index, logBitsPerWord].lc,
offset: index.lo MOD bitsPerWord
]]];
};
BitsForSamples: PUBLIC PROC [fSize: NAT, bitsPerSample: NAT] RETURNS [NAT] ~ {
Returns required number of bits, rounded up to a whole number of words.
RETURN[Basics.BITAND[Basics.LongMult[fSize, bitsPerSample]+(bitsPerWord-1), WORD.LAST-(bitsPerWord-1)]];
};
WordsForLines: PUBLIC PROC [sSize: NAT, bitsPerLine: NAT] RETURNS [INT] ~ {
IF (bitsPerLine MOD bitsPerWord)=0
THEN RETURN[Basics.LongMult[sSize, bitsPerLine/bitsPerWord]]
ELSE RETURN[WordsForBits[Basics.LongMult[sSize, bitsPerLine]]];
};
WordsForMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine: NAT ← 0] RETURNS [INT] ~ {
IF bitsPerLine=0 THEN bitsPerLine ← BitsForSamples[size.f, bitsPerSample];
RETURN[WordsForLines[size.s, bitsPerLine]];
};
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 THEN box.max.s ← box.min.s;
IF box.min.f > box.max.f THEN box.max.f ← box.min.f;
RETURN [box];
};
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]]
};
UnsafeNewSampleMap: PUBLIC UNSAFE PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: NAT, base: PrincOps.BitAddress, ref: REF, words: INT, scratchDescriptor: SampleMap ← NIL] RETURNS [SampleMap] ~ {
size: Vec ~ SF.Size[box];
dataBitsPerLine: INT ~ Basics.LongMult[NAT[size.f], bitsPerSample];
fillBits: INT ~ Basics.NonNegative[bitsPerLine-dataBitsPerLine]; -- check sufficient bitsPerLine
totalBits: INT ~ INT[base.bit]+Basics.LongMult[NAT[size.s], bitsPerLine]-fillBits;
totalWords: INT ~ WordsForBits[totalBits];
spareWords: INT ~ Basics.NonNegative[words-totalWords]; -- check sufficient words
s: SampleMap ← scratchDescriptor;
IF s=NIL THEN s ← NEW[SampleMapRep];
s^ ← [box: CleanBox[box], bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine, base: base, ref: ref];
RETURN[s];
};
WordSeqRep: TYPE ~ RECORD [SEQUENCE length: NAT OF WORD];
maxWordSeqWords: INTVM.wordsPerPage-VM.wordsPerPage/10-SIZE[WordSeqRep[0]];
NewSampleMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: NAT ← 0] RETURNS [SampleMap] ~ {
size: Vec ~ SF.Size[box];
actualBitsPerLine: NAT ~ SELECT bitsPerLine FROM
0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine;
words: INT ~ WordsForLines[size.s, actualBitsPerLine];
IF words <= maxWordSeqWords
THEN {
wordSeq: REF WordSeqRep ~ NEW[WordSeqRep[words]];
TRUSTED { RETURN[UnsafeNewSampleMap[
box: box, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine,
base: [word: @wordSeq[0], bit: 0], ref: wordSeq, words: wordSeq.length
]] };
}
ELSE RETURN[MapFromVM[vm: Allocate[words],
box: box, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine]];
};
Allocate: PROC [nWords: CARD] RETURNS [vm: CountedVM.Handle ← NIL] ~ {
nWords ← MAX[nWords, 256];
vm ← CountedVM.SimpleAllocate[nWords ! VM.CantAllocate => CONTINUE];
IF vm = NIL THEN {
Try flushing the global function cache to get some space back.
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
info: FunctionCache.CacheInfo ~ FunctionCache.GetInfo[cache];
FunctionCache.SetLimits[cache, 0, 0];
FunctionCache.SetLimits[x: cache, maxEntries: info.maxEntries, maxTotalSize: info.maxTotalSize];
ClearScratchMaps[];
vm ← CountedVM.SimpleAllocate[nWords];
};
};
MapFromVM: PUBLIC PROC [vm: CountedVM.Handle,
box: Box, bitsPerSample: BitsPerSample ← 1, bitsPerLine: NAT ← 0
] RETURNS [SampleMap] ~ {
size: Vec ~ SF.Size[box];
actualBitsPerLine: NAT ~ SELECT bitsPerLine FROM
0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine;
TRUSTED { RETURN[UnsafeNewSampleMap[
box: box, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine,
base: [word: vm.pointer, bit: 0], ref: vm, words: vm.words
]] };
};
MapFromFrameBuffer: PUBLIC PROC [frameBuffer: Terminal.FrameBuffer]
RETURNS [SampleMap] ~ {
bitsPerLine: NAT ~ Basics.LongMult[frameBuffer.wordsPerLine, bitsPerWord];
vm: CountedVM.Handle ~ frameBuffer.vm;
base: LONG POINTER ~ frameBuffer.base;
baseOffset: INT ~ LOOPHOLE[base, INT]-LOOPHOLE[vm.pointer, INT];
words: INT ~ vm.words-Basics.NonNegative[baseOffset];
TRUSTED { RETURN[UnsafeNewSampleMap[
box: [min: [0, 0], max: [s: frameBuffer.height, f: frameBuffer.width]],
bitsPerSample: frameBuffer.bitsPerPixel, bitsPerLine: bitsPerLine,
base: [word: base, bit: 0], ref: vm, words: words
]] };
};
nScratchDescs: NAT ~ 5;
scratchDescsTop: [0..nScratchDescs] ← 0;
scratchDescs: ARRAY [0..nScratchDescs) OF SampleMap ← ALL[NIL];
NewDescriptor: ENTRY PROC [map: SampleMap ← NIL] RETURNS [new: SampleMap ← NIL] ~ {
IF map # NIL THEN {
IF scratchDescsTop = 0
THEN new ← NEW[SampleMapRep]
ELSE {
scratchDescsTop ← scratchDescsTop - 1;
new ← scratchDescs[scratchDescsTop];
scratchDescs[scratchDescsTop] ← NIL;
};
IF map # NIL THEN new^ ← map^;
};
};
ReleaseDescriptor: PUBLIC ENTRY PROC [map: SampleMap] ~ {
FOR i: NAT IN [0..scratchDescsTop) DO
IF map = scratchDescs[i] THEN RETURN WITH ERROR MultipleReleaseOfScratch;
ENDLOOP;
IF map # NIL AND scratchDescsTop < nScratchDescs THEN {
map.base.word ← NIL;
map.ref ← NIL;
scratchDescs[scratchDescsTop] ← map;
scratchDescsTop ← scratchDescsTop + 1;
};
};
InlineClip: UNSAFE PROC [map: SampleMap, box: Box] ~ INLINE {
newBox: Box ~ SF.Intersect[map.box, box];
skipBits: CARD ~
Basics.LongMult[NAT[newBox.min.s-map.box.min.s], map.bitsPerLine] +
Basics.LongMult[NAT[newBox.min.f-map.box.min.f], map.bitsPerSample];
map.box ← CleanBox[newBox];
map.base ← IndexBit[map.base, skipBits];
};
InlineShift: UNSAFE PROC [map: SampleMap, delta: Vec] ~ INLINE {
map.box.min ← SF.Add[map.box.min, delta];
map.box.max ← SF.Add[map.box.max, delta];
};
ObtainUnsafeDescriptor: PUBLIC UNSAFE PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine: NAT, base: PrincOps.BitAddress, ref: REF, words: INT, rawMin, delta: Vec ← zeroVec] RETURNS [SampleMap] ~ UNCHECKED {
new: SampleMap ~ UnsafeNewSampleMap[box: [[0, 0], SF.Add[rawMin, size]], bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine, base: base, ref: ref, words: words, scratchDescriptor: NewDescriptor[]];
InlineShift[new, SF.Neg[rawMin]];
InlineClip[new, [[0, 0], size]];
InlineShift[new, delta];
RETURN [new];
};
ReIndex: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [SampleMap] ~ TRUSTED {
new: SampleMap ← NewDescriptor[map];
InlineShift[new, delta];
InlineClip[new, box];
RETURN [new]
};
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: SF.Neg[map.box.min], box: maxBox]];
};
Copy: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [SampleMap] ~ {
temp: SampleMap ← ReIndex[map, delta, box];
new: SampleMap ~ NewSampleMap[box: temp.box, bitsPerSample: map.bitsPerSample];
Transfer[dst: new, src: temp];
ReleaseDescriptor[temp];
RETURN[new];
};
nScratchMaps: NAT ~ 5;
scratchMaps: ARRAY [0..nScratchMaps) OF SampleMap ← ALL[NIL];
ObtainScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample]
RETURNS [SampleMap] ~ {
size: Vec ~ SF.Size[box];
bitsPerLine: NAT ~ BitsForSamples[fSize: size.f, bitsPerSample: bitsPerSample];
nWords: INT ~ WordsForLines[sSize: size.s, bitsPerLine: bitsPerLine];
TryObtainScratch: ENTRY PROC RETURNS [SampleMap] ~ TRUSTED {
FOR i: NAT IN [0..nScratchMaps) DO
s: SampleMap ← scratchMaps[i];
IF s # NIL THEN WITH s.ref SELECT FROM
vm: CountedVM.Handle => {
IF vm.words >= nWords THEN {
scratchMaps[i] ← NIL;
s.box ← box;
s.bitsPerSample ← bitsPerSample;
s.bitsPerLine ← bitsPerLine;
s.base ← [word: vm.pointer, bit: 0];
RETURN [s]
};
};
ENDCASE => NULL;
ENDLOOP;
RETURN [NIL]
};
s: SampleMap ← TryObtainScratch[];
IF s = NIL THEN {
vm: CountedVM.Handle ~ Allocate[WordsForMap[size, bitsPerSample]];
s ← MapFromVM[vm: vm, box: box, bitsPerSample: bitsPerSample];
};
RETURN [s]
};
ReleaseScratchMap: PUBLIC ENTRY PROC [map: SampleMap] ~ {
r: SampleMap ← map;
slot: NAT ← 0;
*** test for excessive size here ? ***
IF map = NIL THEN RETURN;
WITH map.ref SELECT FROM vm: CountedVM.Handle => NULL ENDCASE => RETURN;
FOR i: NAT IN [0..nScratchMaps) DO
t: SampleMap ← scratchMaps[i];
IF t = NIL THEN slot ← i
ELSE IF map = t OR map.ref = t.ref THEN RETURN WITH ERROR MultipleReleaseOfScratch;
ENDLOOP;
scratchMaps[slot] ← map;
};
ClearScratchMaps: ENTRY PROC ~ {
FOR i: NAT IN [0..nScratchMaps) DO
scratchMaps[i] ← NIL;
ENDLOOP;
};
DoWithScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample,
action: PROC [SampleMap]] ~ {
scratch: SampleMap ~ ObtainScratchMap[box, bitsPerSample];
action[scratch ! UNWIND => ReleaseScratchMap[scratch]];
ReleaseScratchMap[scratch];
};
Clear: PUBLIC PROC [map: SampleMap] ~ TRUSTED {
size: Vec ~ InlineSize[map];
bitsPerLine: NAT ~ map.bitsPerLine;
pointer: LONG POINTER ← map.base.word;
fillBitsPerLine: NAT ~ bitsPerLine - map.bitsPerSample*size.f;
IF fillBitsPerLine < bitsPerWord THEN {
-- Contiguous, except perhaps for some padding. --
nBits: CARD ~ Basics.LongMult[bitsPerLine, size.s]+map.base.bit-fillBitsPerLine;
nWords: CARD ← WordsForBits[nBits];
WHILE nWords > 32768 DO
PrincOpsUtils.LongZero[pointer, 32768];
pointer ← pointer + 32768;
nWords ← nWords - 32768;
ENDLOOP;
PrincOpsUtils.LongZero[pointer, nWords];
}
ELSE {
-- Discontiguous, so do pieces (could BITBLT here; which is cheaper?) --
dataBitsPlus: NAT ~ map.bitsPerSample*size.f + (bitsPerWord-1);
bit: CARDINAL ← map.base.bit;
FOR i: NAT IN [0..size.s) DO
PrincOpsUtils.LongZero[where: pointer, nwords: (bit + dataBitsPlus)/bitsPerWord];
bit ← bit + bitsPerLine;
pointer ← pointer + bit/bitsPerWord;
bit ← bit MOD bitsPerWord;
ENDLOOP;
};
};
Get: PUBLIC PROC [map: SampleMap, index: Vec] RETURNS [Sample] ~ TRUSTED {
size: Vec ~ InlineSize[map];
v: Vec ~ SF.Sub[index, map.box.min];
bitsPerSample: NAT ~ map.bitsPerSample;
bitIndex: CARD ~
Basics.LongMult[Basics.BoundsCheck[v.s, size.s], map.bitsPerLine] +
Basics.LongMult[Basics.BoundsCheck[v.f, size.f], bitsPerSample];
bitAddress: PrincOps.BitAddress ~ IndexBit[map.base, bitIndex];
IF bitsPerSample=1 THEN TRUSTED {
pointer: LONG POINTER TO Basics.ShortNumber ~ bitAddress.word;
RETURN[LOOPHOLE[pointer.bits[bitAddress.bit]]];
}
ELSE TRUSTED {
pointer: LONG POINTER TO Basics.RawWords ~ bitAddress.word;
shiftAmt: INTEGER ~ bitAddress.bit + bitsPerSample - bitsPerWord;
mask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1;
shiftedBits: WORD ~ IF shiftAmt <= 0
THEN Basics.BITSHIFT[pointer[0], shiftAmt]
ELSE Basics.BITSHIFT[pointer[0], shiftAmt] + Basics.BITSHIFT[pointer[1], shiftAmt-16];
RETURN[Basics.BITAND[shiftedBits, mask]];
};
};
Put: PUBLIC PROC [map: SampleMap, index: Vec, value: Sample,
function: Function ← nullFunction] ~ {
size: Vec ~ InlineSize[map];
v: Vec ~ SF.Sub[index, map.box.min];
bitsPerSample: NAT ~ map.bitsPerSample;
bitIndex: CARD ~
Basics.LongMult[Basics.BoundsCheck[v.s, size.s], map.bitsPerLine] +
Basics.LongMult[Basics.BoundsCheck[v.f, size.f], bitsPerSample];
bitAddress: PrincOps.BitAddress ~ IndexBit[map.base, bitIndex];
IF bitsPerSample=1 AND function=nullFunction THEN TRUSTED {
pointer: LONG POINTER TO Basics.ShortNumber ~ bitAddress.word;
pointer.bits[bitAddress.bit] ← LOOPHOLE[value MOD 2];
}
ELSE TRUSTED {
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bb^ ← nullBitBltTable;
bb.dst ← bitAddress;
bb.src.word ← @value;
bb.src.bit ← bitsPerWord-bitsPerSample;
bb.height ← 1;
bb.width ← bitsPerSample;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
PrincOpsUtils.BITBLT[bb];
};
};
GetSamples: PUBLIC PROC [map: SampleMap, initIndex: Vec ← zeroVec, delta: Vec ← [s: 0, f: 1],
buffer: SampleBuffer, start: NAT ← 0, count: NAT ← maxCount] ~ {
size: NAT ~ MIN[buffer.length-start, count];
boxSize: Vec ~ InlineSize[map];
v0: Vec ~ SF.Sub[initIndex, map.box.min];
IF size#0 THEN TRUSTED {
bitsPerLine: NAT ~ map.bitsPerLine;
bitsPerSample: NAT ~ map.bitsPerSample;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bbTableSpace: PrincOps.BBTableSpace;
bufferPointer: LONG POINTER ~ InlinePointerToSamples[buffer, start, size];
srcBitIndex: CARD
Basics.LongMult[Basics.BoundsCheck[v0.s, boxSize.s], bitsPerLine] +
Basics.LongMult[Basics.BoundsCheck[v0.f, boxSize.f], bitsPerSample];
srcBitDelta: NAT ~
Basics.LongMult[delta.s, bitsPerLine] +
Basics.LongMult[delta.f, bitsPerSample];
srcBitSkipDelta: NAT ← srcBitDelta;
lgSkip: NAT ← 0;
skipMinusOne: NAT ← 0;
IF delta.s#0 THEN []�sics.BoundsCheck[v0.s+Basics.LongMult[size-1, delta.s], boxSize.s];
IF delta.f#0 THEN []�sics.BoundsCheck[v0.f+Basics.LongMult[size-1, delta.f], boxSize.f];
WHILE srcBitSkipDelta<=NAT.LAST/2 AND CARDINAL[srcBitSkipDelta] MOD bitsPerWord#0 DO
srcBitSkipDelta ← srcBitSkipDelta*2; lgSkip ← lgSkip+1;
ENDLOOP;
skipMinusOne ← Basics.BITSHIFT[1, lgSkip]-1;
PrincOpsUtils.LongZero[where: bufferPointer, nwords: size];
bb^ ← nullBitBltTable;
bb.dst.bit ← bitsPerWord-bitsPerSample;
bb.dstBpl ← Basics.BITSHIFT[bitsPerWord, lgSkip];
bb.srcDesc ← [srcBpl[srcBitSkipDelta]];
bb.width ← bitsPerSample;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
FOR i: NAT IN [0..Basics.BITSHIFT[1, lgSkip]) DO
bb.src ← IndexBit[map.base, srcBitIndex];
bb.height ← Basics.BITSHIFT[size-i+skipMinusOne, -lgSkip];
bb.dst.word ← bufferPointer+i;
PrincOpsUtils.BITBLT[bb];
srcBitIndex ← srcBitIndex + srcBitDelta;
ENDLOOP;
};
};
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] ~ {
size: NAT ~ MIN[buffer.length-start, count];
boxSize: Vec ~ InlineSize[map];
v0: Vec ~ SF.Sub[initIndex, map.box.min];
IF size # 0 THEN TRUSTED {
bitsPerLine: NAT ~ map.bitsPerLine;
bitsPerSample: NAT ~ map.bitsPerSample;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bbTableSpace: PrincOps.BBTableSpace;
bufferPointer: LONG POINTER ~ InlinePointerToSamples[buffer, start, size];
dstBitIndex: CARD
Basics.LongMult[Basics.BoundsCheck[v0.s, boxSize.s], bitsPerLine] +
Basics.LongMult[Basics.BoundsCheck[v0.f, boxSize.f], bitsPerSample];
dstBitDelta: NAT
Basics.LongMult[delta.s, bitsPerLine] +
Basics.LongMult[delta.f, bitsPerSample];
dstBitSkipDelta: NAT ← dstBitDelta;
lgSkip: NAT ← 0;
skipMinusOne: NAT ← 0;
IF delta.s#0 THEN []�sics.BoundsCheck[v0.s+Basics.LongMult[size-1, delta.s], boxSize.s];
IF delta.f#0 THEN []�sics.BoundsCheck[v0.f+Basics.LongMult[size-1, delta.f], boxSize.f];
WHILE dstBitSkipDelta<=NAT.LAST/2 AND CARDINAL[dstBitSkipDelta] MOD bitsPerWord#0 DO
dstBitSkipDelta ← dstBitSkipDelta*2; lgSkip ← lgSkip+1;
ENDLOOP;
skipMinusOne ← Basics.BITSHIFT[1, lgSkip]-1;
bb^ ← nullBitBltTable;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
bb.dstBpl ← dstBitSkipDelta;
bb.src.bit ← bitsPerWord-bitsPerSample;
bb.srcDesc ← [srcBpl[Basics.BITSHIFT[bitsPerWord, lgSkip]]];
bb.width ← bitsPerSample;
FOR i: NAT IN [0..Basics.BITSHIFT[1, lgSkip]) DO
bb.dst ← IndexBit[map.base, dstBitIndex];
bb.height ← Basics.BITSHIFT[size-i+skipMinusOne, -lgSkip];
bb.src.word ← bufferPointer+i;
PrincOpsUtils.BITBLT[bb];
dstBitIndex ← dstBitIndex + dstBitDelta;
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;
};
Check: PROC [value: INTEGER, bound: NAT] RETURNS [INTEGER] ~
INLINE { RETURN [LOOPHOLE[Basics.BoundsCheck[LOOPHOLE[value], CARDINAL[bound]+1]]] };
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] ~ {
bitsPerSample: NAT ~ dst.bitsPerSample;
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];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
dstBitIndex: CARD ~
Basics.LongMult[sDst0, dst.bitsPerLine] +
Basics.LongMult[fDst0, bitsPerSample];
srcBitIndex: CARD ~
Basics.LongMult[sSrc0, src.bitsPerLine] +
Basics.LongMult[fSrc0, bitsPerSample];
bb^ ← nullBitBltTable;
bb.dst ← IndexBit[dst.base, dstBitIndex];
bb.dstBpl ← dst.bitsPerLine;
bb.src ← IndexBit[src.base, srcBitIndex];
bb.srcDesc ← [srcBpl[src.bitsPerLine]];
bb.width ← fSize*bitsPerSample;
bb.height ← sSize;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
PrincOpsUtils.BITBLT[bb];
}
ELSE BufferTransfer[dst, src, dstMin, srcMin, size, function];
};
RawTransfer: PUBLIC PROC [dst: SampleMap, rawGenerator: RawGenerator, function: Function ← nullFunction] ~ TRUSTED {
bitsPerSample: [1..1] ~ dst.bitsPerSample;
bbTableSpace: PrincOps.BBTableSpace;
bbPtr: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
fastTransfer: RawAction ~ TRUSTED {
sCheck: NAT ~ dst.box.max.s-box.max.s;
fCheck: NAT ~ dst.box.max.f-box.max.f;
dstBitIndex: CARD ~ Basics.LongMult[NAT[box.min.s-dst.box.min.s], dst.bitsPerLine] + NAT[box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1)
bb: PrincOps.BBptr ~ bbPtr;
bb.dst ← IndexBit[dst.base, dstBitIndex];
bb.src ← base;
bb.srcDesc ← [srcBpl[bitsPerLine]];
bb.width ← SF.SizeF[box]; -- (know bitsPerSample = 1)
bb.height ← SF.SizeS[box];
PrincOpsUtils.BITBLT[bb];
};
bbPtr^ ← nullBitBltTable;
bbPtr.dstBpl ← dst.bitsPerLine;
bbPtr.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bbPtr.flags.srcFunc ← function.srcFunc;
bbPtr.flags.dstFunc ← function.dstFunc;
rawGenerator[fastTransfer];
};
MultipleTransfer: PUBLIC PROC [dst: SampleMap, n: [0..rawArraySize], a: POINTER TO RawArray, function: Function ← nullFunction] ~ TRUSTED {
bitsPerSample: [1..1] ~ dst.bitsPerSample;
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
r: POINTER TO RawDescriptor ← @(a[0]);
bb^ ← nullBitBltTable;
bb.dstBpl ← dst.bitsPerLine;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
FOR i: NAT IN [0..n) DO
sCheck: NAT ~ dst.box.max.s-r.box.max.s;
fCheck: NAT ~ dst.box.max.f-r.box.max.f;
dstBitIndex: CARD ~ Basics.LongMult[NAT[r.box.min.s-dst.box.min.s], dst.bitsPerLine] + NAT[r.box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1)
bb.dst ← IndexBit[dst.base, dstBitIndex];
bb.src.word ← r.basePointer;
bb.srcDesc ← [srcBpl[r.bitsPerLine]];
bb.width ← SF.SizeF[r.box]; -- (know bitsPerSample = 1)
bb.height ← SF.SizeS[r.box];
PrincOpsUtils.BITBLT[bb];
r ← r + SIZE[RawDescriptor];
ENDLOOP;
};
Transfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ← zeroVec, function: Function ← nullFunction] ~ {
box: Box ~ SF.Intersect[dst.box, SF.Displace[src.box, delta]];
IF SF.Nonempty[box] THEN BasicTransfer[dst: dst, src: src, dstMin: box.min, srcMin: SF.Sub[box.min, delta], size: SF.Size[box], function: function];
};
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;
};
TransferBoxes: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ← zeroVec,
boxes: BoxGenerator, function: Function ← nullFunction] ~ {
bitsPerSample: NAT ~ dst.bitsPerSample;
IF src.bitsPerSample=bitsPerSample THEN TRUSTED {
dstBox: Box ~ dst.box;
srcBox: Box ~ SF.Displace[src.box, delta];
bounds: Box ~ SF.Intersect[dstBox, srcBox];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
fastTransfer: BoxAction ~ TRUSTED {
IF box.max.s > box.min.s AND box.max.f > box.min.f 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 ~
Basics.LongMult[LOOPHOLE[box.min.s-dstBox.min.s], dst.bitsPerLine] +
Basics.LongMult[LOOPHOLE[box.min.f-dstBox.min.f], bitsPerSample];
srcBitIndex: CARD ~
Basics.LongMult[LOOPHOLE[box.min.s-srcBox.min.s], src.bitsPerLine] +
Basics.LongMult[LOOPHOLE[box.min.f-srcBox.min.f], bitsPerSample];
bb.dst ← IndexBit[dst.base, dstBitIndex];
bb.src ← IndexBit[src.base, srcBitIndex];
bb.width ← LOOPHOLE[box.max.f-box.min.f, CARDINAL]*bitsPerSample;
bb.height ← LOOPHOLE[box.max.s-box.min.s];
PrincOpsUtils.BITBLT[bb];
};
};
bb^ ← nullBitBltTable;
bb.dstBpl ← dst.bitsPerLine;
bb.srcDesc ← [srcBpl[src.bitsPerLine]];
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
boxes[fastTransfer];
}
ELSE {
slowTransfer: BoxAction ~ {
dstMin: Vec ~ box.min;
srcMin: Vec ~ box.min.Sub[delta];
BufferTransfer[dst, src, dstMin, srcMin, box.max.Sub[box.min], function];
};
boxes[slowTransfer];
};
};
Move: PUBLIC PROC [map: SampleMap,
dstMin: Vec ← zeroVec, srcMin: Vec ← zeroVec, size: Vec ← maxVec,
function: Function ← nullFunction] ~ TRUSTED {
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];
bitsPerSample: NAT ~ map.bitsPerSample;
bitsPerLine: NAT ~ map.bitsPerLine;
dstBitIndex: CARD
Basics.LongMult[dstMin.s, bitsPerLine] +
Basics.LongMult[dstMin.f, bitsPerSample];
srcBitIndex: CARD
Basics.LongMult[srcMin.s, bitsPerLine] +
Basics.LongMult[srcMin.f, bitsPerSample];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
IF sSize=0 OR fSize = 0 THEN RETURN;
bb^ ← nullBitBltTable;
bb.dstBpl ← bitsPerLine;
bb.srcDesc ← [srcBpl[bitsPerLine]];
bb.height ← sSize;
bb.width ← bitsPerSample * fSize;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
IF (srcMin.f+fSize) > dstMin.f AND (dstMin.f+fSize) > srcMin.f AND
(srcMin.s+sSize) > dstMin.s AND (dstMin.s+sSize) > srcMin.s THEN {
bb.flags.disjoint ← FALSE; -- the rectangles overlap
IF srcMin.s=dstMin.s THEN bb.flags.disjointItems ← FALSE; -- so do the items
IF dstMin.s>srcMin.s OR (dstMin.s=srcMin.s AND dstMin.f>srcMin.f) THEN {
delta: CARD ~ Basics.LongMult[sSize-1, bitsPerLine];
bb.flags.direction ← backward; -- reverse direction
bb.srcDesc.srcBpl ← bb.dstBpl ← -bitsPerLine;
dstBitIndex ← dstBitIndex + delta;
srcBitIndex ← srcBitIndex + delta;
};
};
bb.dst ← IndexBit[map.base, dstBitIndex];
bb.src ← IndexBit[map.base, srcBitIndex];
PrincOpsUtils.BITBLT[bb];
};
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;
};
replicator: ARRAY [0..4] OF Sample ~ [0FFFFH, 05555H, 01111H, 00101H, 00001H];
Fill: PUBLIC PROC [map: SampleMap, box: Box, value: Sample, function: Function ← nullFunction] ~ {
bitsPerSample: NAT ~ map.bitsPerSample;
bitsPerLine: NAT ~ map.bitsPerLine;
IF (bitsPerWord MOD bitsPerSample)=0 THEN TRUSTED {
actualBox: Box ~ SF.Intersect[map.box, box];
sSize: CARDINAL ~ SF.SizeS[actualBox];
fSize: CARDINAL ~ SF.SizeF[actualBox];
dstBitIndex: CARD ~
Basics.LongMult[actualBox.min.s-map.box.min.s, bitsPerLine] +
Basics.LongMult[actualBox.min.f-map.box.min.f, bitsPerSample];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bitMask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1;
replicatedPixel: WORD ← Basics.BITAND[value, bitMask] * replicator[Lg[bitsPerSample]];
IF function = [null, complement] THEN {
-- Bug in Dorado BITBLT microcode (as of December 4, 1985) --
replicatedPixel ← WORD.LAST-replicatedPixel;
function ← [null, null];
};
bb^ ← nullBitBltTable;
bb.dst ← IndexBit[map.base, dstBitIndex];
bb.dstBpl ← bitsPerLine;
bb.src.word ← @replicatedPixel;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0];
bb.width ← fSize*bitsPerSample;
bb.height ← sSize;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: TRUE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
PrincOpsUtils.BITBLT[bb];
}
ELSE DumbFill[map: map, box: box, value: value, function: function];
};
FillBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator,
value: Sample ← maxSample, function: Function ← nullFunction] ~ {
bitsPerSample: NAT ~ map.bitsPerSample;
IF (bitsPerWord MOD bitsPerSample)=0 THEN TRUSTED {
lgBitsPerSample: NAT ~ Lg[bitsPerSample];
bitsPerLine: NAT ~ map.bitsPerLine;
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
dstBox: Box ~ map.box;
fastFill: BoxAction ~ TRUSTED {
IF box.max.s > box.min.s AND box.max.f > box.min.f THEN {
sSpaceHi: NAT ~ dstBox.max.s-box.max.s; -- for bounds check
fSpaceHi: NAT ~ dstBox.max.f-box.max.f;
dstBitIndex: CARD ~
Basics.LongMult[NAT[box.min.s-dstBox.min.s], bitsPerLine] +
Basics.BITSHIFT[NAT[box.min.f-dstBox.min.f], lgBitsPerSample];
bb.dst ← IndexBit[map.base, dstBitIndex];
bb.width ← Basics.BITSHIFT[LOOPHOLE[box.max.f-box.min.f], lgBitsPerSample];
bb.height ← LOOPHOLE[box.max.s-box.min.s];
PrincOpsUtils.BITBLT[bb];
};
};
bitMask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1;
replicatedPixel: WORD ← Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample];
IF function = [null, complement] THEN {
-- Bug in Dorado BITBLT microcode (as of December 4, 1985) --
replicatedPixel ← WORD.LAST-replicatedPixel;
function ← [null, null];
};
bb^ ← nullBitBltTable;
bb.dstBpl ← bitsPerLine;
bb.src.word ← @replicatedPixel;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0];
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: TRUE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
boxes[fastFill];
}
ELSE {
slowFill: BoxAction ~ { DumbFill[map, box, value, function] };
boxes[slowFill];
};
};
RegionFill: PUBLIC PROC [dst: SampleMap, edgeGenerator: PROC [EdgeAction], value: Sample, function: Function] ~ TRUSTED {
Edge: TYPE ~ RECORD [sMin: INTEGER, sMax: INTEGER, f0: Scaled.Value, df: Scaled.Value];
edges: ARRAY [0..1] OF Edge ← ALL[[0, 0, [0, 0], [0, 0]]];
bitsPerSample: NAT ~ dst.bitsPerSample;
zero: [0..0] ~ bitsPerWord MOD bitsPerSample; -- not implemented for strange bps . . .
lgBitsPerSample: NAT ~ Lg[bitsPerSample]; -- so we can shift rather than multiply
bbTableSpace: PrincOps.BBTableSpace;
bbPtr: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
edgeAction: SAFE PROC [which: [0..1], sMin: INTEGER, sCount: NAT, f0, df: Scaled.Value] ~ TRUSTED {
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 ← [];
bb: PrincOps.BBptr ~ bbPtr;
Addr: TYPE ~ MACHINE DEPENDENT RECORD [pointer: LONG POINTER, offset: CARDINAL];
line: Addr ← LOOPHOLE[IndexBit[dst.base, Basics.LongMult[NAT[s-box.min.s], dst.bitsPerLine]]];
addrp: POINTER TO Addr ~ LOOPHOLE[@(bb^.dst)];
bitsPerLine: NAT ~ dst.bitsPerLine;
IF e[0].df = Scaled.zero AND e[1].df = Scaled.zero AND s >= box.min.s
THEN {
This is just a rectangle - do it with one BITBLT
f0: INTEGER ← e[0].f0.integerPart;
f1: INTEGER ← e[1].f0.integerPart;
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.offset+LOOPHOLE[f0-box.min.f, CARDINAL];
addrp^.pointer ← offset/bitsPerWord+line.pointer;
addrp^.offset ← offset MOD bitsPerWord;
bb.height ← sMax-s;
bb.width ← Basics.BITSHIFT[LOOPHOLE[f1-f0], lgBitsPerSample];
PrincOpsUtils.BITBLT[bb];
};
s ← sMax;
}
ELSE {
bb.height ← 1;
WHILE s < sMax DO
f0: INTEGER ← e[0].f0.integerPart;
f1: INTEGER ← e[1].f0.integerPart;
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.offset+LOOPHOLE[f0-box.min.f, CARDINAL];
addrp^.pointer ← offset/bitsPerWord+line.pointer;
addrp^.offset ← offset MOD bitsPerWord;
bb.width ← Basics.BITSHIFT[LOOPHOLE[f1-f0], lgBitsPerSample];
PrincOpsUtils.BITBLT[bb];
};
e[0].f0 ← e[0].f0.PLUS[e[0].df];
e[1].f0 ← e[1].f0.PLUS[e[1].df];
{offset: CARDINAL ~ line.offset+bitsPerLine;
line.pointer ← line.pointer + offset/bitsPerWord;
line.offset ← offset MOD bitsPerWord;
};
s ← s + 1;
ENDLOOP;
};
e[0].sMin ← e[1].sMin ← s;
bb.height ← 0;
};
edges ← e;
};
bitMask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1;
replicatedPixel: WORD ← Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample];
IF function = [null, complement] THEN {
-- Bug in Dorado BITBLT microcode (as of December 4, 1985) --
replicatedPixel ← WORD.LAST-replicatedPixel;
function ← [null, null];
};
bbPtr^ ← nullBitBltTable;
bbPtr.dstBpl ← dst.bitsPerLine;
bbPtr.src.word ← @replicatedPixel;
bbPtr.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0];
bbPtr.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: TRUE];
bbPtr.flags.srcFunc ← function.srcFunc;
bbPtr.flags.dstFunc ← function.dstFunc;
bbPtr.height ← 0;
edgeGenerator[edgeAction];
};
Zeros: PROC [pointer: LONG POINTER, count: NAT] RETURNS [BOOL] ~ TRUSTED {
-- Checks for (pointer+i)^ = 0 for i IN [0..count) --
chomp: NAT ~ 8;
scratch: ARRAY [0..chomp) OF WORDALL[0];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bb^ ← nullBitBltTable;
bb.dstBpl ← 0;
bb.srcDesc ← [srcBpl[chomp*bitsPerWord]];
bb.height ← count/chomp;
bb.width ← chomp*bitsPerWord;
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, dstFunc: or];
bb.dst.word ← @scratch;
bb.src.word ← pointer;
IF bb.height # 0 THEN PrincOpsUtils.BITBLT[bb];
bb.src.word ← pointer + Basics.LongMult[bb.height, chomp];
bb.height ← 1;
bb.width ← count MOD chomp;
IF bb.width # 0 THEN PrincOpsUtils.BITBLT[bb];
FOR i: NAT IN [0..MIN[chomp, count]) DO IF scratch[i]#0 THEN RETURN [FALSE] ENDLOOP;
RETURN [TRUE];
};
Equal: PUBLIC PROC [s1, s2: SampleMap] RETURNS [equal: BOOLTRUE] ~ {
bandAction: PROC [band: SampleMap] ~ {
boxSize: NAT ~ SF.SizeS[s1.box];
bandSize: NAT ~ SF.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[band.base.word, bandWords] THEN { equal ← FALSE; EXIT };
ENDLOOP;
};
IF SF.Empty[s1.box] AND SF.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/SF.SizeF[s1.box], 1], SF.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: BOOLTRUE] ~ {
clipped: SampleMap ~ Clip[map, box];
bandAction: PROC [band: SampleMap] ~ {
boxSize: NAT ~ SF.SizeS[clipped.box];
bandSize: NAT ~ SF.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[band.base.word, bandWords] THEN { equal ← FALSE; EXIT };
ENDLOOP;
};
IF SF.Empty[clipped.box]THEN NULL
ELSE {
bandLines: NAT ~ MIN[MAX[4096/SF.SizeF[clipped.box], 1], SF.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 ~ SF.Intersect[map.box, box];
min: Vec ← actualBox.min; max: Vec ← actualBox.max;
bandAction: PROC [band: SampleMap] ~ {
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[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[band.base.word, bandWords] THEN min.s ← min.s+1 ELSE EXIT;
ENDLOOP;
};
DoWithScratchMap[box: [zeroVec, [s: 1, f: SF.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 [[min: min, max: max]];
};
TileFromStipple: PUBLIC PROC [stipple: WORD, bitsPerSample: BitsPerSample ← 1, value0: Sample ← 0, value1: Sample ← Sample.LAST, scratch: SampleMap ← NIL] RETURNS [SampleMap] ~ {
easy: BOOL ~ (16 MOD (bitsPerSample*4) = 0) OR (bitsPerSample=8 AND Basics.BITAND[stipple, 3333H] = Basics.BITAND[stipple/4, 3333H]);
size: Vec ~ IF easy THEN [s: 4, f: 16/bitsPerSample] ELSE [s: 8, f: 16];
temp: SampleMap ~ ObtainScratchMap[[[0, 0], [4, 4]], bitsPerSample];
tile: SampleMap ~ 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;
Tile[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
IF d#1 THEN {
nn: Basics.LongNumber ← [li[n]];
qq: Basics.LongNumber ← [lc[0]];
IF nn.li < 0 THEN {nn.highbits ← nn.highbits + d; -- qq.highbits ← CARDINAL.LAST --};
[quotient: qq.lowbits, remainder: qr.remainder] ← Basics.LongDivMod[nn.lc, d];
-- quotient ← qq.li; --
qr.quotient ← LOOPHOLE[qq.lowbits];
}
ELSE RETURN [[quotient: n, remainder: 0]];
};
Mod: PROC [n: INT, d: NAT] RETURNS [remainder: NAT] ~ {
Number-theoretic: 0 <= remainder < d
IF d#1 THEN {
nn: Basics.LongNumber ← [li[n]];
WHILE nn.li < 0 DO nn.highbits ← nn.highbits + d ENDLOOP;
WHILE nn.highbits >= d DO nn.highbits ← nn.highbits - d ENDLOOP;
RETURN [Basics.LongDivMod[nn.lc, d].remainder];
}
ELSE RETURN [remainder: 0];
};
QMul: PROC [a: INTEGER, b: CARDINAL] RETURNS [INT] ~ INLINE {
IF a >= 0 THEN RETURN [LOOPHOLE[Basics.LongMult[a, b]]]
ELSE RETURN [-LOOPHOLE[Basics.LongMult[-a, b], INT]]
};
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;
};
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];
};
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]
};
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 ~ SF.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 ~ SF.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];
};
dumb: BOOLFALSE;
GeneralTileBoxes: PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ← 0, function: Function ← nullFunction] ~ {
dumbGeneralTile: BoxAction ~ {
DumbTile[map, box, tile, phase, function];
};
generalTile: BoxAction ~ {
s0: INT ~ tile.box.min.s;
f0: INT ~ tile.box.min.f;
actualBox: Box ~ SF.Intersect[map.box, box];
sOffset: INT ~ actualBox.min.s-s0;
tileSize: Vec ~ SF.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: SF.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;
};
IF phase>=SF.SizeF[tile.box] THEN phase ← phase MOD SF.SizeF[tile.box];
boxes[IF dumb THEN dumbGeneralTile ELSE generalTile];
};
Tile: PUBLIC PROC [map: SampleMap, box: Box ← maxBox, tile: SampleMap, phase: NAT ← 0, function: Function ← nullFunction] ~ {
boxes: BoxGenerator ~ { boxAction[box] };
TileBoxes[map, boxes, tile, phase, function];
};
smallWidth: NAT ← 128;
TileBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ← 0, function: Function ← nullFunction] ~ {
bitsPerSample: NAT ~ map.bitsPerSample;
sSizeTile: NAT ~ tile.box.max.s-tile.box.min.s;
fSizeTile: NAT ~ tile.box.max.f-tile.box.min.f;
selfBox: Box ~ map.box;
sSizeSelf: NAT ~ selfBox.max.s-selfBox.min.s;
fSizeSelf: NAT ~ selfBox.max.f-selfBox.min.f;
IF phase=0 AND tile.bitsPerSample=bitsPerSample AND tile.bitsPerLine=bitsPerWord AND tile.base.bit=0 AND fSizeTile*bitsPerSample=bitsPerWord AND sSizeTile IN [1..16] AND IsPowerOfTwo[sSizeTile] AND function#[null, complement] THEN TRUSTED {
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
sTileMask: CARDINAL ~ sSizeTile-1; -- sSizeTile is a power of 2
fTileMask: CARDINAL ~ fSizeTile-1; -- So is fSizeTile (it divides the wordsize)
sOffset: CARDINAL ~ Basics.BITAND[LOOPHOLE[-tile.box.min.s], sTileMask];
fOffset: CARDINAL ~ Basics.BITAND[LOOPHOLE[-tile.box.min.f], fTileMask];
lgBitsPerSample: NAT ~ Lg[bitsPerSample];
fastTile: BoxAction ~ TRUSTED {
IF box.max.s > box.min.s AND box.max.f > box.min.f THEN {
sDst: NAT ~ box.min.s - selfBox.min.s;
fDst: NAT ~ box.min.f - selfBox.min.f;
sSpace: NAT ~ selfBox.max.s - box.max.s; -- for bounds check
fSpace: NAT ~ selfBox.max.f - box.max.f;
dstBitIndex: CARD ~
Basics.LongMult[sDst, map.bitsPerLine] +
Basics.BITSHIFT[fDst, lgBitsPerSample];
yOffset: CARDINAL ~ Basics.BITAND[sOffset+LOOPHOLE[box.min.s, CARDINAL], sTileMask];
bb.dst ← IndexBit[map.base, dstBitIndex];
bb.src.word ← tile.base.word+yOffset;
bb.src.bit ← Basics.BITSHIFT[fOffset+LOOPHOLE[box.min.f, CARDINAL], lgBitsPerSample] MOD bitsPerWord;
bb.srcDesc.gray.yOffset ← yOffset;
bb.height ← box.max.s - box.min.s;
bb.width ← Basics.BITSHIFT[box.max.f-box.min.f, lgBitsPerSample];
PrincOpsUtils.BITBLT[bb];
};
};
bb^ ← nullBitBltTable;
bb.dstBpl ← map.bitsPerLine;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: sSizeTile-1];
bb.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: TRUE];
bb.flags.srcFunc ← function.srcFunc;
bb.flags.dstFunc ← function.dstFunc;
boxes[fastTile];
}
ELSE IF tile.bitsPerSample#bitsPerSample
OR fSizeTile*bitsPerSample < MIN[smallWidth, fSizeSelf*bitsPerSample] THEN {
ns: CARDINAL ~ 1;
nf: CARDINAL ~ ((smallWidth+fSizeTile-1)/fSizeTile+bitsPerSample-1)/bitsPerSample;
macroTileSize: Vec ~ [s: ns*sSizeTile, f: nf*fSizeTile];
macroTileAction: PROC [macroTile: SampleMap] ~ {
GeneralTile[map: macroTile, tile: tile, phase: phase];
GeneralTileBoxes[map: map, boxes: boxes, tile: macroTile, phase: ns*phase, function: function];
};
DoWithScratchMap[[tile.box.min, SF.Add[tile.box.min, macroTileSize]], bitsPerSample, macroTileAction];
}
ELSE GeneralTileBoxes[map, boxes, tile, phase, function];
};
BoxesFromBitmap: PUBLIC PROC [map: SampleMap, boxAction: BoxAction] ~ {
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: PrincOps.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.BITSHIFT[wordPtr^, lineStart.bit];
remainingBitsInWord: INTEGER ← bitsPerWord-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 = bitsPerWord 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 ← bitsPerWord;
};
wordPtr ← wordPtr + 1;
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;
}
ELSE 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.
unitsPerBuffer: NAT ~ 8*16;
bitsPerBuffer: NAT ~ unitsPerBuffer*16;
BitBuffer: TYPE ~ PACKED ARRAY [0..unitsPerBuffer) OF [0..65535];
bitBuffer: BitBuffer;
bitBufferMap: SampleMap ~ ObtainUnsafeDescriptor[size: [s: 1, f: bitsPerBuffer], bitsPerSample: 1, bitsPerLine: bitsPerBuffer, base: [word: @bitBuffer, bit: 0], ref: NIL, words: SIZE[BitBuffer]];
count: NAT ~ MIN[sampleBuffer.length, thresholdBuffer.length];
index: SF.Vec ← min;
residual: NAT ← count;
s: LONG POINTER TO ARRAY [0..16) OF CARDINALLOOPHOLE[InlinePointerToSamples[buffer: sampleBuffer, start: 0, count: count]];
b: LONG POINTER TO ARRAY [0..16) OF CARDINALLOOPHOLE[InlinePointerToSamples[buffer: thresholdBuffer, start: 0, count: count]];
Bit: UNSAFE PROC [j: NAT] RETURNS [CARDINAL] ~ UNCHECKED INLINE {RETURN [Basics.BITSHIFT[s[j]-b[j]-1, 1-Basics.bitsPerWord]]};
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.BITSHIFT[w, 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];
};
END.