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˜—Kšžœœœœœœœœœ˜`Kš žœœœœœ˜@šœ œœ œœœœ˜AJšœ˜šœ ˜ Jšœœ˜Jšœœœ˜6Jšœ!˜!J˜—Jšœ˜ —Kšœœœ˜Kš žœœœœœ˜ZKšœ"œœŸ˜KKšœœœ˜.šžœœ œ˜Jšœœœ˜*Jšœ œœœœœ˜2Jš œœœœœ˜;Jš œœœœœ˜;Jšœ"˜"Jšœœ˜ Jšœ8œ˜HJšœœ˜.Jšœ.˜.Jšœ0˜0Jšœ*˜*Jšœ-˜-Jšœ˜Jšœ˜Jšœ˜!Jšœ)œ(˜Všœ˜šžœœœ˜2šœœ˜'Jšœœ$œ˜WJšœ-˜1—Jšœ˜—šœœ˜&Jšœ˜Jšœ˜Jšœ˜—Jšœœ˜Jšœœ˜Jšœ˜Jšœ<˜<Jšœ9˜9Jšœœœ˜3Jšœœ˜/Jšœœ˜.Jšœœ3˜NJšœœ1˜Lšœ˜J™XJ™Z—Jšœ5˜5Jšœ5˜5Jšœ˜Jšœœ˜<šœœ(˜3Jšœ8˜8—Jšœœ˜*Jšœ8˜8J˜.šœ ˜Icodešœ œœ)œ/˜tšœ˜Jšœœ˜4Jšœœ*œ˜OJšœœ Ÿ*˜GJšœœœœœ#œ˜yJšœœœœ˜0Jšœœ™œJšœH˜HJšœœœ˜-šœ#˜(Jšœ œ ˜Jšœœ+œ œœ˜}Jš œœœ0œ˜wJš œœœœŸ%˜OJšœ0˜0Jšœ1˜1Jšœ˜Jšœœœ˜!Jšœœ ˜-Jšœ(˜(Jšœ"˜"Jšœ˜—Jšœ˜JšœH˜HJ˜—šœœ˜šœ œ œ˜šœ'œœ˜dJšœ"˜"—Jšœ˜šœœ'œ˜HJšœ<˜<—šœ˜LšœA˜ALšœ-˜-šœ6˜<Lšœ,˜,—Lš œ)œœ œœí˜ÅLšœ ˜ L˜—Jšœ˜—Jšœœ ˜-Jšœ(˜(šœœ ˜-J™:—Jšœ(˜(Jšœ"˜"Jšœ˜J˜—Jš˜—J˜ J˜—Jšœœ˜JšœŒ˜‘Jšœ˜—Kšœœœ˜-šœœœ˜"šœœ˜ J™T—šœœ˜J™J—Jšœœ œœ˜-J˜—Jšœœœ œœœ˜Jšœœœ˜!šœ œ˜ J™—šœ˜J™0—J˜—šžœ œœœ%˜VJšœœ˜-šœœœ˜Jšœ-œ'˜WJšœ7˜7Jšœ9˜9Jšœ8˜8Jšœ:˜:Jšœ˜˜˜Jšœ˜—Jšœ˜—šžœ œœœ˜8Jšœ!œ˜=š œœœœœ˜=J˜Jšœœ˜"Jšœ6˜6Jšœ9˜9Jšœœœ:˜\šœ ˜ JšœC˜CJšœ˜Jšœ˜—Jšœœ˜Jšœ'˜'Jšœ˜—Jšœ˜—š žœ œ!œ œœ˜XJšœœ œœœœœœ œœœœ<˜¤Jšžœœœœœœœ8˜€šž œœœœœœ˜Ašœœœ˜(Jšœœœœ˜'Jš˜—Jšœœ˜Jšœ˜—Jšœ˜šœ˜Jšœœœœ˜F—Jšœ˜—š žœ œ!œ œœ˜ZJšœœ œœœœœœ œœœœ<˜¤Jšžœœœœœœœ8˜€šž œœœœœœ˜>šœœœ˜'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��