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. 4ImagerMasksImpl.mesa Michael Plass, July 20, 1983 12:32 pm Count clear words in this scanline. Advance to next scanline Count clear bits. Count one bits. Count all-ones words. Count some more one bits. Fix up so a new line will start next time around. Not implemented yet. Fill one box. Fill one box. Ê$ý˜J™J™%šÏk ˜ J˜J˜ Jšœ ˜ Jšœ˜J˜Jšœ˜J˜ J˜—J˜šœœ˜Jšœ,˜3Jšœ ˜'Jšœœœ ˜—Jšœœ˜Jšœ œ˜šœ œœ˜JšœœÏc0˜IJšœœ˜Jšœ˜Jšœ˜—Jšœœœœ ˜@Jš œœœœœœœ˜NJšœ œœ ˜JšÏnœœœ œœœœ"˜YšŸ œœœœœ œ œœœ˜jJšœœ˜Jšœ œœœ˜*Jšœ œ˜&Jšœ œ˜šŸœœœ˜šœœœ˜šœœœ˜+Jšœœœœ˜6Jšœ˜—Jšœœ˜Jšœ˜—Jšœ˜—Jšœœœœ˜Jšœœœœ˜Jšœœœœ˜Jšœœœœ˜šœœœ˜$Jš œœœœœ˜,šŸœœœ˜Jšœœ˜#Jšœ(˜(Jšœ-œ˜BJšœ˜Jšœ˜—š Ÿœœœœœ˜3Jšœ œœ˜Jšœ œœ œœœœœœ˜`šœ ˜šœ œ˜Jšœœ ˜-Jšœ˜Jšœ˜Jšœ˜—Jšœ œ˜Jšœ˜Jšœ˜—Jšœœ ˜-Jšœœ œœ˜;Jšœ˜Jšœœ ˜Jšœœ˜Jšœ˜—Jšœœ˜#Jšœ ˜ Jšœœ@˜QJš œ œœœœ˜8šœ˜Jšœœ˜Jšœ œœ˜š œœ œœ˜6Jšœœ˜1Jšœ˜šœ œœ˜;Jšœ˜šœ ˜ Jšœœœœ˜7Jšœ˜Jšœ˜J˜—Jšœ'˜'Jšœ˜—Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœœ œœ˜JšœM˜Mšœœ˜"Jšœ3˜3Jšœ˜—J˜—Jšœ˜—Jšœ ˜Jšœ˜—Jšœœœœ˜$šŸœœœ˜šœœž˜%Jšœœ œ œž&˜PJšœœœž)˜SJšœ˜—Jšœœœ˜"Jšœœ˜Jšœœ˜Jšœ œœœ˜*Jšœ œ˜&Jšœ œ˜šŸœœœ˜šœœœ˜šœœœ˜+Jšœœœœ˜6Jšœ˜—Jšœœ˜Jšœ˜—Jšœ˜—Jšœœœœ˜Jšœœœœ˜Jšœœœœ˜Jšœœœœ˜Jšœœœœ˜ šœœœ˜$Jš œœœœœž"˜OJšœœ˜ Jšœœ˜&šŸœœœ˜Jšœ"˜"šœ"œ˜*Jšœœ˜#Jšœ(˜(Jšœ&œ˜;J˜—Jšœ%˜%Jšœ˜Jšœ˜—šŸ œœœ˜Jšœœ˜!Jšœœ˜'Jšœœ˜Jšœœ˜!Jšœœœœ˜/Jšœœœœ"˜9Jšœœœ˜šœž=˜@Jšœ˜šœœ˜Jšœœœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ ˜ šœœ˜Jšœœœ˜J˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ ˜ šœ*œ˜2Jšœ˜šœœœœ˜QJšœœœ˜%JšœN˜NJšœ˜Jšœ,˜,Jšœ+˜+Jšœ˜Jšœ˜—Jšœ˜ Jšœ˜—Jšœœœ˜Jšœ˜—Jšœ˜Jšœ,˜,Jšœ˜—š Ÿœœœœœ˜3Jšœ œœ˜Jšœ œœ œœœœœœ˜ošœ ˜šœ œ˜Jšœœ ˜-Jšœ˜Jšœ#˜#Jšœ˜Jšœ ˜ Jšœ˜—Jšœ œ˜)Jšœ˜—Jšœœ ˜-šœ œ˜Jšœ˜Jšœ$˜$Jšœ˜—Jšœ%˜)Jšœœœ˜*Jšœœ œ˜Jšœ˜Jšœœ ˜Jšœœ˜Jšœ˜—š Ÿœœœœœ˜9Jšœœœœ7˜NJšœ œ0˜>Jšœœœ˜Jšœœ œœ˜?JšœQ˜QJšœ&˜&Jšœ˜—Jšœœ˜#Jšœ˜Jšœž4˜DJšœœ=˜NJš œ œœœœ˜8šœœœœ˜3Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœœ˜Jšœ˜—šœ˜Jšœ œœ˜AJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜š œœ œœ˜3Jšœœ˜-Jšœ˜Jšœ˜šœ œœ˜;Jšœ˜šœ ˜ Jšœ-˜-Jšœœœœ˜7Jšœ˜J˜—Jšœ˜—Jšœ˜Jšœ˜—J˜—Jšœ˜—Jšœ ˜Jšœ˜—Jš œœœœœœœ˜@šŸ œœœœœœ œ˜ZJšœœ)˜2Jšœ%˜%Jšœœœ˜9Jšœœœ˜1Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ&˜,Jšœ˜—š Ÿœœœœœœ ˜LJšœœ˜'Jšœ˜—Jšœœ%˜9Jšœœ˜)šŸ œœœœ œœ œ˜SJšœ˜Jšœ˜Jšœ˜Jšœœ˜Jšœ˜Jšœœ˜šœœ˜Jšœœ˜Jšœ˜Jšœ œœ˜Jš˜Jšœ˜—šœ œ˜šœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ7˜7Jšœ˜Jšœœ!˜3Jšœœ"˜3Jšœ˜Jšœ˜Jšœœ˜Jšœ˜—šœœ˜Jšœ œ˜Jšœ˜Jšœ ˜ Jšœ,˜,Jšœ?˜?Jšœ˜—šœœ˜Jšœ œ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜JšœX˜XJšœ(˜(Jšœ˜Jšœ?˜?Jšœ˜—šœ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ'˜'Jšœ˜——Jšœ˜—šŸœœœœœœ œ˜@Jšœ œ˜Jšœœœ˜šœ˜šœ ˜ Jšœœ˜šœ˜šœœ˜3J™#Jšœ*˜*Jšœ˜Jšœ"˜"Jšœ˜Jšœ˜—šœœ˜J™šœœ˜#Jšœœœ˜Jšœ˜Jšœœ˜!Jšœ˜Jšœ˜—Jšœ˜JšœE˜EJšœ˜Jšœ"˜"Jšœ˜Jšœ˜—Jšœ˜—šœœ˜Jšœœœ˜,Jšœ#˜#Jšœ&˜&Jšœ˜Jšœ˜—šœ˜Jšœ(˜(šœ˜J™Jšœ ˜ Jšœ$˜$Jšœ˜—Jšœ5˜5šœ˜J™Jšœ ˜ Jšœ$˜$Jšœ˜—Jšœ(˜(J˜—šœœ˜5J™Jšœ ˜ Jšœ"˜"Jšœœ&˜@Jšœ˜#Jšœ˜—šœœ˜šœ˜J™Jšœ ˜ Jšœ$˜$Jšœ˜Jšœ˜—J˜—Jšœœ œ˜TJšœ˜šœœ˜J™1Jšœ˜Jšœ˜—Jšœ˜—šœ ˜ Jšœ4œ˜Išœ/œ˜7Jšœ ˜ Jšœ&œœ œœœœ˜sJšœ˜—Jšœ3˜3Jšœ0˜0Jšœ˜—šœ ˜ Jšœ œœ˜Jšœ˜šœ-œ˜5J˜ šœMœ˜Ušœœ˜Jšœ œ˜Jšœœ˜Jšœ˜Jšœœœ˜Jš˜Jšœ˜—šœ˜Jšœ˜JšœV˜VJšœ˜—Jšœ˜—Jšœ6˜:Jšœ˜—Jšœ˜šœ4œ˜˜>Jšœ˜—šœ˜šœ)œ˜1Jšœ œ˜Jšœ˜Jšœœœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—šŸœœœœœœ œ œ˜KJš œœœœœ˜#Jšœ œœ˜šœ˜šœ ˜ šœ ˜šœœœ˜0Jšœœœ˜)Jšœ&˜&Jšœ˜Jšœ˜—Jšœ ˜Jšœ˜—Jšœ˜—šœ˜šœœœ˜'Jšœ œ˜Jšœ˜Jšœœœ˜Jšœ˜—šœ˜Jšœ(˜(J˜Jšœ˜—Jšœ˜—Jšœœ œ œ˜1—Jšœ˜—šŸœœœœ ˜0šŸœœœ œ œ œœœ˜iJšœ˜Jšœ˜Jšœ˜š˜Jšœ œ˜Jšœœ˜ Jšœœœ˜%Jš œœœœœ˜UJš œœœœœ˜UJšœ˜Jšœ œ/˜?šœœ˜(Jšœœœ˜0Jšœœœ˜0Jšœ œ3œ˜Lšœ˜Jšœ$˜$Jšœ$˜$Jšœœ˜—Jšœ˜—šœœ˜Jšœœ˜/Jšœ!˜!Jšœ!˜!J˜—Jšœ˜—Jšœ˜—šœ ˜Jšœœ˜)Jšœ&œ%˜QJšœœ˜—šœ ˜Jšœœ˜)Jšœ&œ%˜QJšœœ˜—šœ œ ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœœœ˜ —Jšœ˜Jšœ˜—šŸœœœœ ˜/šŸœœœ œ œ œœœ˜iJšœ˜Jšœ˜Jšœ˜šœœ˜&Jšœœ ˜Jšœœ ˜Jšœœœ ˜Jšœ œœ+˜@Jšœœœ˜%šœœ˜'Jšœœœœ˜ Jšœœ˜Jšœœ˜.Jšœœ œ˜;Jšœ˜š˜šœœ˜šœœ˜+Jšœœ˜Jšœ˜Jšœ˜—šœœ˜+Jšœœ˜Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—Jšœœ7œ˜RJšœ˜—šœœœœ˜.Jšœœ˜1Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—šŸ œœœœ ˜7šŸœœœ œ œ œœœ˜iJšœ˜Jšœ˜Jšœ˜š˜Jšœ œ˜Jšœœ˜ Jšœœœ˜%Jšœœ˜$Jšœ˜Jšœ œ)˜9šœ˜Jšœ:˜:Jšœ˜šœœ˜Jšœœ8˜NJšœ˜Jšœ œ)˜9Jšœ˜—Jšœ˜—Jšœœœ˜Jšœœ˜:Jšœ˜Jšœ œ/˜?Jšœœœ˜Jšœ˜šœ˜Jšœœœ˜0Jšœœœ˜0Jšœ œ3œ˜Lšœ˜Jšœ$˜$šœ˜Jš œœœœœ˜*Jšœ˜Jšœ˜Jš œœœœœœ˜EJšœ˜—Jšœœ˜—Jšœ˜—šœœ˜Jšœœ˜/Jšœ!˜!Jšœ!˜!J˜—Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—š Ÿœœœœ œ˜CJšœA˜GJšœ˜—š Ÿœœœœœ ˜AJšœA˜GJšœ˜—šŸœœœœ ˜3Jšœ ˜ šœ œ˜Jšœœœ˜&Jšœœ˜—Jšœ.˜.JšœS˜SJšœ%˜+Jšœ˜—šŸœœœœ œ œœ˜\Jšœ˜Jšœ˜šœ˜Jšœ:˜:Jšœ˜Jšœ˜—Jšœ˜—šŸœœœœœœœ˜gJšœ˜Jšœ˜šœ˜Jšœœ˜!Jšœœ ˜šœ˜Jšœ9˜9Jšœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜—šŸœœœœ œ œœ˜lJšœ˜Jšœ˜Jšœ˜š˜Jšœœ˜ Jš œœœœœ˜UJš œœœœœ˜UJšœ˜šœœ˜(Jšœœœ˜0Jšœœœ˜0Jšœ œ#˜6šœ˜Jšœ$˜$Jšœ$˜$Jšœœ˜—Jšœ˜—Jšœ˜—Jšœ˜—šŸœœœœœœœ˜wJšœ˜Jšœ˜Jšœ˜š˜Jšœ œ˜Jšœœ˜ Jš œœœœœ˜UJš œœœœœ˜UJšœ˜Jšœ œ/˜?šœœ˜(Jšœœœ˜0Jšœœœ˜0Jšœ œ1˜Dšœ˜Jšœ$˜$Jšœ$˜$Jšœœ˜—Jšœ˜—šœœ˜Jšœ!˜!Jšœ!˜!J˜—Jšœ˜—Jšœ˜—š Ÿ œœœœœ˜MJšœ˜Jšœœœ˜š Ÿœœœ œ œœ˜CJšœœ˜Jšœœ˜Jšœœœœ˜TJšœ˜—Jšœ˜Jšœ(˜(Jšœœœ œœ œ œ˜TJšœ˜—Jšœ œ˜+š Ÿ œœœ-œ'œ˜yJšœ ˜ Jšœ œœ˜;Jšœ"˜"šœ œœ˜J™—Jšœœœ˜Jšœœœ˜"Jšœ œœ˜šœœœœœœœœ˜JšœAœ˜X—šœœ œ˜!šœœ˜J˜"J˜;Jšœ˜Jšœœ˜Jšœ œœ˜-Jšœ œœ!˜8Jšœ-œœœ˜TJšœ6˜6Jšœ"˜"JšœM˜MJšœ/˜/Jšœ˜Jšœ:˜:šœ ˜Jšœœ&˜Cšœœœ˜J™ Jšœœœ˜!Jšœœœ˜$Jšœœœ˜$Jšœœœ˜+šœ œ œ˜%Jšœ œ œœ˜QJšœ œ œ˜9Jšœ œœ˜8Jšœ œœ˜7šœ ˜ Jšœ;˜;Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ%˜%Jšœ.˜.Jšœœ*˜GJšœœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœœœ˜HJšœ˜—Jšœ˜—Jšœœ˜—Jšœ˜—šŸ œœœMœ˜nJšœœœ˜3šœ˜JšœE˜EJšœœœ˜?šœ œ˜šœœ˜J˜"J˜;Jšœ˜Jšœœ˜Jšœ œœ˜0Jšœ œœ˜0Jšœ œœ<˜UJšœ œœG˜`Jšœ-œœœ˜UJšœ6˜6Jšœ"˜"Jšœ5˜5Jšœœ ˜ Jšœ˜Jšœ:˜:šœ ˜Jšœœ&˜Cšœœœ˜J™ Jšœœœ˜*Jšœœœ˜-Jšœœœ˜&Jšœœœ˜-šœ œ œ˜%Jšœ œ œœ˜QJšœ œ œ˜9Jšœ œ˜*Jš œ œœœœ˜ešœ ˜ Jšœ;˜;Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœa˜aJšœœ(˜EJšœœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœœœ˜HJšœ˜—Jšœ˜—Jšœœ˜—J˜—Jšœ˜—šŸœœœœ˜9J˜Jšœ!˜'Jšœ˜—šŸ œœœœ ˜)J˜+Jšœ œœ˜2Jšœ˜Jšœ#˜#Jšœ*˜*Jšœœœ˜"Jšœ*œœ˜7Jšœ1˜7Jšœ˜—Jšœ œ˜!šŸœœœ œ˜(Jšœœœ˜1Jšœ˜Jš œ œœœœ˜,Jšœœ ˜&Jšœ˜Jšœ!˜'Jšœ˜—Jšœ˜—…—hî