DIRECTORY BitBlt, Environment, Imager, ImagerBasic, ImagerFontCache, ImagerHalftone, ImagerInline, ImagerMasks, ImagerRegistration, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UnifiedFonts, UserTerminal; ImagerLFImpl: CEDAR PROGRAM IMPORTS BitBlt, Imager, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerRegistration, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UserTerminal SHARES UnifiedFonts ~ BEGIN stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = { loadRunGroups _ stats.loadRunGroups; stats.loadRunGroups _ 0; loadRasters _ stats.loadRasters; stats.loadRasters _ 0; rasterChars _ stats.rasterChars; stats.rasterChars _ 0; runGroupChars _ stats.runGroupChars; stats.runGroupChars _ 0; clippedChars _ stats.clippedChars; stats.clippedChars _ 0; culledChars _ stats.culledChars; stats.culledChars _ 0; }; ROPE: TYPE ~ Rope.ROPE; FONT: TYPE ~ UnifiedFonts.FONT; Mask: TYPE ~ ImagerMasks.Mask; Rectangle: TYPE ~ Imager.Rectangle; IntRectangle: TYPE ~ Imager.IntRectangle; Pair: TYPE ~ Imager.Pair; IntPair: TYPE ~ Imager.IntPair; Context: TYPE ~ Imager.Context; Transformation: TYPE ~ Imager.Transformation; Clipper: TYPE ~ Imager.Clipper; Visibility: TYPE ~ Imager.Visibility; Font: TYPE ~ Imager.Font; Path: TYPE ~ Imager.Path; StrokeEnds: TYPE ~ Imager.StrokeEnds; Color: TYPE ~ Imager.Color; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ currentColor: Color, currentFont: FONT, 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: ImagerBasic.Pair, transformation: Transformation, viewToDevice: Transformation, surfaceToDevice: Transformation, devicePath: ScanConverter.DevicePath, fontCache: ImagerFontCache.FontCache, lineBuffer: Mask, tile: Mask, tileSamples: ImagerBasic.SampledColor, phase: INTEGER, canvas: Mask ]; LFBrick: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255]; Init: PROC [context: Context, name: ROPE] ~ TRUSTED { data: Data ~ NEW[DataRep]; context.data _ data; data.tile _ ImagerMasks.NewBitmap[0, 0, 16, 16]; data.tileSamples _ MakeTileSamples[16, 16]; data.phase _ 0; data.currentFont _ NIL; data.devicePath _ ScanConverter.Allocate[]; data.fontCache _ ImagerFontCache.Create[]; data.canvas _ ImagerMasks.LFDisplay[]; data.transformation _ data.viewToDevice _ data.surfaceToDevice _ 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[context, Imager.black]; }; MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: ImagerBasic.SampledColor] ~ { tileSamples _ NEW[ImagerBasic.SampledColorRep]; tileSamples.transparent _ FALSE; tileSamples.pa _ NEW[ImagerBasic.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: ImagerBasic.SampledColor, intensity: [0..255]] ~ { WITH tileSamples.pa.data SELECT FROM n: REF NAT => n^ _ intensity; ENDCASE => ERROR; }; ConstantGet: PROC [self: ImagerBasic.PixelArray, buffer: ImagerBasic.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; }; DoSaveAll: PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; cp: Pair _ data.currentPosition; DoSave[context, action]; data.currentPosition _ cp; }; DoSave: PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; color: Color _ data.currentColor; font: Font _ data.currentFont; clipper: Clipper _ GetClipper[context]; transformation: Transformation _ data.transformation; data.doSaveCount _ data.doSaveCount + 1; action[]; data.doSaveCount _ data.doSaveCount - 1; data.transformation _ transformation; context.SetClipper[clipper]; data.currentFont _ font; IF data.currentColor # color THEN SetColor[context, color]; }; Flush: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; }; Close: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; }; TranslateT: PROC [context: Context, dx, dy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreTranslate[dx, dy, data.transformation]; }; RotateT: PROC [context: Context, degrees: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreRotate[degrees, data.transformation]; }; ScaleT: PROC [context: Context, sx, sy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreScale[sx, sy, data.transformation]; }; ConcatT: PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.transformation]; }; IntTranslateT: PROC [context: Context, dx, dy: INTEGER] ~ {TranslateT[context, dx, dy]}; ClipperRep: TYPE ~ RECORD [ exclude: BOOLEAN, mask: Mask ]; GetClipper: PROC [context: Context] RETURNS [Clipper] ~ { data: Data _ NARROW[context.data]; IF data.clientClipperPresent THEN { clipper: REF ClipperRep _ 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 { clipperRef: REF ClipperRep _ NARROW[clipper]; data.clientClipperPresent _ TRUE; data.clientClipperExclude _ clipperRef.exclude; data.clientClipper _ clipperRef.mask.Shift[data.sOriginView, data.fOriginView]; ccCount _ ccCount + 1; }; data.compositeClipperValid _ FALSE; }; GetT: PROC [context: Context] RETURNS [Transformation] ~ { data: Data _ NARROW[context.data]; RETURN [ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]] }; SetT: PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.viewToDevice]; }; MaskFromPath: PROC [data: Data, outline: Path] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; Xmove: PROC [p: Pair] ~ {move[Xform[p]]}; Xline: PROC [p: Pair] ~ {line[Xform[p]]}; Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]}; outline.generateProc[outline, Xmove, Xline, Xcurve]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter]; }; data.devicePath.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; move[Xform[[area.x, area.y]]]; line[Xform[[area.x + area.w, area.y]]]; line[Xform[[area.x + area.w, area.y + area.h]]]; line[Xform[[area.x, area.y + area.h]]]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter]; }; 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.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; MaskFromPixelArray: PROC [data: Data, pixelArray: ImagerBasic.PixelArray] RETURNS [Mask] ~ { destArea: Mask _ MaskFromRectangle[data, [0, 0, pixelArray.xPixels, pixelArray.yPixels]]; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { buffer: ImagerBasic.PixelBuffer _ NEW[ImagerBasic.PixelBufferRep[destArea.FSize+1]]; DoRun: PROC [s, fMin: INTEGER, fSize: CARDINAL] ~ { nextPixel: ImagerBasic.Pair _ ImagerTransform.InverseTransformVec[[0, 1], data.transformation]; start: ImagerBasic.Pair _ ImagerTransform.InverseTransform[[0.5+s, 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[s, 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] ~ { 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; }; }; }; ClipPath: PROC [context: Context, outline: Path, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; ClipMask[data, MaskFromPath[data, outline], exclude]; }; ClipRectangle: PROC [context: Context, outline: Rectangle, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; ClipMask[data, MaskFromRectangle[data, outline], exclude]; }; TestRectangle: PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; RETURN [mask.IsVisible[CompositeClipper[data]]]; }; ClipIntRectangle: PROC [context: Context, outline: IntRectangle, exclude: BOOLEAN _ FALSE] ~ { ClipRectangle[context, [outline.x, outline.y, outline.w, outline.h], exclude]; }; 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]; }; SetFont: PROC [context: Context, font: Font] ~ { data: Data _ NARROW[context.data]; data.currentFont _ font; }; GetFont: PROC [context: Context] RETURNS [font: Font] ~ { data: Data _ NARROW[context.data]; font _ data.currentFont; }; GetColor: PROC [context: Context] RETURNS [color: Color] ~ { data: Data _ NARROW[context.data]; color _ data.currentColor; }; SetColor: PROC [context: Context, color: Color] ~ { data: Data _ NARROW[context.data]; IF color = data.currentColor THEN RETURN; data.currentColor _ color; IF color = Imager.black OR color = Imager.white THEN RETURN; WITH color SELECT FROM constantColor: ImagerBasic.CIEColor => { intensity: INT _ constantColor.Y; intensity _ (intensity + 127)/(LAST[CARDINAL]/255); SetTileSamples[data.tileSamples, intensity]; ImagerHalftone.Halftone[ dest: data.tile, mask: ImagerMasks.Box[0, 0, 16, 16], source: data.tileSamples.pa, transformation: ImagerTransform.Rotate[0], deviceBrick: LFBrick ]; }; sampledColor: ImagerBasic.SampledColor => NULL; ENDCASE => SetColor[context, Imager.black]; }; MaskStroke: PROC [context: Context, path: Path, width: REAL, strokeEnds: StrokeEnds] ~ { }; ApplyMask: PROC [data: Data, mask: Mask] ~ { clippedMask: Mask _ mask.And[CompositeClipper[data]]; IF clippedMask.sSize = 0 THEN RETURN; SELECT data.currentColor FROM Imager.black => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase]; Imager.white => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase, [and, complement]]; ENDCASE => WITH data.currentColor SELECT FROM constantColor: ImagerBasic.CIEColor => { data.canvas.MaskConstant[clippedMask, data.tile, data.phase]; }; sampledColor: ImagerBasic.SampledColor => { transform: Transformation _ ImagerTransform.Concat[sampledColor.m, data.viewToDevice]; ImagerHalftone.Halftone[data.canvas, clippedMask, sampledColor.pa, transform, LFBrick]; }; ENDCASE => ERROR; }; MaskFill: PROC [context: Context, path: Path] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPath[data, path]; ApplyMask[data, mask]; }; MaskPixel: PROC [context: Context, pixelArray: ImagerBasic.PixelArray] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPixelArray[data, pixelArray]; ApplyMask[data, mask]; }; MaskSegment: PROC [context: Context, p: Pair] ~ { }; MaskThinStroke: PROC [context: Context, path: Path] ~ { }; MaskRectangle: PROC [context: Context, area: Rectangle] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; ApplyMask[data, mask]; }; MaskIntRectangle: PROC [context: Context, area: IntRectangle] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, [area.x, area.y, area.w, area.h]]; ApplyMask[data, mask]; }; SetCP: PROC [context: Context, cp: Pair] ~ { data: Data _ NARROW[context.data]; data.currentPosition _ ImagerTransform.Transform[cp, data.transformation]; }; GetCP: PROC [context: Context] RETURNS [cp: Pair] ~ { data: Data _ NARROW[context.data]; cp _ ImagerTransform.InverseTransform[data.currentPosition, data.transformation]; }; SetIntCP: PROC [context: Context, cp: IntPair] ~ { SetCP[context, [cp.x, cp.y]]; }; GetIntCP: PROC [context: Context] RETURNS [cp: IntPair] ~ { realCP: Pair _ GetCP[context]; cp.x _ Real.RoundLI[realCP.x]; cp.y _ Real.RoundLI[realCP.y]; }; 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[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { Run: PROC [s, fMin: INTEGER, fSize: NAT] ~ { run[s: s, 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] RETURNS [t: Transformation] ~ { t _ data.transformation; t.c _ t.f _ 0; t _ ImagerTransform.Concat[data.currentFont.actualTransformation, t]; }; MaskChars: PROC [context: Context, map: PROC[PROC[CHAR]]] ~ { data: Data _ NARROW[context.data]; fontCache: ImagerFontCache.FontCache _ data.fontCache; font: FONT _ data.currentFont; transform: Transformation _ FontCompositeTransform[data]; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f, font.graphicsKey]; clipper: Mask _ CompositeClipper[data]; sCurrent: Scaled.Value _ Scaled.FromReal[data.currentPosition.x]; fCurrent: Scaled.Value _ Scaled.FromReal[data.currentPosition.y]; Load: PROC [char: CHAR] = { 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], data.transformation]; 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]; fontCache.LoadCharData[fontCode, char, loadInfo]; }; DoChar: PROC [char: CHAR] = { sCP: INTEGER _ sCurrent.Round; fCP: INTEGER _ fCurrent.Round; loadInfo: REF CharLoadInfo _ LoadInfo[]; LoadInfo: PROC RETURNS [cli: REF CharLoadInfo] ~ INLINE { cli _ NARROW[ fontCache.GetCharData[fontCode, char ! ImagerFontCache.CacheMiss => {Load[char]; RETRY} ] ]; }; ApplyMask[data, loadInfo.mask.Shift[sCP, fCP]]; sCurrent _ sCurrent.PLUS[loadInfo.sWidth]; fCurrent _ fCurrent.PLUS[loadInfo.fWidth]; }; map[DoChar]; data.currentPosition _ [x: sCurrent.Float, y: fCurrent.Float]; }; MaskChar: PROC [context: Context, char: CHAR] ~ { MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]}; MaskChars[context, MapOneChar]; }; MaskCharacters: PROC [ context: Context, characters: REF, -- may be a Rope.ROPE or a REF TEXT start: INT _ 0, length: INT _ LAST[INT] ] ~ { WITH characters SELECT FROM rope: ROPE => { Map: PROC [Char: PROC[CHAR]] = { Action: PROC [c: CHAR] RETURNS [quit: BOOL _ FALSE] = {Char[c]}; [] _ rope.Map[start: start, len: length, action: Action]; }; MaskChars[context, Map] }; text: REF TEXT => { size: INT _ text.length; rem: INT _ RopeInline.NonNeg[size - RopeInline.NonNeg[start]]; Map: PROC [Char: PROC[CHAR]] = { FOR i: INT IN [start..start+length) DO Char[text[i]]; ENDLOOP; }; IF length > rem THEN length _ rem; MaskChars[context, Map]; }; ENDCASE => IF characters # NIL THEN ERROR; }; CreateBuffer: PROC [context: Context, box: IntRectangle] RETURNS [Context] ~ { RETURN [NIL] }; TransferBuffer: PROC [context, source: Context] ~ { ERROR Imager.NonConformingBuffers; }; 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]}; clipperData.canvas _ ImagerMasks.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; IF data.doSaveCount # 0 THEN ERROR; -- Can't change a view with DoSave in progress. 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]] _ ImagerTransform.IntTransform[halftoneOrigin, data.surfaceToDevice]; }; 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]] }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] RETURNS [BOOLEAN] ~ { data: Data _ NARROW[context.data]; bitmap: REF ImagerMasks.MaskRep.bitmap; sSizeCanvas, fSizeCanvas: NAT; sTranslate, fTranslate: INTEGER; sMinSource, fMinSource, sMinDest, fMinDest: INTEGER; sSize, fSize: INTEGER; WITH data.canvas.refRep SELECT FROM b: REF ImagerMasks.MaskRep.bitmap => bitmap _ b; ENDCASE => ERROR; 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: REF ImagerInline.ClassRep _ NEW [ImagerInline.ClassRep _ [ deviceType: $LFDisplay, Init: Init, DoSaveAll: DoSaveAll, DoSave: DoSave, Flush: Flush, Close: Close, TranslateT: TranslateT, RotateT: RotateT, ScaleT: ScaleT, ConcatT: ConcatT, GetT: GetT, SetT: SetT, IntTranslateT: IntTranslateT, GetClipper: GetClipper, SetClipper: SetClipper, ClipPath: ClipPath, ClipRectangle: ClipRectangle, TestRectangle: TestRectangle, ClipIntRectangle: ClipIntRectangle, TestIntRectangle: TestIntRectangle, DoWithoutClipping: DoWithoutClipping, SetFont: SetFont, GetFont: GetFont, SetColor: SetColor, GetColor: GetColor, MaskStroke: MaskStroke, MaskFill: MaskFill, MaskPixel: MaskPixel, MaskSegment: MaskSegment, MaskThinStroke: MaskThinStroke, MaskRectangle: MaskRectangle, MaskIntRectangle: MaskIntRectangle, SetCP: SetCP, GetCP: GetCP, SetIntCP: SetIntCP, GetIntCP: GetIntCP, MaskChar: MaskChar, MaskCharacters: MaskCharacters, CreateBuffer: CreateBuffer, TransferBuffer: TransferBuffer, SetView: SetView, ClipView: ClipView, GetSurfaceBounds: GetSurfaceBounds, MoveSurfaceRectangle: MoveSurfaceRectangle, SpecialOp: SpecialOp ]]; ImagerRegistration.RegisterDevice[LFDisplayClass]; END. jImagerLFImpl.mesa Michael Plass, July 21, 1983 2:43 pm in device space Transforms (x, y) to (f, s)  note f comes first for ScanConvert! Transforms (x, y) to (f, s)  note f comes first for ScanConvert! Because of an apparent bug in Real.FScale. Not implemented yet Not implemented yet Not implemented yet Not implemented yet Not implemented yet Ê÷˜Jšœ™J™$J˜JšÏk œñ˜úJ˜šœ ˜Jšœº˜ÁJšœ ˜—Jšœ˜JšœœUœ˜kšÏnœœœUœ˜pJšœ=˜=Jšœ7˜7Jšœ7˜7Jšœ=˜=Jšœ:˜:Jšœ7˜7Jšœ˜—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˜Yšžœœœœ˜5Jšœ œ ˜Jšœ˜Jšœ0˜0Jšœ+˜+Jšœ˜Jšœœ˜Jšœ+˜+Jšœ*˜*Jšœ&˜&Jšœ{˜{Jšœ9œ˜?Jšœ˜Jšœi˜iJšœŽ˜ŽJšœ ˜ Jšœ˜—šžœœœœ,˜]Jšœœ˜/Jšœœ˜ Jšœœ˜0Jšœ*˜*Jšœ&˜&Jšœ˜Jšœ˜Jšœ$˜$Jšœ#˜#Jšœ!˜!Jšœœœ˜#Jšœ˜—šžœœA˜Ušœœ˜$Jšœœœ˜Jšœœ˜—Jšœ˜—šž œœKœ œA˜¬šœ œ˜šœœœ˜Jšœœ˜Jš œœœœœ˜9Jšœ˜—Jšœœ˜—Jšœ˜—šž œœœ˜4Jšœ œ˜"Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—šžœœœ˜1Jšœ œ˜"Jšœ!˜!Jšœœ ˜Jšœ'˜'Jšœ5˜5Jšœ(˜(Jšœ ˜ Jšœ(˜(Jšœ%˜%Jšœ˜Jšœ˜Jšœœ˜;Jšœ˜—šžœœ˜"Jšœ œ˜"J˜Jšœ˜—šžœœ˜"Jšœ œ˜"Jšœ˜—šž œœœ˜5Jšœ œ˜"JšœP˜PJšœ˜—šžœœœ˜3Jšœ œ˜"JšœN˜NJšœ˜—šžœœœ˜1Jšœ œ˜"JšœL˜LJšœ˜—šžœœ7˜DJšœ œ˜"JšœR˜RJšœ˜—Jšž œœœ"˜Xšœ œœ˜Jšœ œ˜J˜ Jšœ˜—šž œœœ˜9Jšœ œ˜"šœœ˜#Jšœ œœ ˜*Jšœ,˜,JšœT˜TJ˜Jšœ ˜Jšœ˜—Jšœœœ˜Jšœ˜—šž œœ)˜9Jšœ œ˜"šœ œœ˜Jšœœ˜"Jšœœ˜ J˜Jšœ˜—šœ˜Jšœ œœ ˜-Jšœœ˜!Jšœ/˜/JšœO˜OJ˜Jšœ˜—Jšœœ˜#Jšœ˜—šžœœœ˜:Jšœ œ˜"JšœY˜_Jšœ˜—šžœœ7˜AJšœ œ˜"JšœP˜PJšœ˜—šž œœœ˜Gš žœœœœœ˜UJšœ4˜4šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšžœœ˜)Jšžœœ˜)Jšžœœ>˜KJšœ4˜4Jšœ˜—šžœœœ œ œ œœ˜ašžœœœ˜4Jšœœœ˜Jšœ˜Jšœ˜—Jšœ8˜8Jšœ˜—Jšœ˜Jšœ"˜"Jšœ ˜ Jšœ˜Jšœ˜—šžœœœ˜Nš žœœœœœ˜UJšœ4˜4šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ'˜'Jšœ0˜0Jšœ'˜'Jšœ˜—šžœœœ œ œ œœ˜ašžœœœ˜4Jšœœœ˜Jšœ˜Jšœ˜—Jšœ8˜8Jšœ˜—šœ!œ˜)JšœL˜LJšœZ˜ZJš œœœœœœ˜EJš œœœœœœ˜DJš œœœœœœ˜EJš œœœœœœ˜DJšœU˜[Jšœ˜—Jšœ˜Jšœ"˜"Jšœ ˜ Jšœ˜Jšœ˜—š žœœœœœ˜DJ™*Jšœœ ˜&Jšœœ˜Jšœ˜—šžœœ2œ ˜\JšœY˜Yšžœœœ œ œ œœ˜aJšœ"œ/˜Tšžœœ œ œ˜3Jšœ_˜_Jšœc˜cJšœ/˜/Jšœ/˜/Jšœ8˜8Jšœ8˜8Jšœ œ˜Jšœœ˜JšœW˜Wšœ˜Jšœ˜Jšœœœ˜2Jšœœ(˜?Jšœ˜Jšœœœ˜2Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜"Jšœ˜—šžœœœ ˜6šœœœ˜(šœœ˜#JšœœH˜iJšœB˜FJšœ˜—Jšœ*˜.Jšœœ˜"Jšœ˜—Jšœ˜Jšœ˜—Jšœ œ˜šžœœ#œ˜=Jšœœ˜#šœœœ˜'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šœ˜—šžœœ,œœ˜NJšœ œ˜"Jšœ5˜5J˜—šž œœ1œœ˜XJšœ œ˜"Jšœ:˜:Jšœ˜—šž œœ%œ˜PJšœ œ˜"J˜+Jšœ*˜0Jšœ˜—šžœœ4œœ˜^JšœN˜NJšœ˜—šžœœ(œ˜VJšœ<˜BJšœ˜—šžœœ4œ˜]Jšœ˜Jšœ˜—šžœœ#˜0Jšœ œ˜"Jšœ˜Jšœ˜—šžœœœ˜9Jšœ œ˜"Jšœ˜Jšœ˜—šžœœœ˜˜>Jšœ˜—šžœœœ˜1Jšž œœœœ˜3Jšœ˜Jšœ˜—šžœœ˜Jšœ˜Jšœ œŸ#˜4Jšœœ˜Jšœœœœ˜šœ˜šœ œ˜šœœ˜š žœœžœœœ˜ Jš žœœœœœœ˜@Jšœ9˜9J˜—Jšœ˜Jšœ˜—šœœœ˜Jšœœ˜Jšœœ6˜>š žœœžœœœ˜ šœœœ˜&Jšœ˜Jšœ˜—Jšœ˜—Jšœœ˜"Jšœ˜Jšœ˜—Jš œœœœœ˜*—Jšœ˜——J˜šž œœ'œ˜NJšœ™Jšœœ˜ Jšœ˜—šžœœ˜3Jšœ™Jšœ˜"Jšœ˜—š ž œœœœœœ˜ZJšœœ˜)Jšœœ˜šœ˜šœ˜Jšœ œ˜Jšœ4˜;Jšœœ(œ!˜XJšœ-˜-Jšœ4˜;Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—šžœœ!œ ˜QJšœ[˜[JšœM˜SJšœ˜—šžœœL˜YJšœ œ˜"Jšœ1˜1Jšœ[˜[Jšœ œ˜&Jšœ œ˜&Jšœœ ˜/Jšœœ ˜/JšœœœŸ/˜SJšœœ˜#Jšœœ˜#šœœ˜#Jšœ>˜>J˜J˜—Jšœ˜Jšœ9˜9Jšœ9˜9Jšœ7˜7Jšœ7˜7Jšœ3˜3Jšœ3˜3Jšœ˜Jšœ˜Jšœn˜nJšœ˜—šžœœ0œ˜JJšœ œ˜"Jšœ1˜1Jšœœ˜#Jšœœ˜#šœ˜Jšœ œ"˜1Jšœ˜ —Jšœ˜—šžœœœ˜DJšœ œ˜"JšœO˜OJšœ…˜‹Jšœ˜—šžœœ9œœ˜hJšœ œ˜"Jšœœ˜'Jšœœ˜Jšœœ˜ Jšœ,œ˜4Jšœœ˜šœœ˜#Jšœœ*˜0Jšœœ˜—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šœ<˜