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��