ImagerMasksImpl.mesa
Michael Plass, October 17, 1983 3:44 pm
DIRECTORY
ImagerMasks,
ImagerMasksPrivate,
ImagerPixelMaps,
UnsafeStorage,
ImagerScanConverter,
Environment
;
ImagerMasksImpl:
CEDAR
PROGRAM
IMPORTS UnsafeStorage, ImagerPixelMaps, ImagerScanConverter
EXPORTS ImagerMasks, ImagerMasksPrivate
= BEGIN OPEN ImagerMasks;
maxBlockLength: NAT ~ 150;
indexSize: NAT ~ 200;
bitsPerWord: NAT ~ Environment.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: INTEGER ← LAST[INTEGER];
maxf: INTEGER ← FIRST[INTEGER];
mins: INTEGER ← LAST[INTEGER];
lasts: INTEGER ← LAST[INTEGER];
lastf: INTEGER ← FIRST[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: BOOLEAN ← FALSE;
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 ~ ImagerMasksPrivate.Representation;
Reader: TYPE ~ ImagerMasksPrivate.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: BOOLEAN ← FALSE;
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: NAT ← MIN[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: BOOLEAN ← FALSE;
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: INTEGER ← MAX[aReader.fMin, bReader.fMin];
fMax: INTEGER ← MIN[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: INTEGER ← MIN[as, bs];
duplicates: NAT ← MIN[aReader.repeatCount, bReader.repeatCount];
somethingInScanline: BOOLEAN ← FALSE;
WHILE s = aReader.s
OR s = bReader.s
DO
fStart: INTEGER ← LAST[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: BOOLEAN ← FALSE;
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: INTEGER ← MAX[aReader.fMin, bInversefMin];
fMax: INTEGER ← MIN[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: INTEGER ← MAX[aReader.fMin, bReader.fMin];
fMax: INTEGER ← MIN[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: INTEGER ← MAX[aReader.fMin, bReader.fMin];
fMax: INTEGER ← MIN[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: BOOLEAN ← TRUE;
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.