OldImagerMasksImpl.mesa
Michael Plass, October 17, 1983 3:44 pm
Edited by Doug Wyatt, November 28, 1983 2:10 pm
DIRECTORY
Basics USING [bitsPerWord],
OldImagerMasks,
OldImagerMasksPrivate USING [Reader, Representation],
ImagerPixelMaps USING [Clear, Clip, Create, Fill, Function, GetPixel, IsAll, PixelMapRep, Tile, Transfer, TransferTile, Trim, Window],
ImagerScanConverter USING [BoundingBox, ConvertToPixels, ConvertToRuns, NumberOfRuns],
UnsafeStorage USING [GetSystemUZone];
OldImagerMasksImpl: CEDAR PROGRAM
IMPORTS UnsafeStorage, ImagerPixelMaps, ImagerScanConverter
EXPORTS OldImagerMasks, OldImagerMasksPrivate
= BEGIN OPEN OldImagerMasks;
maxBlockLength: NAT ~ 150;
indexSize: NAT ~ 200;
bitsPerWord: NAT ~ Basics.bitsPerWord;
TempRunRep: TYPE ~ RECORD [
newLineRepeatCount: NAT, -- zero if the run does not start a new scanline
fMin: INTEGER,
fSize: INTEGER
];
TempRunGroupRep: TYPE ~ ARRAY [0..maxBlockLength) OF TempRunRep;
IndexArrayRep: TYPE ~ ARRAY [0..indexSize) OF LONG POINTER TO TempRunGroupRep;
uz: UNCOUNTED ZONE ← SysZone[];
SysZone: PROC RETURNS[UNCOUNTED ZONE] ~ TRUSTED {RETURN[UnsafeStorage.GetSystemUZone[]]};
RunsOutOfOrder: PUBLIC ERROR ~ CODE;
Create: PUBLIC PROC [
runs: PROC[ -- Create calls this back
run: PROC[sMin, fMin: INTEGER, fSize: NAT], -- client calls this from inside runs.
repeat: PROC[timesToRepeatScanline: NAT] -- client calls this to repeat a scanline
]
] RETURNS [mask: Mask] ~ TRUSTED {
indexArrayLength: CARDINAL ← 0;
indexArrayEnd: CARDINAL ← 0;
indexArray: LONG POINTER TO IndexArrayRep;
tempLength: CARDINAL ← maxBlockLength;
totalLength: INT;
Release: UNSAFE PROC = {
IF indexArray # NIL THEN {
FOR i: CARDINAL IN [0..indexArrayLength) DO
IF indexArray[i] # NIL THEN uz.FREE[@(indexArray[i])];
ENDLOOP;
uz.FREE[@indexArray];
};
};
minf: INTEGERLAST[INTEGER];
maxf: INTEGERFIRST[INTEGER];
mins: INTEGERLAST[INTEGER];
lasts: INTEGERLAST[INTEGER];
lastf: INTEGERFIRST[INTEGER];
TRUSTED {ENABLE UNWIND => Release[];
temp: LONG POINTER TO TempRunGroupRep ← NIL; -- = indexArray[indexArrayEnd - 1]
curLineStartIndex: CARDINAL ← 0;
curLineStartBlockNumber: CARDINAL ← 0;
Extend: UNSAFE PROC = {
indexArrayEnd ← indexArrayEnd + 1;
IF indexArrayEnd > indexArrayLength THEN {
indexArray[indexArrayLength] ← NIL;
indexArrayLength ← indexArrayLength + 1;
indexArray[indexArrayLength - 1] ← uz.NEW[TempRunGroupRep];
};
temp ← indexArray[indexArrayEnd - 1];
tempLength ← 0;
};
StartNewLine: UNSAFE PROC = {
pi: CARDINAL ← curLineStartIndex;
pb: CARDINAL ← curLineStartBlockNumber;
ci: CARDINAL ← tempLength;
cb: CARDINAL ← indexArrayEnd - 1;
cBlock: LONG POINTER TO TempRunGroupRep ← temp;
pBlock: LONG POINTER TO TempRunGroupRep ← indexArray[pb];
lastf ← FIRST[INTEGER];
DO -- Terminate by RETURN if duplicate scanline, by EXIT if not.
cr: TempRunRep;
IF pi = 0 THEN {
IF pb = 0 THEN EXIT;
pb ← pb - 1;
pBlock ← indexArray[pb];
pi ← maxBlockLength;
};
pi ← pi - 1;
IF ci = 0 THEN {
IF cb = 0 THEN ERROR;
cb ← cb - 1;
cBlock ← indexArray[cb];
ci ← maxBlockLength;
};
ci ← ci - 1;
IF (cr ← cBlock[ci]).newLineRepeatCount # 0 THEN {
pr: TempRunRep ← pBlock[pi];
IF cr.fMin = pr.fMin AND cr.fSize = pr.fSize AND pr.newLineRepeatCount # 0 THEN {
IF ci # curLineStartIndex THEN ERROR;
pBlock[pi].newLineRepeatCount ← pr.newLineRepeatCount + cr.newLineRepeatCount;
tempLength ← curLineStartIndex;
indexArrayEnd ← curLineStartBlockNumber + 1;
temp ← indexArray[curLineStartBlockNumber];
RETURN;
}
ELSE EXIT
};
IF cr # pBlock[pi] THEN EXIT;
ENDLOOP;
curLineStartIndex ← tempLength;
curLineStartBlockNumber ← indexArrayEnd - 1;
};
Run: SAFE PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
newLine: BOOLEANFALSE;
IF sMin < lasts THEN {IF lasts = LAST[INTEGER] THEN {mins ← lasts ← sMin; newLine ← TRUE} ELSE ERROR RunsOutOfOrder};
WHILE sMin # lasts DO
IF newLine THEN {
IF tempLength = maxBlockLength THEN Extend[];
StartNewLine[];
temp[tempLength] ← [sMin-lasts, 0, 0];
tempLength ← tempLength + 1;
lasts ← sMin;
}
ELSE {newLine ← TRUE; lasts ← lasts + 1};
ENDLOOP;
IF tempLength = maxBlockLength THEN Extend[];
IF newLine THEN {
StartNewLine[];
temp[tempLength] ← [1, fMin, fSize];
}
ELSE temp[tempLength] ← [0, fMin, fSize];
IF lastf > fMin THEN ERROR RunsOutOfOrder;
lastf ← INT[fMin] + fSize;
tempLength ← tempLength + 1;
minf ← MIN[minf, fMin];
maxf ← MAX[maxf, fMin + fSize];
};
Repeat: SAFE PROC[timesToRepeatScanline: NAT] ~ TRUSTED {
pBlock: LONG POINTER TO TempRunGroupRep ← indexArray[curLineStartBlockNumber];
oldRepeat: NAT ← pBlock[curLineStartIndex].newLineRepeatCount;
IF oldRepeat = 0 THEN ERROR;
IF pBlock = temp AND curLineStartIndex = tempLength THEN ERROR;
pBlock[curLineStartIndex].newLineRepeatCount ← oldRepeat + timesToRepeatScanline;
lasts ← lasts + timesToRepeatScanline;
};
indexArray ← uz.NEW[IndexArrayRep];
runs[Run, Repeat];
StartNewLine[]; -- merges duplicates in case last call was to Repeat
totalLength ← INT[indexArrayEnd]*maxBlockLength + tempLength - maxBlockLength;
IF lasts = LAST[INTEGER] THEN {mask ← [0, 0, 0, 0, NIL]}
ELSE IF indexArrayEnd = 1 AND tempLength = 1 THEN {
mask.sMin ← mins;
mask.fMin ← minf;
mask.sSize ← lasts + 1 - mins;
mask.fSize ← maxf - minf;
mask.refRep ← NIL;
}
ELSE {
runsMaskRef: REF RunsRep ← NEW[RunsRep[indexArrayEnd]];
mask.sMin ← mins;
mask.fMin ← minf;
mask.sSize ← lasts + 1 - mins;
mask.fSize ← maxf - minf;
mask.refRep ← runsMaskRef;
FOR i: CARDINAL DECREASING IN [0..indexArrayEnd) DO
new: RunBlock ← NEW[RunBlockRep[tempLength]];
runsMaskRef[i] ← new;
temp ← indexArray[i];
FOR j: [0..maxBlockLength) DECREASING IN [0..tempLength) DO
tempj: TempRunRep ← temp[j];
new[j] ← [
newLineRepeatCount: tempj.newLineRepeatCount,
fMin: IF tempj.fSize = 0 THEN 0 ELSE tempj.fMin - minf,
fSize: tempj.fSize
];
ENDLOOP;
tempLength ← maxBlockLength;
ENDLOOP;
};
};
TRUSTED {Release[]};
};
MaskFromPixelMap: PUBLIC PROC [pixelMap: PixelMap, minValue, maxValue: CARDINAL] RETURNS [Mask] ~ {
IF minValue > maxValue THEN RETURN [[0, 0, 0, 0, NIL]];
IF pixelMap.refRep.lgBitsPerPixel = 0 THEN {
function: ImagerPixelMaps.Function;
copy: PixelMap;
SELECT TRUE FROM
maxValue = 0 => function ← [null, complement];
minValue = 1 => function ← [null, null];
minValue > 1 => RETURN [[0, 0, 0, 0, NIL]];
minValue = 0 AND maxValue > 0 => RETURN [Box[pixelMap.Window]];
ENDCASE => ERROR;
copy ← ImagerPixelMaps.Create[0, pixelMap.Window];
copy.Transfer[pixelMap, function];
RETURN [[pixelMap.sOrigin+pixelMap.sMin, pixelMap.fOrigin+pixelMap.fMin, pixelMap.sSize, pixelMap.fSize, pixelMap.refRep]];
}
ELSE {
window: DeviceRectangle ← pixelMap.Window;
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
fMax: INTEGER ← window.fMin + window.fSize;
FOR s: INTEGER IN [window.sMin..window.sMin+window.sSize) DO
f, fMin: INTEGER ← window.fMin;
WHILE f < fMax DO
WHILE f < fMax AND NOT (pixelMap.GetPixel[s, f] IN [minValue..maxValue]) DO
f ← f + 1;
ENDLOOP;
IF f = fMax THEN EXIT;
fMin ← f;
WHILE f < fMax AND pixelMap.GetPixel[s, f] IN [minValue..maxValue] DO
f ← f + 1;
ENDLOOP;
IF f > fMin THEN run[s, fMin, f-fMin];
ENDLOOP;
ENDLOOP;
};
RETURN [Create[Runs]];
};
};
MaskFromDevicePath: PUBLIC PROC [devicePath: DevicePath, parityFill: BOOLEAN] RETURNS [Mask] ~ {
window: DeviceRectangle ← ImagerScanConverter.BoundingBox[devicePath];
runCount: INT ← devicePath.NumberOfRuns;
IF runCount * 16 > INT[window.sSize]*window.fSize THEN {
pixelMap: PixelMap ← ImagerPixelMaps.Create[0, window];
ImagerPixelMaps.Clear[pixelMap];
devicePath.ConvertToPixels[pixelMap, 1, parityFill];
pixelMap ← pixelMap.Trim[0];
IF pixelMap.fOrigin # 0 OR pixelMap.sOrigin # 0 THEN
RETURN [MaskFromPixelMap[pixelMap, 1, 1]]
ELSE IF pixelMap.IsAll[1] THEN RETURN[Box[pixelMap.Window]]
ELSE RETURN [[pixelMap.sMin, pixelMap.fMin, pixelMap.sSize, pixelMap.fSize, pixelMap.refRep]];
}
ELSE {
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]]
~ {devicePath.ConvertToRuns[run, window, parityFill]};
RETURN [Create[Runs]];
};
};
Box: PUBLIC PROC [r: DeviceRectangle] RETURNS [Mask] ~ {
RETURN[[r.sMin, r.fMin, r.sSize, r.fSize, NIL]]
};
Representation: TYPE ~ OldImagerMasksPrivate.Representation;
Reader: TYPE ~ OldImagerMasksPrivate.Reader;
SetReader: PUBLIC UNSAFE PROC [reader: POINTER TO Reader, mask: Mask] ~ UNCHECKED {
reader.s ← mask.sMin;
reader.index ← 0;
reader.fTranslate ← mask.fMin;
reader.done ← FALSE;
reader.repeatCount ← 1;
reader.firstRunOnLine ← TRUE;
reader.refRep ← mask.refRep;
IF mask.sSize = 0 THEN {
reader.done ← TRUE;
reader.fMin ← reader.fMax ← 0;
reader.s ← LAST[INTEGER];
RETURN
};
WITH reader.refRep SELECT FROM
b: REF ImagerPixelMaps.PixelMapRep => {
IF b.lgBitsPerPixel # 0 THEN ERROR;
reader.representation ← bitmap;
reader.currentWord ← 0;
reader.validBitCount ← 0;
reader.nextWordPtr ← reader.currentLinePtr ← b.pointer;
reader.fCurrent ← 0;
reader.fSize ← MIN[mask.fSize, b.rast*bitsPerWord];
reader.sMax ← MIN[mask.sSize, b.lines] + mask.sMin;
reader.wordsPerLine ← b.rast;
Advance[reader];
reader.firstRunOnLine ← TRUE;
};
r: REF RunsRep => {
fMin, fSize: NAT;
reader.representation ← runs;
reader.blockDirectory ← r;
reader.blockDirectoryIndex ← 0;
reader.runBlock ← r[0];
[newLineRepeatCount: reader.repeatCount, fMin: fMin, fSize: fSize] ← reader.runBlock[0];
reader.lineStartBlockDirectoryIndex ← 0;
reader.lineStartIndex ← 0;
reader.fMax ← (reader.fMin ← fMin + reader.fTranslate) + fSize;
};
ENDCASE => {
reader.representation ← box;
reader.repeatCount ← mask.sSize;
reader.fMin ← mask.fMin;
reader.fMax ← reader.fMin + mask.fSize;
};
};
Advance: PUBLIC UNSAFE PROC [r: POINTER TO Reader] ~ UNCHECKED {
IF r.done THEN RETURN;
SELECT r.representation FROM
bitmap => {
olds: INTEGER ← r.s;
WHILE r.currentWord = 0 DO
WHILE r.currentWord = 0 AND r.fCurrent < r.fSize DO
Count clear words in this scanline.
r.fCurrent ← r.fCurrent + r.validBitCount;
r.currentWord ← r.nextWordPtr^;
r.nextWordPtr ← r.nextWordPtr + 1;
r.validBitCount ← bitsPerWord;
ENDLOOP;
IF r.fCurrent >= r.fSize THEN {
Advance to next scanline
IF (r.s ← r.s + 1) >= r.sMax THEN {
r.s ← LAST[INTEGER];
r.fMin ← r.fMax ← 0;
r.firstRunOnLine ← r.done ← TRUE;
RETURN;
};
r.fCurrent ← 0;
r.nextWordPtr ← r.currentLinePtr ← r.currentLinePtr + r.wordsPerLine;
r.currentWord ← r.nextWordPtr^;
r.nextWordPtr ← r.nextWordPtr + 1;
r.validBitCount ← bitsPerWord;
};
ENDLOOP;
IF r.currentWord = -1 THEN {
IF r.validBitCount # bitsPerWord THEN ERROR;
r.fMin ← r.fTranslate + r.fCurrent;
r.fCurrent ← r.fCurrent + bitsPerWord;
r.validBitCount ← 0;
}
ELSE {
r.fCurrent ← r.fCurrent+r.validBitCount;
WHILE r.currentWord > 0 DO
Count clear bits.
r.currentWord ← 2*r.currentWord;
r.validBitCount ← r.validBitCount-1;
ENDLOOP;
r.fMin ← r.fTranslate + r.fCurrent - r.validBitCount;
WHILE r.currentWord < 0 DO
Count one bits.
r.currentWord ← 2*r.currentWord;
r.validBitCount ← r.validBitCount-1;
ENDLOOP;
r.fCurrent ← r.fCurrent-r.validBitCount;
};
WHILE r.fCurrent < r.fSize AND r.validBitCount = 0 DO
Count all-ones words.
r.currentWord ← r.nextWordPtr^;
r.nextWordPtr ← r.nextWordPtr + 1;
IF r.currentWord = -1 THEN r.fCurrent ← r.fCurrent + bitsPerWord
ELSE r.validBitCount ← bitsPerWord;
ENDLOOP;
IF r.fCurrent < r.fSize THEN {
WHILE r.currentWord < 0 DO
Count some more one bits.
r.currentWord ← 2*r.currentWord;
r.validBitCount ← r.validBitCount-1;
r.fCurrent ← r.fCurrent + 1;
ENDLOOP;
};
IF (r.fMax ← r.fTranslate + MIN[r.fSize, r.fCurrent]) < r.fMin THEN r.fMin ← r.fMax;
r.firstRunOnLine ← r.s # olds;
IF r.fCurrent >= r.fSize THEN {
Fix up so a new line will start next time around.
r.currentWord ← 0;
};
};
runs => {
newline: BOOLEANFALSE;
runRep: RunRep;
IF r.runBlock.length = (r.index ← r.index + 1) THEN {
r.index ← 0;
IF (r.blockDirectoryIndex ← r.blockDirectoryIndex+1) = r.blockDirectory.length THEN {
IF r.repeatCount = 1 THEN {
r.done ← TRUE;
r.firstRunOnLine ← TRUE;
r.fMin ← r.fMax ← 0;
r.s ← LAST[INTEGER];
RETURN
}
ELSE {
r.index ← r.lineStartIndex;
r.runBlock ← r.blockDirectory[r.blockDirectoryIndex ← r.lineStartBlockDirectoryIndex];
};
}
ELSE r.runBlock ← r.blockDirectory[r.blockDirectoryIndex];
};
runRep ← r.runBlock[r.index];
IF r.firstRunOnLine ← (runRep.newLineRepeatCount # 0) THEN {
r.s ← r.s + 1;
IF (r.repeatCount ← r.repeatCount - 1) = 0 THEN {
r.repeatCount ← runRep.newLineRepeatCount;
r.lineStartIndex ← r.index;
r.lineStartBlockDirectoryIndex ← r.blockDirectoryIndex;
}
ELSE {
r.index ← r.lineStartIndex;
r.runBlock ← r.blockDirectory[r.blockDirectoryIndex ← r.lineStartBlockDirectoryIndex];
runRep ← r.runBlock[r.index];
};
};
r.fMax ← (r.fMin ← runRep.fMin + r.fTranslate) + runRep.fSize;
};
box => {
IF (r.repeatCount ← r.repeatCount - 1) = 0 THEN {
r.done ← TRUE;
r.fMin ← r.fMax ← 0;
r.s ← LAST[INTEGER];
}
ELSE r.s ← r.s + 1;
};
ENDCASE => ERROR;
};
SkipTo: PUBLIC UNSAFE PROC [r: POINTER TO Reader, s: INTEGER] ~ UNCHECKED {
IF r.s >= s THEN RETURN;
SELECT r.representation FROM
runs => {
WHILE r.s < s DO
IF r.firstRunOnLine AND r.repeatCount > 1 THEN {
delta: NATMIN[s-r.s, r.repeatCount-1];
r.repeatCount ← r.repeatCount - delta;
r.s ← r.s + delta;
}
ELSE Advance[r];
ENDLOOP;
};
box => {
IF s >= INT[r.s] + r.repeatCount THEN {
r.done ← TRUE;
r.fMin ← r.fMax ← 0;
r.s ← LAST[INTEGER];
}
ELSE {
r.repeatCount ← r.repeatCount - (s-r.s);
r.s ← s;
};
};
ENDCASE => {WHILE r.s < s DO Advance[r] ENDLOOP};
};
And: PUBLIC PROC [a, b: Mask] RETURNS [Mask] ~ {
Runs: PROC[run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ TRUSTED {
aReader, bReader: Reader;
SetReader[@aReader, a];
SetReader[@bReader, b];
DO
duplicates: NAT;
s: INTEGER;
somethingInScanline: BOOLEANFALSE;
IF aReader.s < bReader.s THEN SkipTo[@aReader, bReader.s]; IF aReader.done THEN EXIT;
IF aReader.s > bReader.s THEN SkipTo[@bReader, aReader.s]; IF bReader.done THEN EXIT;
s ← aReader.s;
duplicates ← MIN[aReader.repeatCount, bReader.repeatCount] - 1;
WHILE s = aReader.s AND s = bReader.s DO
fMin: INTEGERMAX[aReader.fMin, bReader.fMin];
fMax: INTEGERMIN[aReader.fMax, bReader.fMax];
IF fMin < fMax THEN {run[s, fMin, fMax - fMin]; somethingInScanline ← TRUE};
SELECT fMax FROM
aReader.fMax => {Advance[@aReader]};
bReader.fMax => {Advance[@bReader]};
ENDCASE => ERROR;
ENDLOOP;
IF duplicates > 0 THEN {
IF somethingInScanline THEN repeat[duplicates];
SkipTo[@aReader, s+1+duplicates];
SkipTo[@bReader, s+1+duplicates];
};
ENDLOOP;
};
IF b.refRep = NIL
AND a.sMin >= b.sMin AND a.fMin >= b.fMin
AND a.sMin + a.sSize <= b.sMin + b.sSize AND a.fMin + a.fSize <= b.fMin + b.fSize
THEN RETURN [a];
IF a.refRep = NIL
AND b.sMin >= a.sMin AND b.fMin >= a.fMin
AND b.sMin + b.sSize <= a.sMin + a.sSize AND b.fMin + b.fSize <= a.fMin + a.fSize
THEN RETURN [b];
IF a.sSize = 0 OR b.sSize = 0
OR a.sMin >= b.sMin + b.sSize
OR b.sMin >= a.sMin + a.sSize
OR a.fMin >= b.fMin + b.fSize
OR b.fMin >= a.fMin + a.fSize
THEN RETURN [[0, 0, 0, 0, NIL]];
RETURN[Create[Runs]];
};
Or: PUBLIC PROC [a, b: Mask] RETURNS [Mask] ~ {
Runs: PROC[run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ TRUSTED {
aReader, bReader: Reader;
SetReader[@aReader, a];
SetReader[@bReader, b];
UNTIL aReader.done AND bReader.done DO
as: INTEGER ← aReader.s;
bs: INTEGER ← bReader.s;
s: INTEGERMIN[as, bs];
duplicates: NATMIN[aReader.repeatCount, bReader.repeatCount];
somethingInScanline: BOOLEANFALSE;
WHILE s = aReader.s OR s = bReader.s DO
fStart: INTEGERLAST[INTEGER];
fEnd: INTEGER;
IF s = aReader.s THEN {fStart ← aReader.fMin};
IF s = bReader.s THEN {fStart ← MIN[fStart, bReader.fMin]};
fEnd ← fStart;
DO
SELECT TRUE FROM
s = aReader.s AND aReader.fMin <= fEnd => {
fEnd ← MAX[fEnd, aReader.fMax];
Advance[@aReader];
};
s = bReader.s AND bReader.fMin <= fEnd => {
fEnd ← MAX[fEnd, bReader.fMax];
Advance[@bReader];
};
ENDCASE => EXIT;
ENDLOOP;
IF fEnd > fStart THEN {run[s, fStart, fEnd - fStart]; somethingInScanline ← TRUE};
ENDLOOP;
IF as = s AND bs = s AND duplicates > 1 THEN {
IF somethingInScanline THEN repeat[duplicates-1];
SkipTo[@aReader, s+duplicates];
SkipTo[@bReader, s+duplicates];
};
ENDLOOP;
};
RETURN[Create[Runs]];
};
Difference: PUBLIC PROC [a, b: Mask] RETURNS [Mask] ~ {
Runs: PROC[run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ TRUSTED {
aReader, bReader: Reader;
SetReader[@aReader, a];
SetReader[@bReader, b];
DO
duplicates: NAT;
s: INTEGER;
somethingInScanline: BOOLEANFALSE;
bInversefMin, bInversefMax: INTEGER;
s ← aReader.s;
duplicates ← MIN[s + aReader.repeatCount, bReader.s] - s;
WHILE aReader.s < bReader.s DO
run[aReader.s, aReader.fMin, aReader.fMax - aReader.fMin];
Advance[@aReader];
IF s # aReader.s THEN {
IF duplicates > 1 THEN {repeat[duplicates-1]; SkipTo[@aReader, s+duplicates]};
s ← aReader.s;
duplicates ← MIN[s + aReader.repeatCount, bReader.s] - s;
};
ENDLOOP;
IF aReader.done THEN EXIT;
IF aReader.s > bReader.s THEN SkipTo[@bReader, aReader.s];
s ← aReader.s;
duplicates ← MIN[aReader.repeatCount, bReader.repeatCount] - 1;
bInversefMin ← FIRST[INTEGER];
bInversefMax ← bReader.fMin;
WHILE s = aReader.s DO
fMin: INTEGERMAX[aReader.fMin, bInversefMin];
fMax: INTEGERMIN[aReader.fMax, bInversefMax];
IF fMin < fMax THEN {run[s, fMin, fMax - fMin]; somethingInScanline ← TRUE};
SELECT fMax FROM
aReader.fMax => {Advance[@aReader]};
bInversefMax => {
IF bInversefMax = LAST[INTEGER] THEN EXIT;
bInversefMin ← bReader.fMax;
Advance[@bReader];
bInversefMax ← IF bReader.s = s THEN bReader.fMin ELSE LAST[INTEGER];
};
ENDCASE => ERROR;
ENDLOOP;
IF duplicates > 0 THEN {
IF somethingInScanline THEN repeat[duplicates];
SkipTo[@aReader, s+1+duplicates];
SkipTo[@bReader, s+1+duplicates];
};
ENDLOOP;
};
RETURN[Create[Runs]];
};
InShift: PROC [mask: Mask, s, f: INTEGER] RETURNS [Mask] ~ INLINE {
RETURN[[mask.sMin+s, mask.fMin+f, mask.sSize, mask.fSize, mask.refRep]]
};
Shift: PUBLIC PROC [mask: Mask, s, f: INTEGER] RETURNS [Mask] ~ {
RETURN[[mask.sMin+s, mask.fMin+f, mask.sSize, mask.fSize, mask.refRep]]
};
Bitmap: PUBLIC PROC [mask: Mask] RETURNS [Mask] ~ {
WITH mask.refRep SELECT FROM
b: REF ImagerPixelMaps.PixelMapRep => RETURN[mask];
ENDCASE => {
pixelMap: PixelMap ← ImagerPixelMaps.Create[0, [mask.sMin, mask.fMin, mask.sSize, mask.fSize]];
BoxProc: PROC [r: DeviceRectangle] ~ {pixelMap.Fill[r, 1]};
ImagerPixelMaps.Clear[pixelMap];
MapBoxes[mask, BoxProc];
RETURN[[mask.sMin, mask.fMin, mask.sSize, mask.fSize, pixelMap.refRep]];
};
};
CountRuns: PUBLIC PROC [mask: Mask] RETURNS [runs: INT ← 0] ~ TRUSTED {
aReader: Reader;
SetReader[@aReader, mask];
UNTIL aReader.done DO
sSize: NAT ← aReader.repeatCount;
s: INTEGER ← aReader.s;
WHILE s = aReader.s DO
runs ← runs + sSize;
Advance[@aReader];
ENDLOOP;
SkipTo[@aReader, s+sSize];
ENDLOOP;
};
MapRuns: PUBLIC PROC [mask: Mask, run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ TRUSTED {
aReader: Reader;
SetReader[@aReader, mask];
UNTIL aReader.done DO
run[aReader.s, aReader.fMin, aReader.fMax - aReader.fMin];
Advance[@aReader];
ENDLOOP;
};
MapBoxes: PUBLIC PROC [mask: Mask, box: PROC [DeviceRectangle]] ~ TRUSTED {
aReader: Reader;
SetReader[@aReader, mask];
UNTIL aReader.done DO
sSize: NAT ← aReader.repeatCount;
s: INTEGER ← aReader.s;
WHILE s = aReader.s DO
box[[s, aReader.fMin, sSize, aReader.fMax - aReader.fMin]];
Advance[@aReader];
ENDLOOP;
SkipTo[@aReader, s+sSize];
ENDLOOP;
};
MapClippedRuns: PUBLIC PROC [mask, clipper: Mask, run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ TRUSTED {
aReader, bReader: Reader;
SetReader[@aReader, mask];
SetReader[@bReader, clipper];
DO
s: INTEGER;
IF aReader.s < bReader.s THEN SkipTo[@aReader, bReader.s]; IF aReader.done THEN EXIT;
IF aReader.s > bReader.s THEN SkipTo[@bReader, aReader.s]; IF bReader.done THEN EXIT;
s ← aReader.s;
WHILE s = aReader.s AND s = bReader.s DO
fMin: INTEGERMAX[aReader.fMin, bReader.fMin];
fMax: INTEGERMIN[aReader.fMax, bReader.fMax];
IF fMin < fMax THEN run[aReader.s, fMin, fMax - fMin];
SELECT fMax FROM
aReader.fMax => {Advance[@aReader]};
bReader.fMax => {Advance[@bReader]};
ENDCASE => ERROR;
ENDLOOP;
ENDLOOP;
};
MapClippedBoxes: PUBLIC PROC [mask, clipper: Mask, box: PROC [DeviceRectangle]] ~ TRUSTED {
aReader, bReader: Reader;
SetReader[@aReader, mask];
SetReader[@bReader, clipper];
DO
duplicates: NAT;
s: INTEGER;
IF aReader.s < bReader.s THEN SkipTo[@aReader, bReader.s]; IF aReader.done THEN EXIT;
IF aReader.s > bReader.s THEN SkipTo[@bReader, aReader.s]; IF bReader.done THEN EXIT;
s ← aReader.s;
duplicates ← MIN[aReader.repeatCount, bReader.repeatCount] - 1;
WHILE s = aReader.s AND s = bReader.s DO
fMin: INTEGERMAX[aReader.fMin, bReader.fMin];
fMax: INTEGERMIN[aReader.fMax, bReader.fMax];
IF fMin < fMax THEN box[[aReader.s, fMin, duplicates+1, fMax - fMin]];
SELECT fMax FROM
aReader.fMax => {Advance[@aReader]};
bReader.fMax => {Advance[@bReader]};
ENDCASE => ERROR;
ENDLOOP;
IF duplicates > 0 THEN {
SkipTo[@aReader, s+1+duplicates];
SkipTo[@bReader, s+1+duplicates];
};
ENDLOOP;
};
IsVisible: PUBLIC PROC [mask, clipper: Mask] RETURNS [Visibility] ~ TRUSTED {
reader: Reader;
empty: BOOLEANTRUE;
CheckRun: SAFE PROC [sMin, fMin: INTEGER, fSize: CARDINAL] ~ TRUSTED {
fMax: INTEGER ← fMin+fSize;
empty ← FALSE;
IF sMin = reader.s AND fMin = reader.fMin AND fMax = reader.fMax THEN Advance[@reader];
};
IF clipper.refRep = NIL THEN {
IF mask.sMin >= clipper.sMin
AND mask.sMin + mask.sSize <= clipper.sMin + clipper.sSize
AND mask.fMin >= clipper.fMin
AND mask.fMin + mask.fSize <= clipper.fMin + clipper.fSize
THEN RETURN[visible];
};
IF mask.sMin >= clipper.sMin + clipper.sSize
OR mask.sMin + mask.sSize <= clipper.sMin
OR mask.fMin >= clipper.fMin + clipper.fSize
OR mask.fMin + mask.fSize <= clipper.fMin
THEN RETURN[invisible];
SetReader[@reader, mask];
MapClippedRuns[mask, clipper, CheckRun];
RETURN[IF empty THEN invisible ELSE IF reader.done THEN visible ELSE partlyVisible];
};
TileType: PROC [tile: ImagerPixelMaps.Tile] RETURNS [ImagerPixelMaps.Function] ~ TRUSTED {
w: LONG POINTER TO INTEGER ← tile.refRep.pointer;
IF tile.sSize = 1 AND tile.fSize = 16 THEN {
IF w^ = -1 THEN RETURN [[or, null]];
IF w^ = 0 THEN RETURN [[and, complement]];
};
RETURN [[xor, null]];
};
MaskTile: PUBLIC PROC [dest: PixelMap, mask: Mask, tile: ImagerPixelMaps.Tile, function: ImagerPixelMaps.Function] ~ {
IF function = [null, null] AND dest.refRep.lgBitsPerPixel = 0 THEN WITH mask.refRep SELECT FROM
refRep: REF ImagerPixelMaps.PixelMapRep => {
tileType: ImagerPixelMaps.Function ← TileType[tile];
IF tileType = [xor, null] THEN {
dest ← dest.Clip[[mask.sMin, mask.fMin, mask.sSize, mask.fSize]];
dest.TransferTile[tile, [xor, null]];
dest.Transfer[[mask.sMin, mask.fMin, 0, 0, mask.sSize, mask.fSize, refRep], [and, complement]];
dest.TransferTile[tile, [xor, null]];
}
ELSE dest.Transfer[[mask.sMin, mask.fMin, 0, 0, mask.sSize, mask.fSize, refRep], tileType];
RETURN;
};
ENDCASE => NULL;
TRUSTED {
aReader: Reader;
SetReader[@aReader, mask];
UNTIL aReader.done DO
sSize: NAT ← aReader.repeatCount;
s: INTEGER ← aReader.s;
WHILE s = aReader.s DO
dest.Clip[[s, aReader.fMin, sSize, aReader.fMax - aReader.fMin]].TransferTile[tile, function];
Advance[@aReader];
ENDLOOP;
SkipTo[@aReader, s+sSize];
ENDLOOP;
};
};
END.