<> <> 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: INTEGER _ LAST[INTEGER]; maxf: INTEGER _ FIRST[INTEGER]; mins: INTEGER _ LAST[INTEGER]; lasts: INTEGER _ LAST[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: BOOLEAN _ FALSE; 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: BOOLEAN _ TRUE; 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: 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[s, fMin, fSize: INTEGER] ~ TRUSTED { newLine: BOOLEAN _ FALSE; 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 <> 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 { <> 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 <> r.currentWord _ 2*r.currentWord; r.validBitCount _ r.validBitCount-1; ENDLOOP; r.fMin _ r.fTranslate + r.fCurrent - r.validBitCount; WHILE r.currentWord < 0 DO <> 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 <> 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 <> 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 { <> 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: 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 = 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: 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[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: 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[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: 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[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: 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] ~ { 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: 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 [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: 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 [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; <> 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: INTEGER _ MIN[dest.sSize, b.lines]; fMaxDest: INTEGER _ MIN[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 { <> sMin: INTEGER _ MAX[reader.s, 0]; fMin: INTEGER _ MAX[reader.fMin, 0]; sMax: INTEGER _ MIN[sEnd, sMaxDest]; fMax: INTEGER _ MIN[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: INTEGER _ MAX[bitmapSource.sMin, 0]; fMinSource: INTEGER _ MAX[bitmapSource.fMin, 0]; sMaxSource: INTEGER _ MIN[bitmapSource.sSize+bitmapSource.sMin, dest.sSize, b.lines]; fMaxSource: INTEGER _ MIN[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 { <> sMin: INTEGER _ MAX[reader.s, sMinSource]; fMin: INTEGER _ MAX[reader.fMin, fMinSource]; sMax: INTEGER _ MIN[sEnd, sMaxSource]; fMax: INTEGER _ MIN[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[CARDINAL _ LAST[CARDINAL]]; refRep.pointer _ LOOPHOLE[refRep.ref]; refRep.rast _ 1; RETURN [[0, 0, 1, bitsPerWord, refRep]] }; END.