DIRECTORY BitBlt USING [AlignedBBTable, BBTableSpace, BITBLT, BitBltTablePtr], Environment USING [bitsPerWord], Font USING [FONT], Imager, ImagerBasic, ImagerFontCache, ImagerFrameBuffer USING [LFDisplay], ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, Inline, Real USING [RoundLI], Scaled, TerminalMultiplex USING [SelectTerminal], UserTerminal USING [GetState, SetState]; ImagerLFImpl: CEDAR MONITOR IMPORTS BitBlt, Imager, ImagerFrameBuffer, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, Inline, Real, Scaled, TerminalMultiplex, UserTerminal SHARES Font = BEGIN OPEN ImagerBasic; FONT: TYPE ~ Font.FONT; Mask: TYPE ~ ImagerMasks.Mask; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; Tile: TYPE ~ ImagerPixelMaps.Tile; SpecialColor: TYPE ~ REF ColorRep[special]; Name: TYPE = ImagerPrivate.Name; Context: TYPE = Imager.Context; Data: TYPE = REF DataRep; DataRep: TYPE = RECORD[ canvas: PixelMap, cpx, cpy: REAL _ 0, correctMX, correctMY: REAL _ 0, T: Transformation _ NIL, priorityImportant: INT _ 0, mediumXSize, mediumYSize: REAL _ 0, fieldXMin, fieldYMin, fieldXMax, fieldYMax: REAL _ 0, showVec: REF _ NIL, color: Color _ NIL, noImage: INT _ 0, strokeWidth: REAL _ 0, strokeEnd: INT _ 0, underlineStart: REAL _ 0, amplifySpace: REAL _ 1.0, correctPass: INT _ 0, correctShrink: REAL _ 0.5, correctTX, correctTY: REAL _ 0, correctMaskCount: INT _ 0, correctMaskX, correctMaskY: REAL _ 0, correctSumX, correctSumY: REAL _ 0, correctSpaceX, correctSpaceY: REAL _ 0, correctcpx, correctcpy: REAL _ 0, correctTargetX, correctTargetY: REAL _ 0 ]; LFBrick: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255]; Init: PROC [context: Context, info: REF] ~ { data: Data = NEW[DataRep _ []]; TRUSTED {data.canvas _ ImagerFrameBuffer.LFDisplay[]}; data.tile _ ImagerPixelMaps.CreateTile[ pixelMap: ImagerPixelMaps.Create[lgBitsPerPixel: 0, bounds: [0, 0, 16, 16]]]; data.T _ ImagerOps.Scale[1]; data.color _ Imager.black; context.data _ data; }; Reset: PROC[context: Context] ~ { data: Data _ NARROW[context.data]; data.phase _ 0; data.devicePath _ NIL; TRUSTED {data.canvas _ ImagerFrameBuffer.LFDisplay[]}; data.transformation _ data.viewToDevice _ data.surfaceToDevice _ ImagerTransform.Concat[[1,0,0,0,1,0,hard], ImagerTransform.Create[0, -1, data.canvas.sSize, 1, 0, 0]]; data.clientClipperPresent _ data.compositeClipperValid _ FALSE; data.currentPosition _ [0, 0]; data.viewClipper _ ImagerMasks.Box[ [sMin: 0, fMin: 0, sSize: data.canvas.sSize, fSize: data.canvas.fSize]]; [[data.sOriginView, data.fOriginView]] _ [[data.sOriginBrick, data.fOriginBrick]] _ ImagerTransform.IntTransform[[0,0], data.surfaceToDevice]; SetColor[data, Imager.black]; }; IGet: PROC[context: Context, n: Name] RETURNS[REF] = { data: Data = NARROW[context.data]; FromReal: PROC[x: REF] RETURNS[REAL] = { RETURN[NEW[REAL _ x]] }; FromInt: PROC[x: REF] RETURNS[INT] = { RETURN[NEW[INT _ x]] }; SELECT n FROM $cpx => RETURN[FromReal[data.cpx]]; $cpy => RETURN[FromReal[data.cpy]]; $correctMX => RETURN[FromReal[data.correctMX]]; $correctMY => RETURN[FromReal[data.correctMY]]; $T: Transformation => RETURN[data.T]; $priorityImportant => RETURN[FromInt[data.priorityImportant]]; $mediumXSize => RETURN[FromReal[data.mediumXSize]]; $mediumYSize => RETURN[FromReal[data.mediumYSize]]; $fieldXMin => RETURN[FromReal[data.fieldXMin]]; $fieldYMin => RETURN[FromReal[data.fieldYMin]]; $fieldXMax => RETURN[FromReal[data.fieldXMax]]; $fieldYMax => RETURN[FromReal[data.fieldYMax]]; $showVec => RETURN[data.showVec]; $color => RETURN[data.color]; $noImage => RETURN[FromInt[data.noImage]]; $strokeWidth => RETURN[FromReal[data.strokeWidth]]; $strokeEnd => RETURN[FromInt[data.strokeEnd]]; $underlineStart => RETURN[FromReal[data.underlineStart]]; $amplifySpace => RETURN[FromReal[data.amplifySpace]]; $correctPass => RETURN[FromInt[data.correctPass]]; $correctShrink => RETURN[FromReal[data.correctShrink]]; $correctTX => RETURN[FromReal[data.correctTX]]; $correctTY => RETURN[FromReal[data.correctTY]]; $clipOutline => ERROR; ENDCASE => ERROR; }; ISet: PROC[context: Context, n: Name, x: REF] = { data: Data = NARROW[context.data]; ToReal: PROC[x: REF] RETURNS[REAL] = { RETURN[ WITH x SELECT FROM x: REF REAL => x^, ENDCASE => ERROR] }; ToInt: PROC[x: REF] RETURNS[INT] = { RETURN[ WITH x SELECT FROM x: REF INT => x^, ENDCASE => ERROR] }; SELECT n FROM $cpx => data.cpx _ ToReal[x]; $cpy => data.cpy _ ToReal[x]; $correctMX => data.correctMX _ ToReal[x]; $correctMY => data.correctMY _ ToReal[x]; $T: Transformation => data.T _ NARROW[x]; $priorityImportant => data.priorityImportant _ ToInt[x]; $mediumXSize => data.mediumXSize _ ToReal[x]; $mediumYSize => data.mediumYSize _ ToReal[x]; $fieldXMin => data.fieldXMin _ ToReal[x]; $fieldYMin => data.fieldYMin _ ToReal[x]; $fieldXMax => data.fieldXMax _ ToReal[x]; $fieldYMax => data.fieldYMax _ ToReal[x]; $showVec => data.showVec _ NARROW[x]; $color => data.color _ NARROW[x]; $noImage => data.noImage _ ToInt[x]; $strokeWidth => data.strokeWidth _ ToReal[x]; $strokeEnd => data.strokeEnd _ ToInt[x]; $underlineStart => data.underlineStart _ ToReal[x]; $amplifySpace => data.amplifySpace _ ToReal[x]; $correctPass => data.correctPass _ ToInt[x]; $correctShrink => data.correctShrink _ ToReal[x]; $correctTX => data.correctTX _ ToReal[x]; $correctTY => data.correctTY _ ToReal[x]; $clipOutline => ERROR; ENDCASE => ERROR; }; IGetReal: PROC[context: Context, n: Name] RETURNS[REAL] = { data: Data = NARROW[context.data]; SELECT n FROM $cpx => RETURN[data.cpx]; $cpy => RETURN[data.cpy]; $correctMX => RETURN[data.correctMX]; $correctMY => RETURN[data.correctMY]; $mediumXSize => RETURN[data.mediumXSize]; $mediumYSize => RETURN[data.mediumYSize]; $fieldXMin => RETURN[data.fieldXMin]; $fieldYMin => RETURN[data.fieldYMin]; $fieldXMax => RETURN[data.fieldXMax]; $fieldYMax => RETURN[data.fieldYMax]; $strokeWidth => RETURN[data.strokeWidth]; $underlineStart => RETURN[data.underlineStart]; $amplifySpace => RETURN[data.amplifySpace]; $correctShrink => RETURN[data.correctShrink]; $correctTX => RETURN[data.correctTX]; $correctTY => RETURN[data.correctTY]; ENDCASE => ERROR; }; ISetReal: PROC[context: Context, n: Name, x: REAL] = { data: Data = NARROW[context.data]; SELECT n FROM $cpx => data.cpx _ x; $cpy => data.cpy _ x; $correctMX => data.correctMX _ x; $correctMY => data.correctMY _ x; $mediumXSize => data.mediumXSize _ x; $mediumYSize => data.mediumYSize _ x; $fieldXMin => data.fieldXMin _ x; $fieldYMin => data.fieldYMin _ x; $fieldXMax => data.fieldXMax _ x; $fieldYMax => data.fieldYMax _ x; $strokeWidth => data.strokeWidth _ x; $underlineStart => data.underlineStart _ x; $amplifySpace => data.amplifySpace _ x; $correctShrink => data.correctShrink _ x; $correctTX => data.correctTX _ x; $correctTY => data.correctTY _ x; ENDCASE => ERROR; }; IGetInt: PROC[context: Context, n: Name] RETURNS[INT] = { data: Data = NARROW[context.data]; SELECT n FROM $priorityImportant => RETURN[data.priorityImportant]; $noImage => RETURN[data.noImage]; $strokeEnd => RETURN[data.strokeEnd]; $correctPass => RETURN[data.correctPass]; ENDCASE => ERROR; }; ISetInt: PROC[context: Context, n: Name, x: INT] = { data: Data = NARROW[context.data]; SELECT n FROM $priorityImportant => data.priorityImportant _ x; $noImage => data.noImage _ x; $strokeEnd => data.strokeEnd _ x; $correctPass => data.correctPass _ x; ENDCASE => ERROR; }; InnerDoSave: PROC[data: Data, body: PROC, all: BOOL] = { data: Data = NARROW[context.data]; saved: DataRep; Save: PROC = { saved _ data^; }; Restore: PROC = { IF NOT all THEN { saved.cpx _ data.cpx; saved.cpy _ data.cpy; saved.correctMX _ data.correctMX; saved.correctMY _ data.correctMY; }; data^ _ saved; }; Save[]; body[!UNWIND => Restore[]]; Restore[]; }; DoSave: PROC[context: Context, body: PROC] = { data: Data = NARROW[context.data]; InnerDoSave[data, body, FALSE]; }; DoSaveAll: PROC[context: Context, body: PROC] = { data: Data = NARROW[context.data]; InnerDoSave[data, body, TRUE]; }; ConcatT: PROC [context: Context, m: Transformation] = { data: Data = NARROW[context.data]; data.T _ ImagerOps.Concat[m, data.T]; }; TranslateT: PROC [context: Context, x, y: REAL] ~ { ConcatT[context, ImagerOps.Translate[x, y]]; }; RotateT: PROC [context: Context, degrees: REAL] ~ { ConcatT[context, ImagerOps.Rotate[degrees]]; }; ScaleT: PROC [context: Context, s: REAL] ~ { ConcatT[context, ImagerOps.Scale[s]]; }; Scale2T: PROC [context: Context, sx, sy: REAL] ~ { ConcatT[context, ImagerOps.Scale2[sx, sy]]; }; Move: PROC[context: Context] = { data: Data = NARROW[context.data]; data.T _ ImagerOps.TranslateTo[data.T, [data.cpx, data.cpy]]; }; Trans: PROC[context: Context] = { data: Data = NARROW[context.data]; data.T _ ImagerOps.TranslateTo[data.T, DRound[data.cpx, data.cpy]]; }; SetXY: PROC[context: Context, p: Pair] = { data: Data = NARROW[context.data]; [data.cpx, data.cpy] _ ImagerOps.Transform[data.T, p]; }; IntegerSetXY: PROC[context: Context, x, y: INTEGER] = { SetXY[context, [x, y]]; }; SetXYRel: PROC [context: Context, v: Pair] = { data: Data = NARROW[context.data]; delta: Pair = ImagerOps.TransformVec[data.T, v]; data.cpx _ data.cpx + delta.x; data.cpy _ data.cpy + delta.y; }; IntegerSetXYRel: PROC [context: Context, x, y: INTEGER] = { SetXYRel[context, [x, y]]; }; GetCP: PROC[context: Context] RETURNS[Pair] = { data: Data = NARROW[context.data]; RETURN[ImagerOps.InverseTransform[data.T, [data.cpx, data.cpy]]]; }; GetCPRounded: PROC[context: Context] RETURNS[Pair] = { data: Data = NARROW[context.data]; RETURN[ImagerOps.InverseTransform[data.T, DRound[[data.cpx, data.cpy]]]]; }; inkTile: Tile _ ImagerPixelMaps.TileFromConstant[pixelValue: 1, lgBitsPerPixel: 0]; ApplyMask: PROC[data: Data, mask: Mask] = { clippedMask: Mask = mask.And[CompositeClipper[data]]; IF clippedMask.sSize = 0 THEN RETURN; SELECT data.color FROM Imager.black => ImagerMasks.MaskTile[ data.canvas, clippedMask, inkTile]; Imager.white => ImagerMasks.MaskTile[ data.canvas, clippedMask, inkTile, [and, complement]]; Imager.XOR => ImagerMasks.MaskTile[ data.canvas, clippedMask, inkTile, [xor, null]]; ENDCASE => WITH data.color SELECT FROM sampledColor: SampledColor => { ERROR; }; ENDCASE => { -- constant (other than black or white) or stipple ImagerMasks.MaskTile[data.canvas, clippedMask, data.tile]; }; }; MaskFill: PROC[context: Context, pathMap: PathMapType, pathData: REF] = { data: Data = NARROW[context.data]; ApplyMask[data, MaskFromPath[data, pathMap, pathData]]; }; MakeTileSamples: PROC[xSize, ySize: NAT] RETURNS[tileSamples: SampledColor] ~ { tileSamples _ NEW[ColorRep[sampled]]; tileSamples.transparent _ FALSE; tileSamples.pa _ NEW[PixelArrayRep]; tileSamples.m _ ImagerTransform.Rotate[0]; tileSamples.colorMap _ $Intensity8bpp; tileSamples.pa.xPixels _ xSize; tileSamples.pa.yPixels _ ySize; tileSamples.pa.maxSampleValue _ 255; tileSamples.pa.samplesPerPixel _ 1; tileSamples.pa.get _ ConstantGet; tileSamples.pa.data _ NEW[NAT _ 0]; }; SetTileSamples: PROC[tileSamples: SampledColor, intensity: [0..255]] ~ { WITH tileSamples.pa.data SELECT FROM n: REF NAT => n^ _ intensity; ENDCASE => ERROR; }; ConstantGet: PROC[self: PixelArray, buffer: PixelBuffer, nSamples: NAT, layer: INT, xStart, yStart: Scaled.Value, xDelta, yDelta: Scaled.Value] ~ { WITH self.data SELECT FROM n: REF NAT => { value: NAT _ n^; FOR i: NAT IN [0..nSamples) DO buffer[i] _ value ENDLOOP; }; ENDCASE => ERROR; }; Clipper: TYPE = REF ClipperRep; ClipperRep: TYPE ~ RECORD [ exclude: BOOLEAN, mask: Mask ]; GetClipper: PROC[context: Context] RETURNS[Clipper] ~ { data: Data = NARROW[context.data]; IF data.clientClipperPresent THEN { clipper: Clipper = NEW[ClipperRep]; clipper.exclude _ data.clientClipperExclude; clipper.mask _ data.clientClipper.InlineShift[-data.sOriginView, -data.fOriginView]; ccCount _ ccCount + 1; RETURN [clipper] } ELSE RETURN [NIL] }; SetClipper: PROC[context: Context, clipper: Clipper] ~ { data: Data = NARROW[context.data]; IF clipper = NIL THEN { data.clientClipperPresent _ FALSE; data.clientClipper.refRep _ NIL; ccCount _ ccCount + 1; } ELSE { data.clientClipperPresent _ TRUE; data.clientClipperExclude _ clipper.exclude; data.clientClipper _ clipper.mask.Shift[data.sOriginView, data.fOriginView]; ccCount _ ccCount + 1; }; data.compositeClipperValid _ FALSE; }; MaskFromPath: PROC [data: Data, pathMap: PathMapType, pathData: REF] RETURNS [mask: Mask] = { GenPath: ImagerScanConverter.PathProc = { m: Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.a * p.x + m.b * p.y + m.c, m.d * p.x + m.e * p.y + m.f ]]}; Xmove: PROC [p: Pair] ~ { q: Pair = Xform[p]; move[q.x, q.y] }; Xline: PROC [p: Pair] ~ { q: Pair = Xform[p]; line[q.x, q.y] }; Xcurve: PROC [p1, p2, p3: Pair] ~ { q1: Pair = Xform[p1]; q2: Pair = Xform[p2]; q3: Pair = Xform[p3]; curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y] }; pathMap[pathData, Xmove, Xline, Xcurve, NIL]; }; Runs: PROC[ run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] = { data.devicePath.ConvertToRuns[runProc: run]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath]; mask _ ImagerMasks.Create[Runs]; }; MaskFromStroke: PROC [data: Data, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] RETURNS [mask: Mask] = { Runs: PROC[ run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] = { data.devicePath.ConvertToRuns[runProc: run]; }; data.devicePath _ ImagerStroke.DevicePathFromStroke[ pathClosure: [pathMap, pathData], clientToDevice: data.transformation, width: strokeWidth, strokeEnd: strokeEnd, closed: FALSE, scratch: data.devicePath ]; mask _ ImagerMasks.Create[Runs]; }; MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ { GenPath: ImagerScanConverter.PathProc = { m: Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.a * p.x + m.b * p.y + m.c, m.d * p.x + m.e * p.y + m.f ]]}; { q: Pair = Xform[[area.x, area.y]]; move[q.x, q.y] }; { q: Pair = Xform[[area.x + area.w, area.y]]; line[q.x, q.y] }; { q: Pair = Xform[[area.x + area.w, area.y + area.h]]; line[q.x, q.y] }; { q: Pair = Xform[[area.x, area.y + area.h]]; line[q.x, q.y] }; }; Runs: PROC[ run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] = { data.devicePath.ConvertToRuns[runProc: run]; }; IF data.transformation.type # hard THEN { p1: Pair _ ImagerTransform.Transform[[area.x, area.y], data.transformation]; p2: Pair _ ImagerTransform.Transform[[area.x+area.w, area.y+area.h], data.transformation]; sMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.x, p2.x], -LAST[INTEGER]/2]]; sMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.x, p2.x], LAST[INTEGER]/2]]; fMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.y, p2.y], -LAST[INTEGER]/2]]; fMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.y, p2.y], LAST[INTEGER]/2]]; RETURN [ImagerMasks.InlineBox[ [sMin: sMin, fMin: fMin, sSize: sMax-sMin, fSize: fMax-fMin]]]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath]; mask _ ImagerMasks.Create[Runs]; }; ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; MaskFromPixelArray: PROC[data: Data, pixelArray: PixelArray] RETURNS[Mask] = { destArea: Mask _ MaskFromRectangle[data, [0, 0, pixelArray.xPixels, pixelArray.yPixels]]; Runs: PROC[ run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] = { buffer: PixelBuffer _ NEW[PixelBufferRep[destArea.fSize+1]]; DoRun: PROC[sMin, fMin: INTEGER, fSize: CARDINAL] = { nextPixel: Pair _ ImagerTransform.InverseTransformVec[[0, 1], data.transformation]; start: Pair _ ImagerTransform.InverseTransform[[0.5+sMin, 0.5+fMin], data.transformation]; xStart: Scaled.Value _ ScaledFromReal[start.x]; yStart: Scaled.Value _ ScaledFromReal[start.y]; xDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.x]; yDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.y]; runStart: NAT _ 0; fRel: NAT _ 0; pixelArray.get[pixelArray, buffer, fSize, 0, xStart, yStart, xDeltaPixel, yDeltaPixel]; WHILE fRel < fSize DO buffer[fSize] _ 0; WHILE buffer[fRel] # 0 DO fRel _ fRel + 1 ENDLOOP; IF fRel > runStart THEN {run[sMin, fMin + fRel, fRel - runStart]}; buffer[fSize] _ 1; WHILE buffer[fRel] = 0 DO fRel _ fRel + 1 ENDLOOP; runStart _ fRel; ENDLOOP; }; destArea.MapRuns[DoRun]; }; RETURN [ImagerMasks.Create[Runs]]; }; CompositeClipper: PROC [data: Data] RETURNS [Mask] ~ { IF NOT data.compositeClipperValid THEN { IF data.clientClipperPresent THEN { IF data.clientClipperExclude THEN data.compositeClipper _ data.viewClipper.Difference[data.clientClipper] ELSE data.compositeClipper _ data.viewClipper.And[data.clientClipper]; } ELSE data.compositeClipper _ data.viewClipper; data.compositeClipperValid _ TRUE; }; RETURN [data.compositeClipper] }; ccCount: INT _ 0; ClipMask: PROC [data: Data, mask: Mask, exclude: BOOLEAN _ FALSE] ~ { data.compositeClipperValid _ FALSE; IF NOT data.clientClipperPresent THEN { data.clientClipperPresent _ TRUE; data.clientClipperExclude _ exclude; data.clientClipper _ mask; ccCount _ ccCount + 1; } ELSE IF exclude THEN { IF data.clientClipperExclude THEN { data.clientClipper _ data.clientClipper.Or[mask]; ccCount _ ccCount + 1; } ELSE { data.clientClipper _ data.clientClipper.Difference[mask]; ccCount _ ccCount + 1; }; } ELSE { IF data.clientClipperExclude THEN { data.clientClipper _ mask.Difference[data.clientClipper]; ccCount _ ccCount + 1; data.clientClipperExclude _ FALSE; } ELSE { data.clientClipper _ data.clientClipper.And[mask]; ccCount _ ccCount + 1; }; }; }; ClipOutline: PROC [context: Context, pathMap: PathMapType, pathData: REF] = { data: Data _ NARROW[context.data]; ClipMask[data, MaskFromPath[data, pathMap, pathData]]; }; ClipRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { data: Data = NARROW[context.data]; ClipMask[data, MaskFromRectangle[data, [x, y, w, h]]]; }; IntegerClipRectangle: PROC[context: Context, x, y, w, h: INTEGER] ~ { ClipRectangle[context, x, y, w, h]; }; IntegerExcludeRectangle: PROC[context: Context, x, y, w, h: INTEGER] ~ { data: Data = NARROW[context.data]; ClipMask[data, MaskFromRectangle[data, [x, y, w, h]], TRUE]; }; SetColor: PROC[data: Data, color: Color] ~ { IF color = data.currentColor THEN RETURN; data.currentColor _ color; IF color = Imager.black OR color = Imager.white THEN RETURN; WITH color SELECT FROM constantColor: ConstantColor => { intensity: INT _ constantColor.Y; intensity _ (intensity + 127)/(LAST[CARDINAL]/255); SetTileSamples[data.tileSamples, intensity]; ImagerHalftone.Halftone[ dest: [sOrigin: data.tile.sOrigin, fOrigin: data.tile.fOrigin, sMin: 0, fMin: 0, sSize: data.tile.sSize, fSize: data.tile.fSize, refRep: data.tile.refRep], mask: ImagerMasks.Box[[0, 0, 16, 16]], source: data.tileSamples.pa, transformation: ImagerTransform.Rotate[0], deviceBrick: LFBrick ]; }; specialColor: SpecialColor => { WITH specialColor.ref SELECT FROM stipple: REF CARDINAL => data.tile _ ImagerPixelMaps.TileFromStipple[stipple: stipple^, scratch: data.tile.refRep]; atom: ATOM => { SELECT atom FROM $XOR => NULL; ENDCASE => data.currentColor _ Imager.black; }; ENDCASE => ERROR; }; sampledColor: SampledColor => NULL; ENDCASE => data.currentColor _ Imager.black; }; MaskStroke: PROC[context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd] = { data: Data = NARROW[context.data]; mask: ImagerMasks.Mask _ MaskFromStroke[data, pathMap, pathData, strokeWidth, strokeEnd, FALSE]; ApplyMask[data, mask]; }; MaskStrokeClosed: PROC[context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL] = { data: Data = NARROW[context.data]; mask: ImagerMasks.Mask _ MaskFromStroke[data, pathMap, pathData, strokeWidth, nil, TRUE]; ApplyMask[data, mask]; }; MaskVector: PROC[context: Context, x1, y1, x2, y2: REAL] = { ERROR; }; IntegerMaskVector: PROC[context: Context, x1, y1, x2, y2: INTEGER] = { MaskVector[context, x1, y1, x2, y2]; }; MaskPixel: PROC [context: Context, pa: PixelArray] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPixelArray[data, pa]; ApplyMask[data, mask]; }; scratchPixelArray: PixelArray _ NEW[PixelArrayRep]; scratchBitmapDesc: REF ImagerPixelMaps.PixelMapRep _ NEW[ImagerPixelMaps.PixelMapRep]; MaskBits: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data _ NARROW[context.data]; color: Color _ data.currentColor; IF data.transformation.type = rot90 AND tile.w = 16 AND raster = 1 AND tile.h <= 16 AND (color = Imager.black OR color = Imager.XOR) THEN { sMin, fMin, sSize, fSize: INTEGER; sMinTile, fMinTile, sSizeTile, fSizeTile: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, data.transformation]; [[sMinTile, fMinTile, sSizeTile, fSizeTile]] _ ImagerTransform.TransformIntRectangle[tile, data.transformation]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSizeTile; ImagerMasks.MaskTile[data.canvas, CompositeClipper[data].And[[sMin, fMin, sSize, fSize, NIL]], [sMinTile, fMinTile, sSizeTile, fSizeTile, 0, scratchBitmapDesc], IF color = Imager.black THEN [or, null] ELSE [xor, null]]; } ELSE { IF firstBadMaskBits THEN {firstBadMaskBits _ FALSE; ERROR} -- sorry, not yet. }; }; firstBadMaskBits: BOOLEAN _ TRUE; MaskSegment: PROC [context: Context, p: Pair] ~ { }; MaskIntSegment: PROC [context: Context, p: IntPair] ~ { }; MaskRectangle: PROC[context: Context, x, y, w, h: REAL] = { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, [x, y, w, h]]; ApplyMask[data, mask]; }; IntegerMaskRectangle: PROC[context: Context, x, y, w, h: INTEGER] = { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, [x, y, w, h]]; ApplyMask[data, mask]; }; StartUnderline: PROC[context: Context] = { ERROR; }; MaskUnderline: PROC[context: Context, dy, h: REAL] = { ERROR; }; IntegerMaskUnderline: PROC[context: Context, dy, h: INTEGER] = { MaskUnderline[context, dy, h]; }; rasterToRunGroupStorageRatio: INT _ 1; CharLoadInfo: TYPE = RECORD[sWidth, fWidth: Scaled.Value, mask: Mask, loadRepSize: INT]; MaskAndRunCount: TYPE ~ RECORD [mask: Mask, nRuns: INT]; GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR] RETURNS [ans: MaskAndRunCount] ~ { nRuns: INT _ 0; Runs: PROC[ run: PROC[sMin, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { run[sMin: sMin, fMin: fMin, fSize: fSize]; nRuns _ nRuns + 1; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; nRuns _ 0; ans.mask _ ImagerMasks.Create[Runs]; ans.nRuns _ nRuns; }; FontCompositeTransform: PROC [data: Data, font: FONT] RETURNS [t: Transformation] ~ { t _ data.transformation; t.c _ t.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, t]; }; fontCache: ImagerFontCache.FontCache _ ImagerFontCache.Create[]; FontData: TYPE = REF FontDataRep; FontDataRep: TYPE = RECORD[ ]; LoadCharData: PROC[self: ImagerFontCache.FontObject, charCode: CARDINAL] RETURNS [charData: REF, memoryCost: INT] = { font: FONT = NARROW[self.fontAnyData]; char: CHAR = 0C+charCode; -- does this bounds check properly? transform: Transformation = ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device clientTransform: Transformation = ImagerTransform.Concat[ ImagerTransform.Invert[font.actualTransformation], transform]; loadInfo: REF CharLoadInfo _ NEW[CharLoadInfo]; maskN: MaskAndRunCount _ GetCharMask[font, transform, char]; runGroupSize: INT _ maskN.nRuns * 2 + 2; rasterSize: INT _ maskN.mask.sSize * INT[(maskN.mask.fSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], clientTransform]; loadInfo.mask _ maskN.mask; SELECT TRUE FROM maskN.mask.sSize = 0 OR maskN.mask.fSize = 0 => {loadInfo.loadRepSize _ 0}; maskN.mask.fSize > 32*Environment.bitsPerWord OR runGroupSize*rasterToRunGroupStorageRatio < rasterSize => {loadInfo.loadRepSize _ runGroupSize}; ENDCASE => {loadInfo.loadRepSize _ rasterSize; loadInfo.mask _ loadInfo.mask.Bitmap}; loadInfo.sWidth _ Scaled.FromReal[width.x]; loadInfo.fWidth _ Scaled.FromReal[width.y]; RETURN[loadInfo, 0]; }; ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ { data: Data = NARROW[context.data]; transform: Transformation _ FontCompositeTransform[data, font]; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[[ CharDataProc: LoadCharData, r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f, fontScalarData: 0, -- unused fontAnyData: font]]; sCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.x]; fCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.y]; loadInfo: REF CharLoadInfo = NARROW[ ImagerFontCache.GetCharData[fontCache, fontCode, char-0C]]; sCP: INTEGER _ sCurrent.Round; fCP: INTEGER _ fCurrent.Round; ApplyMask[data, loadInfo.mask.Shift[sCP, fCP]]; sCurrent _ sCurrent.PLUS[loadInfo.sWidth]; fCurrent _ fCurrent.PLUS[loadInfo.fWidth]; data.currentPosition _ [x: sCurrent.Float, y: fCurrent.Float]; }; ShowCharacters: PROC [ context: Context, characters: REF, -- may be a ROPE or a REF TEXT font: FONT, start: INT _ 0, length: INT _ LAST[INT] ] ~ { data: Data = NARROW[context.data]; transform: Transformation _ FontCompositeTransform[data, font]; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[[ CharDataProc: LoadCharData, r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f, fontScalarData: 0, -- unused fontAnyData: font]]; sCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.x]; fCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.y]; DoChar: PROC[charCode: CARDINAL, charData: REF] = { loadInfo: REF CharLoadInfo = NARROW[charData]; sCP: INTEGER _ sCurrent.Round; fCP: INTEGER _ fCurrent.Round; ApplyMask[data, loadInfo.mask.Shift[sCP, fCP]]; sCurrent _ sCurrent.PLUS[loadInfo.sWidth]; fCurrent _ fCurrent.PLUS[loadInfo.fWidth]; }; ImagerFontCache.GetStringData[DoChar, fontCache, fontCode, characters, start, length]; data.currentPosition _ [x: sCurrent.Float, y: fCurrent.Float]; }; CorrectMask: PROC[context: Context] = { ERROR; }; CorrectSpace: PROC[context: Context, v: Pair] = { ERROR; }; SetCorrectMeasure: PROC[context: Context, v: Pair] = { ERROR; }; SetCorrectTolerance: PROC[context: Context, v: Pair] = { ERROR; }; Space: PROC[context: Context, x: REAL] = { ERROR; }; IntegerSpace: PROC[context: Context, x: INTEGER] = { ERROR; }; Correct: PROC[context: Context, body: PROC] = { ERROR; }; firstBadDrawBitmap: BOOLEAN _ TRUE; DrawBitmap: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data _ NARROW[context.data]; IF data.transformation.type = rot90 THEN { sMin, fMin, sSize, fSize: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, data.transformation]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSize; ImagerMasks.MaskTile[data.canvas, ImagerMasks.Box[[sMin, fMin, sSize, fSize]].And[CompositeClipper[data]], [sMin, fMin, sSize, fSize, 0, scratchBitmapDesc]]; } ELSE { IF firstBadDrawBitmap THEN {firstBadDrawBitmap _ FALSE; ERROR} }; }; SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [implemented: BOOLEAN] ~ { clipperData: Data _ NARROW[context.data]; implemented _ TRUE; SELECT op FROM $OtherScreen => { swapped: BOOLEAN; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; TRUSTED {IF UserTerminal.GetState[] = disconnected THEN [] _ UserTerminal.SetState[on]}; TRUSTED {clipperData.canvas _ ImagerFrameBuffer.LFDisplay[]}; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; implemented _ swapped }; ENDCASE => implemented _ FALSE; }; MaskFromSurfaceRectangle: PROC [data: Data, box: IntRectangle] RETURNS [Mask] ~ { deviceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[box, data.surfaceToDevice]; RETURN [ImagerMasks.InlineBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]]; }; SetView: PROC [context: Context, box: IntRectangle, halftoneOrigin: IntPair _ [0, 0]] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromSurfaceRectangle[data, box]; deviceOrigin: IntPair _ ImagerTransform.IntTransform[[box.x, box.y], data.surfaceToDevice]; sOriginView: INTEGER _ deviceOrigin.x; fOriginView: INTEGER _ deviceOrigin.y; sShift: INTEGER _ sOriginView-data.sOriginView; fShift: INTEGER _ fOriginView-data.fOriginView; newHalftoneOrigin: IntPair _ ImagerTransform.IntTransform[halftoneOrigin, data.surfaceToDevice]; IF data.doSaveCount # 0 THEN ERROR; -- Can't change a view with DoSave in progress. IF data.viewClipper = mask AND sShift = 0 AND fShift = 0 AND newHalftoneOrigin = [data.sOriginBrick, data.fOriginBrick] THEN RETURN; data.compositeClipperValid _ FALSE; data.compositeClipper.refRep _ NIL; IF data.clientClipperPresent THEN { data.clientClipper _ data.clientClipper.Shift[sShift, fShift]; ccCount _ ccCount + 1; }; data.viewClipper _ mask; data.currentPosition.x _ data.currentPosition.x + sShift; data.currentPosition.y _ data.currentPosition.y + fShift; data.transformation.c _ data.transformation.c + sShift; data.transformation.f _ data.transformation.f + fShift; data.viewToDevice.c _ data.viewToDevice.c + sShift; data.viewToDevice.f _ data.viewToDevice.f + fShift; data.sOriginView _ sOriginView; data.fOriginView _ fOriginView; [data.sOriginBrick, data.fOriginBrick] _ newHalftoneOrigin; }; ClipView: PROC [context: Context, box: IntRectangle, exclude: BOOLEAN] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromSurfaceRectangle[data, box]; data.compositeClipperValid _ FALSE; data.compositeClipper.refRep _ NIL; data.viewClipper _ IF exclude THEN data.viewClipper.Difference[mask] ELSE data.viewClipper.And[mask]; }; GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data _ NARROW[context.data]; deviceToSurface: Transformation _ ImagerTransform.Invert[data.surfaceToDevice]; RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]] }; GetViewBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data _ NARROW[context.data]; deviceToSurface: Transformation _ ImagerTransform.Invert[data.surfaceToDevice]; RETURN [ImagerTransform.TransformIntRectangle[[data.viewClipper.sMin, data.viewClipper.fMin, data.viewClipper.sSize, data.viewClipper.fSize], deviceToSurface]] }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] RETURNS [BOOLEAN] ~ { data: Data _ NARROW[context.data]; bitmap: REF ImagerPixelMaps.PixelMapRep _ data.canvas.refRep; sSizeCanvas, fSizeCanvas: NAT; sTranslate, fTranslate: INTEGER; sMinSource, fMinSource, sMinDest, fMinDest: INTEGER; sSize, fSize: INTEGER; sSizeCanvas _ MIN[data.canvas.sSize, bitmap.lines]; fSizeCanvas _ MIN[data.canvas.fSize, bitmap.rast*Environment.bitsPerWord]; [[sTranslate, fTranslate]] _ ImagerTransform.TransformIntVec[[dest.x-source.x, dest.y-source.y], data.surfaceToDevice]; [[sMinSource, fMinSource, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[source, data.surfaceToDevice]; sMinSource _ sMinSource - data.canvas.sMin; fMinSource _ fMinSource - data.canvas.fMin; sMinDest _ sMinSource + sTranslate; fMinDest _ fMinSource + fTranslate; IF sMinDest < 0 THEN { d: INTEGER _ - sMinDest; sSize _ sSize - d; sMinSource _ sMinSource + d; sMinDest _ sMinDest + d; }; IF sMinSource < 0 THEN { d: INTEGER _ - sMinSource; sSize _ sSize - d; sMinSource _ sMinSource + d; sMinDest _ sMinDest + d; }; IF fMinDest < 0 THEN { d: INTEGER _ - fMinDest; fSize _ fSize - d; fMinSource _ fMinSource + d; fMinDest _ fMinDest + d; }; IF fMinSource < 0 THEN { d: INTEGER _ - fMinSource; fSize _ fSize - d; fMinSource _ fMinSource + d; fMinDest _ fMinDest + d; }; IF sMinDest + sSize > sSizeCanvas THEN { sSize _ sSizeCanvas - sMinDest }; IF sMinSource + sSize > sSizeCanvas THEN { sSize _ sSizeCanvas - sMinSource }; IF fMinDest + fSize > fSizeCanvas THEN { fSize _ fSizeCanvas - fMinDest }; IF fMinSource + fSize > data.canvas.fMin + fSizeCanvas THEN { fSize _ fSizeCanvas - fMinSource }; IF sSize > 0 AND fSize > 0 THEN TRUSTED { Blt[bitmap.pointer, bitmap.rast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize] }; RETURN [TRUE]; }; Blt: UNSAFE PROC [dbase: LONG POINTER, drast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize: NAT] ~ UNCHECKED INLINE { bpl: INTEGER _ drast * Environment.bitsPerWord; bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; bb^ _ [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]]; IF (fMinSource+fSize)>fMinDest AND (fMinDest+fSize)>fMinSource AND (sMinSource+sSize)>sMinDest AND (sMinDest+sSize)>sMinSource THEN { bb.flags.disjoint _ FALSE; -- the rectangles overlap IF sMinDest=sMinSource THEN bb.flags.disjointItems _ FALSE; -- so do the items IF sMinDest>sMinSource OR (sMinDest=sMinSource AND fMinDest>fMinSource) THEN { -- reverse direction bb.flags.direction _ backward; bpl _ -bpl; sMinSource _ sMinSource + (sSize-1); sMinDest _ sMinDest + (sSize-1); }; }; bb.dst.word _ dbase + Inline.LongMult[sMinDest, drast] + fMinDest/16; bb.dst.bit _ fMinDest MOD 16; bb.src.word _ dbase + Inline.LongMult[sMinSource, drast] + fMinSource/16; bb.src.bit _ fMinSource MOD 16; bb.dstBpl _ bb.srcDesc.srcBpl _ bpl; bb.width _ fSize; bb.height _ sSize; BitBlt.BITBLT[bb]; }; LFDisplayClass: ImagerPrivate.Class _ NEW [ImagerPrivate.ClassRep _ [ deviceType: $LFDisplay, Init: Init, IGet: IGet, ISet: ISet, IGetReal: IGetReal, ISetReal: ISetReal, IGetInt: IGetInt, ISetInt: ISetInt, DoSave: DoSave, DoSaveAll: DoSaveAll, ConcatT: ConcatT, TranslateT: TranslateT, RotateT: RotateT, ScaleT: ScaleT, Scale2T: Scale2T, Move: Move, Trans: Trans, SetXY: SetXY, IntegerSetXY: IntegerSetXY, SetXYRel: SetXYRel, IntegerSetXYRel: IntegerSetXYRel, GetCP: GetCP, GetCPRounded: GetCPRounded, MaskFill: MaskFill, MaskStroke: MaskStroke, MaskStrokeClosed: MaskStrokeClosed, MaskVector: MaskVector, IntegerMaskVector: IntegerMaskVector, MaskRectangle: MaskRectangle, IntegerMaskRectangle: IntegerMaskRectangle, StartUnderline: StartUnderline, MaskUnderline: MaskUnderline, IntegerMaskUnderline: IntegerMaskUnderline, MaskPixel: MaskPixel, ClipOutline: ClipOutline, ClipRectangle: ClipRectangle, IntegerClipRectangle: IntegerClipRectangle, ShowChar: ShowChar, ShowCharacters: ShowCharacters, CorrectMask: CorrectMask, CorrectSpace: CorrectSpace, SetCorrectMeasure: SetCorrectMeasure, SetCorrectTolerance: SetCorrectTolerance, Space: Space, IntegerSpace: IntegerSpace, Correct: Correct, Reset: Reset, SetView: SetView, ClipView: ClipView, DrawBitmap: DrawBitmap, MaskBits: MaskBits, MoveSurfaceRectangle: MoveSurfaceRectangle, IntegerExcludeRectangle: IntegerExcludeRectangle ]]; ImagerPrivate.RegisterDevice[LFDisplayClass]; END. âNewImagerLFImpl.mesa Last edited by: Doug Wyatt, October 6, 1983 10:27 am DataRep: TYPE ~ RECORD [ currentColor: Color, clientClipper: Mask, -- in device coords clientClipperPresent: BOOLEAN, clientClipperExclude: BOOLEAN, compositeClipperValid: BOOLEAN, doSaveCount: [0..1000) _ 0, compositeClipper: Mask, -- in device coords viewClipper: Mask, -- in device coords sOriginView, fOriginView: INTEGER, sOriginBrick, fOriginBrick: INTEGER, currentPosition: Pair, -- in device space transformation: Transformation, viewToDevice: Transformation, surfaceToDevice: Transformation, devicePath: ImagerScanConverter.DevicePath, lineBuffer: Mask, tile: Tile, tileSamples: SampledColor, phase: INTEGER, canvas: PixelMap ]; transform: Transformation _ ImagerTransform.Concat[sampledColor.m, data.viewToDevice]; ImagerHalftone.Halftone[data.canvas, clippedMask, sampledColor.pa, transform, LFBrick]; move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f)  note s comes first for ScanConvert! move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f)  note s comes first for ScanConvert! Because of an apparent bug in Real.FScale. TestRectangle: PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; RETURN [mask.IsVisible[CompositeClipper[data]]]; }; TestIntRectangle: PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ { RETURN [TestRectangle[context, [area.x, area.y, area.w, area.h]]]; }; DoWithoutClipping: PROC [context: Context, bounds: IntRectangle, callBack: PROC[Context]] ~ { callBack[context]; }; Not implemented yet Not implemented yet TransferBuffer: PROC [context, source: Context] ~ { data: Data _ NARROW[context.data]; WITH source.data SELECT FROM sourceData: Data => { ImagerMasks.MaskSampled[data.canvas, CompositeClipper[data], sourceData.canvas.InlineShift[data.sOriginView-sourceData.sOriginView, data.fOriginView-sourceData.fOriginView]]; }; ENDCASE => ERROR Imager.IncompatibleContexts; }; SetColorInvert: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.currentColor _ $XOR; }; Sorry, not yet. Ê)®˜Jšœ™J™™J™$—J˜šÏk ˜ Jšœœ œ˜DJšœ œ˜ Jšœœœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœœ ˜$Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœœ ˜Jšœ˜Jšœœ˜)Jšœ œ˜(—J˜Jšœœ˜JšœÛ˜âJšœ˜ Jšœœœ ˜J˜Jšœœœ˜Jšœœ˜Jšœ œ˜*Jšœœ˜"Jšœœœ˜+Jšœœ˜ J˜Jšœ œ˜J˜Jšœœœ ˜šœ œœ˜J˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜#Jšœ,œ˜5Jšœ œœ˜Jšœœ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜%Jšœœ˜#Jšœœ˜'Jšœœ˜!Jšœ œ˜(J˜J˜—šœ œœ™Jšœ™JšœÏc™(Jšœœ™Jšœœ™Jšœœ™Jšœ™Jšœž™+Jšœž™&Jšœœ™"Jšœœ™$Jšœž™)Jšœœ™Jšœ™Jšœ ™ Jšœ+™+J™Jšœ ™ Jšœ™Jšœœ™Jšœ™Jšœ™—J˜JšœY˜YJ˜šÏnœœœ˜,Jšœ œ˜Jšœ/˜6šœ'˜'JšœM˜M—Jšœœ˜J˜Jšœ˜Jšœ˜—J˜šŸœœ˜!Jšœ œ˜"Jšœ˜Jšœœ˜Jšœ/˜6Jšœ§˜§Jšœ9œ˜?Jšœ˜šœ#˜#JšœH˜H—JšœŽ˜ŽJšœ˜Jšœ˜—J˜šŸœœœœ˜6Jšœ œ˜"JšŸœœœœœœœœ ˜AJšŸœœœœœœœœ ˜>šœ˜ Jšœœ˜#Jšœœ˜#Jšœœ˜/Jšœœ˜/Jšœœœ˜%Jšœœ"˜>Jšœœ˜3Jšœœ˜3Jšœœ˜/Jšœœ˜/Jšœœ˜/Jšœœ˜/Jšœ œ˜!Jšœ œ ˜Jšœ œ˜*Jšœœ˜3Jšœœ˜.Jšœœ ˜9Jšœœ˜5Jšœœ˜2Jšœœ˜7Jšœœ˜/Jšœœ˜/Jšœœ˜Jšœœ˜—J˜—J˜šŸœœœ˜1Jšœ œ˜"š Ÿœœœœœœ˜.Jšœœœœœœœ˜:—š Ÿœœœœœœ˜,Jšœœœœœœœ˜9—šœ˜ Jšœ˜Jšœ˜Jšœ)˜)Jšœ)˜)Jšœœœ˜)Jšœ8˜8Jšœ-˜-Jšœ-˜-Jšœ)˜)Jšœ)˜)Jšœ)˜)Jšœ)˜)Jšœœ˜%Jšœœ˜!Jšœ$˜$Jšœ-˜-Jšœ(˜(Jšœ3˜3Jšœ/˜/Jšœ,˜,Jšœ1˜1Jšœ)˜)Jšœ)˜)Jšœœ˜Jšœœ˜—J˜—J˜šŸœœœœ˜;Jšœ œ˜"šœ˜ Jšœœ ˜Jšœœ ˜Jšœœ˜%Jšœœ˜%Jšœœ˜)Jšœœ˜)Jšœœ˜%Jšœœ˜%Jšœœ˜%Jšœœ˜%Jšœœ˜)Jšœœ˜/Jšœœ˜+Jšœœ˜-Jšœœ˜%Jšœœ˜%Jšœœ˜—J˜—J˜šŸœœœ˜6Jšœ œ˜"šœ˜ Jšœ˜Jšœ˜Jšœ!˜!Jšœ!˜!Jšœ%˜%Jšœ%˜%Jšœ!˜!Jšœ!˜!Jšœ!˜!Jšœ!˜!Jšœ%˜%Jšœ+˜+Jšœ'˜'Jšœ)˜)Jšœ!˜!Jšœ!˜!Jšœœ˜—J˜—J˜šŸœœœœ˜9Jšœ œ˜"šœ˜ Jšœœ˜5Jšœ œ˜!Jšœœ˜%Jšœœ˜)Jšœœ˜—J˜—J˜šŸœœœ˜4Jšœ œ˜"šœ˜ Jšœ1˜1Jšœ˜Jšœ!˜!Jšœ%˜%Jšœœ˜—J˜J˜—šŸ œœœœ˜8Jšœ œ˜"J˜šŸœœ˜J˜J˜—šŸœœ˜šœœœ˜J˜+J˜CJ˜—J˜J˜—Jšœœ˜.Jšœ˜J˜—šŸœœœ˜.Jšœ œ˜"Jšœœ˜Jšœ˜J˜—šŸ œœœ˜1Jšœ œ˜"Jšœœ˜Jšœ˜—J˜šŸœœ*˜7Jšœ œ˜"Jšœ%˜%Jšœ˜J˜—šŸ œœœ˜3J˜,Jšœ˜J˜—šŸœœœ˜3J˜,Jšœ˜J˜—šŸœœœ˜,J˜%Jšœ˜J˜—šŸœœœ˜2Jšœ+˜+Jšœ˜J˜—šŸœœ˜ Jšœ œ˜"Jšœœœ˜=J˜J˜—šŸœœ˜!Jšœ œ˜"Jšœœœ˜CJ˜J˜—šŸœœ˜*Jšœ œ˜"Jšœ0œ˜6Jšœ˜J˜—šŸ œœœ˜7J˜Jšœ˜J˜—šŸœœ ˜.Jšœ œ˜"Jšœ*œ˜0Jšœ˜Jšœ˜Jšœ˜J˜—šŸœœœ˜;J˜Jšœ˜J˜—šŸœœœ ˜/Jšœ œ˜"Jšœ!œ˜AJšœ˜—J˜šŸ œœœ ˜6Jšœ œ˜"Jšœ!œ!˜IJšœ˜—J˜J˜JšœS˜SJ˜šŸ œœ˜+Jšœ5˜5Jšœœœ˜%šœ ˜šœ%˜%Jšœ#˜#—šœ%˜%Jšœ6˜6—šœœ˜#Jšœ0˜0—šœœ œ˜&šœ˜JšœV™VJšœW™WJšœ˜Jšœ˜—šœž2˜?Jšœ:˜:Jšœ˜———Jšœ˜J˜—šŸœœ3œ˜IJšœ œ˜"Jšœ7˜7Jšœ˜—J˜J˜J˜J˜šŸœœœœ˜OJšœœ˜%Jšœœ˜ Jšœœ˜$Jšœ*˜*Jšœ&˜&Jšœ˜Jšœ˜Jšœ$˜$Jšœ#˜#Jšœ!˜!Jšœœœ˜#Jšœ˜—J˜šŸœœ4˜Hšœœ˜$Jšœœœ˜Jšœœ˜—Jšœ˜—J˜šŸ œœ2œ œA˜“šœ œ˜šœœœ˜Jšœœ˜Jš œœœœœ˜9Jšœ˜—Jšœœ˜—Jšœ˜—J˜J˜J˜Jšœ œœ ˜šœ œœ˜Jšœ œ˜J˜ Jšœ˜—J˜šŸ œœœ ˜7Jšœ œ˜"šœœ˜#Jšœœ ˜#Jšœ,˜,JšœT˜TJ˜Jšœ ˜Jšœ˜—Jšœœœ˜Jšœ˜—J˜šŸ œœ(˜8Jšœ œ˜"šœ œœ˜Jšœœ˜"Jšœœ˜ J˜Jšœ˜—šœ˜Jšœœ˜!Jšœ,˜,JšœL˜LJ˜Jšœ˜—Jšœœ˜#Jšœ˜—J˜J˜šŸ œœ.œ˜DJšœ˜šœ%˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜Jšœ(˜(šŸœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—JšŸœœ3˜?JšŸœœ3˜?šŸœœ˜#JšœA˜AJšœ,˜,—Jšœ(œ˜-Jšœ˜—šŸœœ˜ Jšœœ œ œ˜+Jšœœœ˜-Jšœ,˜,Jšœ˜—Jšœ^˜^Jšœ ˜ Jšœ˜J˜—šŸœœ.œ˜FJšœ œ œ˜6Jšœ˜šŸœœ˜ Jšœœ œ œ˜+Jšœœœ˜-Jšœ,˜,Jšœ˜—šœ4˜4Jšœ!˜!Jšœ$˜$Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Jšœ ˜ Jšœ˜—J˜šŸœœœ˜Nšœ%˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜Jšœ(˜(šŸœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšœ6˜6Jšœ?˜?JšœH˜HJšœ?˜?Jšœ˜—šŸœœ˜ Jšœœ œ œ˜+Jšœœœ˜-Jšœ,˜,Jšœ˜—šœ!œ˜)JšœL˜LJšœZ˜ZJš œœœœœœ˜EJš œœœœœœ˜DJš œœœœœœ˜EJš œœœœœœ˜Dšœ˜Jšœ?˜?—Jšœ˜—Jšœ^˜^Jšœ ˜ Jšœ˜—J˜š Ÿœœœœœ˜DJ™*Jšœœ ˜&Jšœœ˜Jšœ˜—J˜šŸœœ%œ ˜NJšœY˜YšŸœœ˜ Jšœœ œ œ˜+Jšœœœ˜-Jšœœ#˜<šŸœœ œ œ˜5JšœS˜SJšœZ˜ZJšœ/˜/Jšœ/˜/Jšœ8˜8Jšœ8˜8Jšœ œ˜Jšœœ˜JšœW˜Wšœ˜Jšœ˜Jšœœœ˜2Jšœœ+˜BJšœ˜Jšœœœ˜2Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜"Jšœ˜—J˜šŸœœœ ˜6šœœœ˜(šœœ˜#JšœœH˜iJšœB˜FJšœ˜—Jšœ*˜.Jšœœ˜"Jšœ˜—Jšœ˜Jšœ˜—J˜Jšœ œ˜J˜šŸœœ#œœ˜EJšœœ˜#šœœœ˜'Jšœœ˜!Jšœ$˜$Jšœ˜J˜Jšœ˜—šœœ œ˜šœœ˜#Jšœ1˜1J˜Jšœ˜—šœ˜Jšœ9˜9J˜J˜—Jšœ˜—šœ˜šœœ˜#Jšœ9˜9J˜Jšœœ˜"Jšœ˜—šœ˜Jšœ2˜2J˜J˜—Jšœ˜—Jšœ˜—J˜šŸ œœ4œ˜MJšœ œ˜"Jšœ6˜6J˜—J˜šŸ œœ œ˜˜>—Jšœ œœ˜/Jšœ<˜˜>Jšœ˜—J˜šŸœœ˜Jšœ˜Jšœ œž˜/Jšœœ˜ Jšœœ˜Jšœœœœ˜Jšœ˜Jšœ œ˜"Jšœ?˜?šœB˜BJšœ˜Jšœe˜eJšœž ˜Jšœ˜—Jšœ@˜@Jšœ@˜@šŸœœ œ œ˜3Jšœ œœ ˜.Jšœœ˜Jšœœ˜Jšœ/˜/Jšœœ˜*Jšœœ˜*J˜—J˜VJšœ>˜>Jšœ˜—J˜šŸ œœ˜'Jšœ˜J˜J˜—šŸ œœ˜1Jšœ˜J˜J˜—šŸœœ˜6Jšœ˜J˜J˜—šŸœœ˜8Jšœ˜J˜J˜—šŸœœœ˜*Jšœ˜J˜J˜—šŸ œœœ˜4Jšœ˜J˜J˜—šŸœœœ˜/Jšœ˜J˜J˜—J˜šŸœœ™3Jšœ œ™"šœ œ™šœ™Jšœ®™®Jšœ™—Jšœœ™-—Jšœ™—J˜šŸœœ™+Jšœ œ™"Jšœ™Jšœ™—J˜Jšœœœ˜#J˜š Ÿ œœœœœ œ˜gJšœœœ˜Jšœ œ˜"šœ"œ˜*Jšœœ˜"Jšœ`˜`Jšœ!˜!Jšœ ˜ Jšœ ˜ šœ!˜!JšœH˜HJšœ2˜2—Jšœ˜—šœ˜šœœœœ˜>J™—Jšœ˜—Jšœ˜—J˜š Ÿ œœœœœœ˜ZJšœœ˜)Jšœœ˜šœ˜šœ˜Jšœ œ˜Jšœ4˜;Jšœœ(œ!˜XJšœ6˜=Jšœ4˜;Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—J˜šŸœœ!œ ˜QJšœ[˜[JšœO˜UJšœ˜—J˜šŸœœL˜YJšœ œ˜"Jšœ1˜1Jšœ[˜[Jšœ œ˜&Jšœ œ˜&Jšœœ ˜/Jšœœ ˜/Jšœ`˜`Jšœœœž/˜SJš œœ œ œ<œœ˜„Jšœœ˜#Jšœœ˜#šœœ˜#Jšœ>˜>J˜J˜—Jšœ˜Jšœ9˜9Jšœ9˜9Jšœ7˜7Jšœ7˜7Jšœ3˜3Jšœ3˜3Jšœ˜Jšœ˜Jšœ;˜;Jšœ˜—J˜šŸœœ0œ˜JJšœ œ˜"Jšœ1˜1Jšœœ˜#Jšœœ˜#šœ˜Jšœ œ"˜1Jšœ˜ —Jšœ˜—J˜šŸœœœ˜DJšœ œ˜"JšœO˜OJšœ…˜‹Jšœ˜—J˜šŸ œœœ˜AJšœ œ˜"JšœO˜OJšœ™˜ŸJšœ˜—J˜šŸœœ9œœ˜hJšœ œ˜"Jšœœ2˜=Jšœœ˜Jšœœ˜ Jšœ,œ˜4Jšœœ˜Jšœœ"˜3Jšœœ9˜JJšœw˜wJšœo˜oJšœ+˜+Jšœ+˜+Jšœ#˜#Jšœ#˜#šœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœ œ˜(Jšœ˜Jšœ˜—šœ"œ˜*Jšœ ˜ Jšœ˜—šœ œ˜(Jšœ˜Jšœ˜—šœ5œ˜=Jšœ ˜ Jšœ˜—šœ œ œœ˜)Jšœ[˜[Jšœ˜—Jšœœ˜Jšœ˜—J˜šŸœœœ œœCœ œœ˜Jšœœ#˜/Jšœ˜Jšœ<˜