<> <> DIRECTORY BitBlt, Environment, Graphics, GraphicsOps, CGContext, CGMatrix, CGDevice, CGPrivate, UnifiedFonts, SafeStorage, CGClipper, Real, Inline, Scaled; UFDrawCharSeqImpl: CEDAR MONITOR IMPORTS BitBlt, Graphics, GraphicsOps, SafeStorage, CGClipper, CGPrivate, UnifiedFonts, Real, Inline, Scaled EXPORTS UnifiedFonts = { OPEN UnifiedFonts; nullBitBltTable: BitBlt.BitBltTable = [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [disjoint: TRUE, gray: FALSE, srcFunc: null, dstFunc: or]]; IsIn: PROCEDURE [b, c: Box] RETURNS [BOOLEAN] = --INLINE-- {RETURN[ b.xmin>=c.xmin AND b.ymin>=c.ymin AND b.xmax<=c.xmax AND b.ymax<=c.ymax ]}; TranslationOnly: PROCEDURE [s: CGMatrix.Ref] RETURNS [BOOLEAN] = --INLINE-- {RETURN[ s.m.a = 1.0 AND s.m.b = 0.0 AND s.m.c = 0.0 AND s.m.d = -1.0 ]}; Floor: PROC [r: REAL] RETURNS [i: INTEGER] = {i _ Real.Fix[r-FIRST[INTEGER]] + FIRST[INTEGER]}; Ceiling: PROC [r: REAL] RETURNS [i: INTEGER] = {i _ -Floor[-r]}; Position: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM scaled => [sv: Scaled.Value], splitIn3 => [ frac: CARDINAL, wordOffset: [0..LAST[WORD]/(Environment.bitsPerWord)], bit: [0..Environment.bitsPerWord) ], ENDCASE]; showWhite: BOOLEAN _ FALSE; GetData: PROC [context: Context] RETURNS [CGContext.Ref] = {RETURN[NARROW[context.data]]}; blting, drawbiting, innerlooping: BOOLEAN _ TRUE; -- for timing experiments trimmingWidth, trimmingHeight: BOOLEAN _ TRUE; DrawCharSeq: PUBLIC PROCEDURE [ font: FONT, context: Context, count: NAT, charPtr: LONG POINTER TO CHAR, charIncrement: NAT, deltaXptr: LONG POINTER TO FixedUnit, deltaXincrement: NAT, deltaYptr: LONG POINTER TO FixedUnit, deltaYincrement: NAT, positioning: Positioning _ rounded ] = TRUSTED { ENABLE SafeStorage.NarrowFault, SafeStorage.NarrowRefFault => GOTO Oops; halfPoint: CARDINAL = (FixedPoint.fraction/2); contextData: CGContext.Ref _ GetData[context]; clientMatrix: CGMatrix.Ref _ contextData.matrix; device: CGDevice.Ref _ contextData.device; clipper: CGClipper.Ref _ contextData.clipper; IF font.bitmapCacheable AND contextData.yUp AND TranslationOnly[clientMatrix] AND (contextData.src.color = Graphics.black OR contextData.src.color = Graphics.white) THEN { SetCP: PROC [positioning: Positioning] = TRUSTED { contextData.cp _ IF positioning = exact THEN [xPosition.sv.MINUS[Scaled.half].Float[], yPosition.sv.MINUS[Scaled.half].Float[]] ELSE [xPosition.sv.Floor[], yPosition.sv.Floor[]] }; easy: BOOLEAN _ contextData.haveRaster AND positioning = rounded AND contextData.yUp AND CGClipper.IsBox[clipper]; bc: CHAR _ font.bc; ec: CHAR _ font.ec; bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; characterCache: CharacterCache _ GetCharacterCache[font]; deviceRasterBase: LONG POINTER _ contextData.dbase; leftPixel: INTEGER _ Ceiling[clipper.box.xmin]; rightPixel: INTEGER _ Floor[clipper.box.xmax]; minAllowedBaseline: INTEGER _ Ceiling[clipper.box.ymin] - characterCache.ymin; maxAllowedBaseline: INTEGER _ Floor[clipper.box.ymax] - characterCache.ymax; xPosition, yPosition: Position; <> <> xPosition.sv _ Scaled.FromReal[contextData.cp.x+0.5]; yPosition.sv _ Scaled.FromReal[contextData.cp.y+0.5]; bb^ _ nullBitBltTable; IF contextData.src.mode = invert THEN bb.flags.dstFunc _ xor ELSE IF contextData.src.color = Graphics.white THEN {bb.flags.srcFunc _ complement; bb.flags.dstFunc _ and}; IF showWhite THEN bb.flags.dstFunc _ null; bb.dstBpl _ contextData.drast * Environment.bitsPerWord; bb.srcDesc.srcBpl _ characterCache.rasterBits; WHILE count>0 DO pointerBase: LONG POINTER _ contextData.dbase + contextData.drast*INT[yPosition.sv.integerPart+characterCache.ymin]; IF easy AND deltaYptr^ = Scaled.zero AND deltaYincrement = 0 AND yPosition.sv.integerPart IN [minAllowedBaseline..maxAllowedBaseline) THEN { innerCount: INTEGER _ count; -- integer to eliminate some bounds checks characterCachePtr: LONG POINTER TO BitmapDescriptor _ LOOPHOLE[(@characterCache[0]) - (bc-'\000)*SIZE[BitmapDescriptor]]; cachedCharPtr: LONG POINTER TO BitmapDescriptor; <> xPosition.sv.integerPart _ xPosition.sv.integerPart+characterCache.xmin; IF NOT innerlooping THEN innerCount _ -1 ELSE WHILE (innerCount _ innerCount - 1) >= 0 AND charPtr^ IN [bc..ec] AND (bb.src.word _ LOOPHOLE[(cachedCharPtr _ characterCachePtr+Inline.BITSHIFT[(charPtr^ - FIRST[CHAR]), 2])^.refBits]) # NIL AND xPosition.sv.integerPart IN [leftPixel..rightPixel-LOOPHOLE[bb.width _ cachedCharPtr.widthAndHeight.width, NAT]) DO IF SIZE[BitmapDescriptor] # 4 THEN ERROR; -- fix the BITSHIFT in the above test bb.height _ cachedCharPtr.widthAndHeight.height; bb.dst.word _ pointerBase + xPosition.wordOffset; bb.dst.bit _ xPosition.bit; IF blting THEN BitBlt.BITBLT[bb]; xPosition.sv _ xPosition.sv.PLUS[deltaXptr^]; deltaXptr _ deltaXptr + deltaXincrement; charPtr _ charPtr + charIncrement; ENDLOOP; count _ innerCount + 1; xPosition.sv.integerPart _ xPosition.sv.integerPart-characterCache.xmin; }; IF count>0 THEN { IF charPtr^ IN [bc..ec] THEN { IF characterCache[charPtr^-bc].refBits = NIL AND positioning = rounded AND font.bitmapCacheable THEN EnterCharInCache[font, charPtr^]; SetCP[positioning]; IF positioning = exact OR characterCache[charPtr^-bc].refBits = NIL THEN font.fontGraphicsClass.drawCharProc[font, charPtr^, context] ELSE { bitmapDescriptor: BitmapDescriptor _ characterCache[charPtr^-bc]; mark: Graphics.Mark _ Graphics.Save[context]; IF Graphics.SetPaintMode[context, transparent] = invert THEN [] _ Graphics.SetPaintMode[context, invert]; CGPrivate.DrawBits[ self: context, base: LOOPHOLE[bitmapDescriptor.refBits, LONG POINTER], raster: characterCache.rasterBits/Environment.bitsPerWord, bitsPerPixel: 0, x: 0, y: 0, w: bitmapDescriptor.widthAndHeight.width, h: bitmapDescriptor.widthAndHeight.height, xorigin:-characterCache.xmin, yorigin:-characterCache.ymin ]; Graphics.Restore[context, mark]; }; }; xPosition.sv _ xPosition.sv.PLUS[deltaXptr^]; deltaXptr _ deltaXptr + deltaXincrement; yPosition.sv _ yPosition.sv.PLUS[deltaYptr^]; <> deltaYptr _ deltaYptr + deltaYincrement; charPtr _ charPtr + charIncrement; count _ count - 1; }; ENDLOOP; SetCP[exact]; } ELSE GOTO Oops; EXITS Oops => SureDrawCharSeq[font, context, count, charPtr, charIncrement, deltaXptr, deltaXincrement, deltaYptr, deltaYincrement, positioning]; }; CharacterCache: TYPE = REF CharacterCacheRec; CharacterCacheRec: TYPE = RECORD [ xmin, ymin, xmax, ymax: INTEGER, <> rasterBits: CARDINAL, <> seq: SEQUENCE length: NAT OF BitmapDescriptor ]; WidthAndHeight: TYPE = MACHINE DEPENDENT RECORD [width, height: CARDINAL]; BitmapDescriptor: TYPE = RECORD [ refBits: REF, <> widthAndHeight: WidthAndHeight <> ]; GetCharacterCache: PROCEDURE [font: FONT] RETURNS [characterCache: CharacterCache] = { characterCache _ NARROW[font.characterCache]; IF characterCache = NIL THEN { font.characterCache _ characterCache _ pZone.NEW[CharacterCacheRec[font.ec-font.bc+1]]; characterCache.xmin _ Floor[font.fontBoundingBox.xmin]; characterCache.xmax _ Ceiling[font.fontBoundingBox.xmax]; characterCache.ymin _ Floor[-font.fontBoundingBox.ymax]; characterCache.ymax _ Ceiling[-font.fontBoundingBox.ymin]; characterCache.rasterBits _ ((characterCache.xmax-characterCache.xmin + Environment.bitsPerWord-1) / Environment.bitsPerWord) * Environment.bitsPerWord; }; }; EnterCharInCache: PROCEDURE [font: FONT, char: CHAR] = { characterCache: CharacterCache _ NARROW[font.characterCache]; IF characterCache # NIL AND char IN [font.bc..font.ec] THEN { context: Graphics.Context; bitmap: REF GraphicsOps.BitmapRep; [context, bitmap] _ GetScratchContext[characterCache]; context.SetCP[-characterCache.xmin, characterCache.ymax]; IF char IN [font.bc..font.ec] THEN font.fontGraphicsClass.drawCharProc[font, char, context]; characterCache[char-font.bc] _ [ widthAndHeight: [width: WidthOf[bitmap], height: HeightOf[bitmap]], refBits: bitmap.base ]; bitmap.base _ NIL; ReleaseScratchContext[context, bitmap]; }; }; WidthOf: PROCEDURE [bitmap: GraphicsOps.BitmapRef] RETURNS [width: CARDINAL] = TRUSTED { bit: LONG DESCRIPTOR FOR PACKED ARRAY CARDINAL OF [0..1] _ DESCRIPTOR[LOOPHOLE[bitmap.base, LONG POINTER], bitmap.raster * Environment.bitsPerWord * bitmap.height]; Bit: PROC [row, col: CARDINAL] RETURNS [[0..1]] = TRUSTED INLINE {RETURN[bit[row*bitmap.raster*Environment.bitsPerWord + col]]}; ColumnAllZero: PROC [col: CARDINAL] RETURNS [BOOLEAN] = TRUSTED { FOR i: CARDINAL IN [0..bitmap.height) DO IF Bit[i, col] = 1 THEN RETURN [FALSE] ENDLOOP; RETURN [TRUE]; }; width _ bitmap.width; IF trimmingWidth THEN WHILE width > 0 AND ColumnAllZero[width-1] DO width _ width-1 ENDLOOP; }; HeightOf: PROCEDURE [bitmap: GraphicsOps.BitmapRef] RETURNS [height: CARDINAL] = TRUSTED { bit: LONG DESCRIPTOR FOR PACKED ARRAY CARDINAL OF [0..1] _ DESCRIPTOR[LOOPHOLE[bitmap.base, LONG POINTER], bitmap.raster * Environment.bitsPerWord * bitmap.height]; Bit: PROC [row, col: CARDINAL] RETURNS [[0..1]] = TRUSTED INLINE {RETURN[bit[row*bitmap.raster*Environment.bitsPerWord + col]]}; RowAllZero: PROC [row: CARDINAL] RETURNS [BOOLEAN] = TRUSTED { FOR j: CARDINAL IN [0..bitmap.width) DO IF Bit[row, j] = 1 THEN RETURN [FALSE] ENDLOOP; RETURN [TRUE]; }; height _ bitmap.height; IF trimmingHeight THEN WHILE height > 0 AND RowAllZero[height-1] DO height _ height-1 ENDLOOP; }; scratchBitmapRef: REF GraphicsOps.BitmapRep _ NIL; scratchContext: Graphics.Context _ NIL; GetScratchContext: ENTRY PROCEDURE [characterCache: CharacterCache] RETURNS [context: Graphics.Context, bitmap: REF GraphicsOps.BitmapRep] = { ENABLE UNWIND => NULL; Words: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF WORD]; IF scratchBitmapRef = NIL THEN scratchBitmapRef _ pZone.NEW[GraphicsOps.BitmapRep]; scratchBitmapRef.raster _ characterCache.rasterBits/Environment.bitsPerWord; scratchBitmapRef.height _ characterCache.ymax-characterCache.ymin; scratchBitmapRef.width _ characterCache.xmax-characterCache.xmin; scratchBitmapRef.base _ pZone.NEW[Words[scratchBitmapRef.raster*scratchBitmapRef.height]]; IF scratchContext=NIL THEN scratchContext _ GraphicsOps.NewContextFromBitmap[scratchBitmapRef] ELSE GraphicsOps.SetTargetBitmap[scratchContext, scratchBitmapRef]; context _ scratchContext; scratchContext _ NIL; bitmap _ scratchBitmapRef; scratchBitmapRef _ NIL; }; ReleaseScratchContext: ENTRY PROCEDURE [context: Graphics.Context, bitmap: REF GraphicsOps.BitmapRep] = { scratchContext _ context; scratchBitmapRef _ bitmap; bitmap.base _ NIL; }; SureDrawCharSeq: PROCEDURE [ font: FONT, context: Context, count: NAT, charPtr: LONG POINTER TO CHAR, charIncrement: NAT, deltaXptr: LONG POINTER TO FixedUnit, deltaXincrement: NAT, deltaYptr: LONG POINTER TO FixedUnit, deltaYincrement: NAT, positioning: Positioning _ rounded ] = TRUSTED { totw: INT _ 0; positionX: Scaled.Value _ Scaled.FromReal[context.GetCP[].x]; positionY: Scaled.Value _ Scaled.FromReal[context.GetCP[].y]; WHILE count#0 DO IF positioning=rounded THEN {x, y: REAL; [x,y] _ context.GetCP[rounded: TRUE]; context.SetCP[x,y]}; IF charPtr^ IN [font.bc..font.ec] THEN font.fontGraphicsClass.drawCharProc[font, charPtr^, context]; positionX _ positionX.PLUS[deltaXptr^]; positionY _ positionY.PLUS[deltaYptr^]; context.SetCP[positionX.Float[], positionY.Float[]]; charPtr _ charPtr + charIncrement; deltaXptr _ deltaXptr + deltaXincrement; deltaYptr _ deltaYptr + deltaYincrement; count _ count - 1; ENDLOOP; }; }.