ImagerMasksImpl.mesa
Michael Plass, July 20, 1983 12:32 pm
DIRECTORY
BitBlt,
Environment,
ImagerMasks,
ImagerMasksPrivate,
Inline,
UnsafeStorage,
UserTerminal
;
ImagerMasksImpl: CEDAR PROGRAM
IMPORTS BitBlt, Inline, UnsafeStorage, UserTerminal
EXPORTS ImagerMasks, ImagerMasksPrivate
= BEGIN OPEN ImagerMasks;
maxBlockLength: NAT ~ 150;
indexSize: NAT ~ 200;
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[]]};
CreatePDRep: PUBLIC PROC [runs: PROC[PROC[s, fMin: INTEGER, fSize: NAT]]] RETURNS [mask: Mask] ~ TRUSTED {
indexArrayLength: 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];
TRUSTED {ENABLE UNWIND => Release[];
temp: LONG POINTER TO TempRunGroupRep ← NIL;
Extend: UNSAFE PROC = {
indexArray[indexArrayLength] ← NIL;
indexArrayLength ← indexArrayLength + 1;
temp ← indexArray[indexArrayLength - 1] ← uz.NEW[TempRunGroupRep];
tempLength ← 0;
};
Run: SAFE PROC[s, fMin, fSize: INTEGER] ~ TRUSTED {
newLine: BOOLEANFALSE;
IF s < lasts THEN {IF lasts = LAST[INTEGER] THEN {mins ← lasts ← s; newLine ← TRUE} ELSE ERROR};
WHILE s # lasts DO
IF newLine THEN {
IF tempLength = maxBlockLength THEN Extend[];
temp[tempLength] ← [1, 0, 0];
tempLength ← tempLength + 1;
};
newLine ← TRUE;
lasts ← lasts + 1;
ENDLOOP;
IF tempLength = maxBlockLength THEN Extend[];
temp[tempLength] ← [IF newLine THEN 1 ELSE 0, fMin, fSize];
tempLength ← tempLength + 1;
minf ← MIN[minf, fMin];
maxf ← MAX[maxf, fMin + fSize];
};
indexArray ← uz.NEW[IndexArrayRep];
runs[Run];
totalLength ← INT[indexArrayLength]*maxBlockLength + tempLength - maxBlockLength;
IF lasts = LAST[INTEGER] THEN {mask ← [0, 0, 0, 0, NIL]}
ELSE {
runGroup: PDRunGroup ← NIL;
lastRun: BOOLEANTRUE;
FOR i: CARDINAL DECREASING IN [0..indexArrayLength) DO
new: PDRunGroup ← NEW[PDRunGroupRep[tempLength]];
temp ← indexArray[i];
FOR j: [0..maxBlockLength) DECREASING IN [0..tempLength) DO
tempj: TempRunRep ← temp[j];
new[j] ← [
fMin: IF tempj.fSize = 0 THEN 0 ELSE tempj.fMin - minf,
lastRun: lastRun,
fSize: tempj.fSize
];
lastRun ← tempj.newLineRepeatCount # 0;
ENDLOOP;
new.rest ← runGroup;
runGroup ← new;
tempLength ← maxBlockLength;
ENDLOOP;
IF NOT lastRun THEN ERROR;
mask ← [sMin: mins, fMin: minf, sSize: lasts + 1 - mins, fSize: maxf - minf];
mask.refRep ← NEW[MaskRep.pdruns ←
[pdruns[runCount: totalLength, runGroup: runGroup]]
];
};
};
TRUSTED {Release[]};
};
RunsOutOfOrder: PUBLIC ERROR = CODE;
Create: PUBLIC PROC [
runs: PROC[ -- Create calls this back
run: PROC[s, 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[s, fMin, fSize: INTEGER] ~ TRUSTED {
newLine: BOOLEANFALSE;
IF s < lasts THEN {IF lasts = LAST[INTEGER] THEN {mins ← lasts ← s; newLine ← TRUE} ELSE ERROR RunsOutOfOrder};
WHILE s # lasts DO
IF newLine THEN {
IF tempLength = maxBlockLength THEN Extend[];
StartNewLine[];
temp[tempLength] ← [s-lasts, 0, 0];
tempLength ← tempLength + 1;
lasts ← s;
}
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] + NAT[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 MaskRep.runs ← NEW[MaskRep.runs[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[]};
};
BitmapStorage: TYPE ~ RECORD [SEQUENCE words: CARDINAL OF WORD];
NewBitmap: PUBLIC PROC [sMin, fMin: INTEGER, sSize, fSize: NAT] RETURNS [Mask] ~ TRUSTED {
rast: INT ← (fSize + bitsPerWord - 1)/bitsPerWord;
storageSize: [0..16000] ← rast*sSize;
ref: REF BitmapStorage ← NEW[BitmapStorage[storageSize]];
refRep: REF MaskRep.bitmap ← NEW[MaskRep.bitmap];
refRep.lines ← sSize;
refRep.ref ← ref;
refRep.pointer ← @ref[0];
refRep.rast ← rast;
RETURN [[sMin, fMin, sSize, fSize, refRep]];
};
Box: PUBLIC PROC [sMin, fMin: INTEGER, sSize, fSize: NAT] RETURNS [Mask] ~ {
RETURN[[sMin, fMin, sSize, 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;
IF mask.sSize = 0 THEN {
reader.done ← TRUE;
reader.fMin ← reader.fMax ← 0;
reader.s ← LAST[INTEGER];
RETURN
};
WITH mask.refRep SELECT FROM
b: REF MaskRep.bitmap => {
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;
};
pd: REF MaskRep.pdruns => {
fMin, fSize: NAT;
reader.representation ← pdruns;
reader.pdRunGroup ← pd.runGroup;
[fMin: fMin, fSize: fSize] ← pd.runGroup[0];
reader.fMax ← (reader.fMin ← fMin + reader.fTranslate) + fSize;
};
r: REF MaskRep.runs => {
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 {
fMin, fSize: NAT;
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;
};
};
pdruns => {
IF (r.firstRunOnLine ← r.pdRunGroup[r.index].lastRun) THEN r.s ← r.s + 1;
IF r.pdRunGroup.length = (r.index ← r.index + 1) THEN {
r.index ← 0;
IF (r.pdRunGroup ← r.pdRunGroup.rest) = NIL THEN {r.done ← TRUE; r.fMin ← r.fMax ← 0; r.s ← LAST[INTEGER]; RETURN};
};
[fMin: fMin, fSize: fSize] ← r.pdRunGroup[r.index];
r.fMax ← (r.fMin ← fMin + r.fTranslate) + fSize;
};
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 = LAST[INTEGER] THEN RETURN;
IF r.s > s THEN ERROR;
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[s, 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[s, 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[s, 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] ~ {
new: Mask;
WITH mask.refRep SELECT FROM
b: REF MaskRep.bitmap => RETURN[mask];
ENDCASE => NULL;
new ← NewBitmap[0, 0, mask.sSize, mask.fSize];
MaskConstant[new, InShift[mask, -mask.sMin, -mask.fMin], inkTile, 0, [null, null]];
RETURN[InShift[new, mask.sMin, mask.fMin]];
};
MapRuns: PUBLIC PROC [mask: Mask, run: PROC [s, fMin: INTEGER, fSize: CARDINAL]] ~ 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 [sMin, fMin: INTEGER, sSize, fSize: CARDINAL]] ~ 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 [s, fMin: INTEGER, fSize: CARDINAL]] ~ 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 [sMin, fMin: INTEGER, sSize, fSize: CARDINAL]] ~ 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 [s, fMin: INTEGER, fSize: CARDINAL] ~ TRUSTED {
fMax: INTEGER ← fMin+fSize;
empty ← FALSE;
IF s = reader.s AND fMin = reader.fMin AND fMax = reader.fMax THEN Advance[@reader];
};
SetReader[@reader, mask];
MapClippedRuns[mask, clipper, CheckRun];
RETURN[IF empty THEN invisible ELSE IF reader.done THEN visible ELSE partlyVisible];
};
bitsPerWord: NAT ~ Environment.bitsPerWord;
MaskConstant: PUBLIC PROC [dest: Mask, mask: Mask, tile: Mask, phase: NAT, function: Function ← [null, null]] ~ TRUSTED {
bitmapTile: Mask ← Bitmap[tile];
tileRepRef: REF MaskRep.bitmap ← NARROW[bitmapTile.refRep];
lines: [0..16] ← tileRepRef.lines;
IF phase # 0 THEN ERROR;
Not implemented yet.
IF tile.fSize # 16 THEN ERROR;
IF tileRepRef.rast # 1 THEN ERROR;
IF lines = 0 THEN ERROR;
IF tile = inkTile AND mask.refRep # NIL AND ISTYPE[mask.refRep, REF MaskRep.bitmap] AND (function = [null, null] OR function = [or, null]) THEN
MaskSampled[dest, [mask.sMin, mask.fMin, mask.sSize, mask.fSize, NIL], mask, [or, null]]
ELSE WITH dest.refRep SELECT FROM
b: REF MaskRep.bitmap => {
bbTableSpace: BitBlt.BBTableSpace;
bbPtr: BitBlt.BBptr ← BitBlt.AlignedBBTable[@bbTableSpace];
reader: Reader;
sEnd: INTEGER;
sMaxDest: INTEGERMIN[dest.sSize, b.lines];
fMaxDest: INTEGERMIN[dest.fSize, b.rast*bitsPerWord];
bbPtr.flags ← [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE];
[bbPtr.flags.dstFunc, bbPtr.flags.srcFunc] ← function;
bbPtr.dstBpl ← b.rast*bitsPerWord;
bbPtr.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: lines-1];
bbPtr.src ← [word: tileRepRef.pointer, bit: 0];
bbPtr.reserved ← 0;
SetReader[@reader, InShift[mask, -dest.sMin, -dest.fMin]];
UNTIL reader.done DO
IF reader.firstRunOnLine THEN {sEnd ← reader.s+reader.repeatCount};
IF TRUE THEN {
Fill one box.
sMin: INTEGERMAX[reader.s, 0];
fMin: INTEGERMAX[reader.fMin, 0];
sMax: INTEGERMIN[sEnd, sMaxDest];
fMax: INTEGERMIN[reader.fMax, fMaxDest];
IF sMin < sMax AND fMin < fMax THEN {
quotient: CARDINAL ← Inline.BITSHIFT[fMin, -INTEGER[Environment.logBitsPerWord]];
remainder: CARDINAL ← Inline.BITAND[fMin, bitsPerWord-1];
sOffset: INTEGER ← (sMin-tile.sMin+dest.sMin) MOD lines;
WHILE sOffset < 0 DO sOffset ← sOffset + lines ENDLOOP;
bbPtr.dst ← [
word: b.pointer + Inline.LongMult[sMin, b.rast] + quotient,
bit: remainder
];
bbPtr.width ← fMax-fMin;
bbPtr.height ← sMax-sMin;
bbPtr.srcDesc.gray.yOffset ← sOffset;
bbPtr.src.word ← tileRepRef.pointer + sOffset;
bbPtr.src.bit ← Inline.BITAND[fMin-tile.fMin+dest.fMin, bitsPerWord-1];
BitBlt.BITBLT[bbPtr];
};
};
Advance[@reader];
IF reader.firstRunOnLine AND sEnd > reader.s THEN SkipTo[@reader, sEnd];
ENDLOOP;
};
ENDCASE => ERROR;
};
MaskSampled: PUBLIC PROC [dest: Mask, mask: Mask, source: Mask, function: Function ← [null, null]] ~ TRUSTED {
IF source.sSize = 0 OR source.fSize = 0 THEN RETURN
ELSE {
bitmapSource: Mask ← Bitmap[InShift[source, -dest.sMin, -dest.fMin]];
sourceRepRef: REF MaskRep.bitmap ← NARROW[bitmapSource.refRep];
WITH dest.refRep SELECT FROM
b: REF MaskRep.bitmap => {
bbTableSpace: BitBlt.BBTableSpace;
bbPtr: BitBlt.BBptr ← BitBlt.AlignedBBTable[@bbTableSpace];
reader: Reader;
sEnd: INTEGER;
sMinSource: INTEGERMAX[bitmapSource.sMin, 0];
fMinSource: INTEGERMAX[bitmapSource.fMin, 0];
sMaxSource: INTEGERMIN[bitmapSource.sSize+bitmapSource.sMin, dest.sSize, b.lines];
fMaxSource: INTEGERMIN[bitmapSource.fSize+bitmapSource.fMin, dest.fSize, b.rast*bitsPerWord];
bbPtr.flags ← [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
[bbPtr.flags.dstFunc, bbPtr.flags.srcFunc] ← function;
bbPtr.dstBpl ← b.rast*bitsPerWord;
bbPtr.srcDesc.srcBpl ← sourceRepRef.rast*bitsPerWord;
bbPtr.src ← [word: NIL, bit: 0];
bbPtr.reserved ← 0;
SetReader[@reader, InShift[mask, -dest.sMin, -dest.fMin]];
UNTIL reader.done DO
IF reader.firstRunOnLine THEN {sEnd ← reader.s+reader.repeatCount};
IF TRUE THEN {
Fill one box.
sMin: INTEGERMAX[reader.s, sMinSource];
fMin: INTEGERMAX[reader.fMin, fMinSource];
sMax: INTEGERMIN[sEnd, sMaxSource];
fMax: INTEGERMIN[reader.fMax, fMaxSource];
IF sMin < sMax AND fMin < fMax THEN {
quotient: CARDINAL ← Inline.BITSHIFT[fMin, -INTEGER[Environment.logBitsPerWord]];
remainder: CARDINAL ← Inline.BITAND[fMin, bitsPerWord-1];
yOffset: INTEGER ← sMin-bitmapSource.sMin;
wordOffset: CARDINAL ← Inline.BITSHIFT[fMin-bitmapSource.fMin, -INTEGER[Environment.logBitsPerWord]];
bbPtr.dst ← [
word: b.pointer + Inline.LongMult[sMin, b.rast] + quotient,
bit: remainder
];
bbPtr.width ← fMax-fMin;
bbPtr.height ← sMax-sMin;
bbPtr.src.word ← sourceRepRef.pointer + Inline.LongMult[yOffset, sourceRepRef.rast] + wordOffset;
bbPtr.src.bit ← Inline.BITAND[fMin-bitmapSource.fMin, bitsPerWord-1];
BitBlt.BITBLT[bbPtr];
};
};
Advance[@reader];
IF reader.firstRunOnLine AND sEnd > reader.s THEN SkipTo[@reader, sEnd];
ENDLOOP;
};
ENDCASE => ERROR;
};
};
GetBitBltTable: PROC RETURNS [BitBlt.BBTable] ~ TRUSTED {
[] ← UserTerminal.SetState[on];
RETURN [UserTerminal.GetBitBltTable[]];
};
LFDisplay: PUBLIC PROC RETURNS [Mask] ~ {
bbTable: BitBlt.BBTable ← GetBitBltTable[];
maskRef: REF MaskRep.bitmap ← NEW[MaskRep.bitmap];
maskRef.lines ← bbTable.height;
maskRef.pointer ← bbTable.dst.word;
maskRef.rast ← bbTable.dstBpl/bitsPerWord;
IF bbTable.dst.bit # 0 THEN ERROR;
IF bbTable.width # maskRef.rast*bitsPerWord THEN ERROR;
RETURN[[0, 0, bbTable.height, bbTable.width, maskRef]];
};
inkTile: PUBLIC Mask ← MakeInk[];
MakeInk: PROC RETURNS [Mask] ~ TRUSTED {
refRep: REF MaskRep.bitmap ← NEW[MaskRep.bitmap];
refRep.lines ← 1;
refRep.ref ← NEW[CARDINALLAST[CARDINAL]];
refRep.pointer ← LOOPHOLE[refRep.ref];
refRep.rast ← 1;
RETURN [[0, 0, 1, bitsPerWord, refRep]]
};
END.