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; }; }. ΆUFDrawCharSeqImpl.mesa Michael Plass, April 5, 1983 2:56 pm This is the position of the origin of the character bounding box, in device coordinates; actually offset by half a pixel to allow truncation instead of rounding in the inner loop. For the inner loop, xPosition points to the left edge of the character instead of the baseline to make the per-character BitBlt table setup a little faster. subtract because y is increasing downwards in device space Font bounding box, in device units (pixels) and coordinates (y increasing downwards) Number of bits per scanline (always a multiple of Environment.bitsPerWord) Ref to the actual bits For trimming the number of bits that need BLTing Κ 8˜Jšœ™Jšœ$™$J˜šΟk ˜ Jšœ‘˜‘J˜—Jšœœ˜ Jšœe˜lšœ˜Jšœ˜Jš œ3œ"œHœœ˜ΣunitšΟnœ œ œœΟcŸœœ˜CJšœœœœ˜GJ˜—šžœ œœœŸŸœœ˜TJšœ œ œ œ ˜šœœœ˜'Jšœœœœ˜'Jš˜—Jšœœ˜Jšœ˜—Jšœ˜šœ˜Jšœ œœœ˜G—Jšœ˜—Kšœœœ˜2Kšœ#œ˜'š žœœ œ"œ%œ˜ŽJšœœœ˜Jšœœœœœœœœ˜9Jšœœœœ˜SJšœL˜LJšœB˜BJšœA˜AJšœœ9˜ZJšœœœD˜^Jšœ?˜CJšœ+œ˜/Jšœ.œ˜2Jšœ˜—šžœœ œ%œ˜iJšœ˜Jšœ˜Jšœœ˜Jšœ˜—šžœ œ˜Jšœœœ˜*Jš œ œœœœœ˜2Jš œ œœœœ˜;Jš œ œœœœ˜;Jšœ"˜"Jšœœ˜ Jšœœ˜Jšœ=˜=Jšœ=˜=šœ ˜Jšœœœ!œ˜cJšœ œœ>˜dJšœœ ˜'Jšœœ ˜'Jšœ4˜4Jšœ"˜"Jšœ(˜(Jšœ(˜(Jšœ˜Jšœ˜—Jšœ˜—K˜J˜—J˜—…—,ˆ;v