DIRECTORY
Basics USING [bitsPerWord, CARD, LongMult],
II USING [ConcatT, Context, DoSave, DoSaveAll, PathProc, PixelArray, SetColor, SetFont, SetXY],
IIBackdoor USING [Clipper, IntKey, RealKey, SetInt, SetReal, ViewTranslateI],
IIBox USING [Box, BoxFromExtents, Rectangle],
IIColor USING [Color, ColorOperator, ColorRep, SpecialColor],
IIDevice USING [AllowedMasks, DeviceClass, DeviceClassRep, DeviceParm, DeviceParmRep, FontTuner],
IIFont,
IIManhattan USING [BoundingBox, CreateFromBoxes, Destroy, DestructiveUnion, Map, Polygon, Shift],
IIMaskCache USING [CharMask, CharMaskRep],
IIMaskCapture USING [],
IIPrivate USING [Class],
IIRaster USING [Create, CreateClass, SetDeviceClipBox],
IISample USING [Clear, Fill, NewSampleMap, ObtainUnsafeDescriptor, ReleaseDescriptor, SampleMap, WordsForMap],
IIState USING [StateGetFont, StateGetInt, StateGetReal, StateSpace],
IITransformation USING [Transformation],
IITypeface USING [MaskChar],
Real USING [LargestNumber, Round],
Scaled USING [FromReal],
SF USING [Box, BoxAction, BoxGenerator, Displace, Max, maxVec, Min, minVec, Size, SizeF, SizeS],
Vector2 USING [VEC];
~
BEGIN
smallestBitmap:
NAT ← 10;
All CharMask rasters will be at least this big, in an attempt to improve locality.
CharMask: TYPE ~ IIMaskCache.CharMask;
CharMaskRep: TYPE ~ IIMaskCache.CharMaskRep;
Clipper: TYPE ~ IIBackdoor.Clipper;
Color: TYPE ~ IIColor.Color;
Context: TYPE ~ II.Context;
CorrectionType: TYPE ~ IIFont.CorrectionType;
Font: TYPE ~ IIFont.Font;
IntKey: TYPE ~ IIBackdoor.IntKey;
PathProc: TYPE ~ II.PathProc;
PixelArray: TYPE ~ II.PixelArray;
RealKey: TYPE ~ IIBackdoor.RealKey;
Rectangle: TYPE ~ IIBox.Rectangle;
SampleMap: TYPE ~ IISample.SampleMap;
Transformation: TYPE ~ IITransformation.Transformation;
VEC: TYPE ~ Vector2.VEC;
XChar:
TYPE ~ IIFont
.XChar;
viewOrigin:
NAT ~ 10000;
The view origin away from the origin of device coords.
Smaller than 16K to hit integerTrans often.
maxSize:
REAL ~ 1200.0;
If the bounding box exceeds this, we won't try to cache the mask in any form, but we may still cache the bounding box and escapement.
unsetColor: IIColor.SpecialColor ~
NEW[IIColor.ColorRep.special ← [special[type: $Unset, data:
NIL, substitute:
NIL]]];
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
boundsOnly: BOOL ← FALSE,
checkColor: BOOL ← FALSE,
bounds: SF.Box ← [],
boxes: IIManhattan.Polygon ← NIL
];
nullReal:
REAL ~ Real.LargestNumber;
Cant:
PUBLIC
SIGNAL [why:
ATOM] ~
CODE;
CreateContext:
PROC
RETURNS [Context] ~ {
data: Data ~ NEW [DataRep ← []];
deviceParm: IIDevice.DeviceParm ←
NEW [IIDevice.DeviceParmRep ← [
class: deviceClass,
sSize: INTEGER.LAST,
fSize: INTEGER.LAST,
scanMode: [slow: right, fast: up],
surfaceUnitsPerInch: [1,1],
surfaceUnitsPerPixel: 1,
fontTuner: NIL,
fontCache: NIL,
rastWeight: 0.0
]];
context: Context ~ IIRaster.Create[class: contextClass, deviceParm: deviceParm, data: data, pixelUnits: TRUE];
IIRaster.SetDeviceClipBox[context, [max: [INTEGER.LAST, INTEGER.LAST]]];
IIBackdoor.ViewTranslateI[context, viewOrigin, viewOrigin];
FOR key: IntKey
IN IntKey[strokeEnd..strokeJoint]
DO
IIBackdoor.SetInt[context, key, INT.LAST];
ENDLOOP;
FOR key: RealKey
IN RealKey[mediumXSize..correctTY]
DO
IIBackdoor.SetReal[context, key, nullReal];
ENDLOOP;
II.SetColor[context, unsetColor];
II.SetFont[context, NIL];
II.SetXY[context, [0, 0]];
RETURN [context]
};
Public Procs
CaptureChar:
PUBLIC
PROC [font: Font, char: XChar, ratio:
REAL, fontTuner: IIDevice.FontTuner ←
NIL, metricsOnly:
BOOL ←
FALSE]
RETURNS [charMask: CharMask ←
NIL] ~ {
escapement: VEC ~ IIFont.Escapement[font, char];
bounds: IIBox.Box ~ IIBox.BoxFromExtents[IIFont.BoundingBox[font, char]];
Operator:
PROC [context: Context] ~ {
IITypeface.MaskChar[font, char, context];
};
TunedOperator:
PROC [context: Context] ~ {
fontTuner.proc[fontTuner, Operator, context];
};
IF
ABS[escapement.x] <= 16383.0
AND
ABS[escapement.y] <= 16383.0
AND bounds.xmin >= -viewOrigin
AND bounds.ymin >= -viewOrigin
AND bounds.xmax <=
LAST[
INTEGER]-viewOrigin
AND bounds.ymax <=
LAST[
INTEGER]-viewOrigin
THEN {
p: IIManhattan.Polygon ← NIL;
ok: BOOL ← TRUE;
IF metricsOnly
OR bounds.xmax-bounds.xmin > maxSize
OR bounds.ymax-bounds.ymin > maxSize
THEN ok ← FALSE
ELSE p ← CaptureManhattan[operator: IF fontTuner = NIL THEN Operator ELSE TunedOperator, m: NIL, checkColor: TRUE ! Cant => {ok ← FALSE; CONTINUE}];
IF ok
THEN {
bb: SF.Box ~ IIManhattan.BoundingBox[p];
bitmapWords: REAL ~ IISample.WordsForMap[size: SF.Size[bb], bitsPerSample: 1];
nRuns: INT ~ CountRuns[p];
charMask ← (
IF nRuns*2*ratio >= bitmapWords THEN RasterCharMaskFromManhattan[p, bb]
ELSE RunsCharMaskFromManhattan[p, bb, nRuns]);
};
IIManhattan.Destroy[p];
IF charMask =
NIL
THEN {
IF metricsOnly
THEN charMask ← NEW[CharMaskRep.culled]
ELSE charMask ← NEW[CharMaskRep.maskNotCacheable];
charMask.box ← [min: [Floor[bounds.xmin], Floor[bounds.ymin]], max: [Ceiling[bounds.xmin], Ceiling[bounds.ymin]]];
};
charMask.font ← font;
charMask.char ← char;
charMask.escapement.s ← Scaled.FromReal[escapement.x];
charMask.escapement.f ← Scaled.FromReal[escapement.y];
charMask.amplified ← IIFont.Amplified[font, char];
charMask.correction ← IIFont.Correction[font, char];
};
};
CaptureBounds:
PUBLIC
PROC [operator:
PROC [Context], m: Transformation]
RETURNS [box:
SF.Box] ~ {
context: Context ~ GetContext[];
data: Data ~ NARROW[context.data];
proc: PROC ~ { II.ConcatT[context, m]; operator[context] };
data.boundsOnly ← TRUE;
data.checkColor ← FALSE;
data.bounds ← [min: SF.maxVec, max: SF.minVec];
II.DoSaveAll[context, proc];
box ← SF.Displace[data.bounds, [-viewOrigin, -viewOrigin]];
FreeContext[context];
};
CaptureBoxes:
PUBLIC
PROC [operator:
PROC [Context], m: Transformation, boxAction:
SF.BoxAction, checkColor:
BOOL] ~ {
p: IIManhattan.Polygon ~ CaptureManhattan[operator, m, checkColor];
FOR each:
LIST
OF
SF.Box ← p, each.rest
UNTIL each =
NIL
DO
boxAction[each.first];
ENDLOOP;
IIManhattan.Destroy[p];
};
CaptureBitmap:
PUBLIC
PROC [operator:
PROC [Context], m: Transformation, checkColor:
BOOL]
RETURNS [SampleMap] ~ {
p: IIManhattan.Polygon ~ CaptureManhattan[operator, m, checkColor];
bitmap: SampleMap ~ IISample.NewSampleMap[IIManhattan.BoundingBox[p]];
IISample.Clear[bitmap];
FOR each:
LIST
OF
SF.Box ← p, each.rest
UNTIL each =
NIL
DO
IISample.Fill[map: bitmap, box: each.first, value: 1];
ENDLOOP;
IIManhattan.Destroy[p];
RETURN [bitmap]
};
CaptureManhattan:
PUBLIC
PROC [operator:
PROC [Context], m: Transformation, checkColor:
BOOL]
RETURNS [p: IIManhattan.Polygon ←
NIL] ~ {
context: Context ~ GetContext[];
data: Data ~ NARROW[context.data];
proc: PROC ~ { II.ConcatT[context, m]; operator[context] };
data.boundsOnly ← FALSE;
data.checkColor ← checkColor;
data.bounds ← [min: SF.maxVec, max: SF.minVec];
data.boxes ← NIL;
II.DoSaveAll[context, proc];
IIManhattan.Shift[data.boxes, -viewOrigin, -viewOrigin];
p ← data.boxes;
data.boxes ← NIL;
FreeContext[context];
};
Support Procs
Floor:
PROC [r:
REAL]
RETURNS [i:
INT] ~
INLINE {
i ← Real.Round[r];
IF i > r THEN i ← i - 1;
};
Ceiling:
PROC [r:
REAL]
RETURNS [i:
INT] ~
INLINE {
i ← Real.Round[r];
IF i < r THEN i ← i + 1;
};
RasterCharMaskFromManhattan:
PROC [p: IIManhattan.Polygon, bb:
SF.Box]
RETURNS [
REF CharMaskRep.raster] ~ {
rast: NAT ~ (SF.SizeF[bb]+(Basics.bitsPerWord-1))/Basics.bitsPerWord;
bitmapWords: LONG CARDINAL ~ Basics.LongMult[SF.SizeS[bb], rast];
IF bitmapWords >
NAT.
LAST-10-
SIZE[CharMaskRep.raster[0]]
THEN RETURN [NIL]
ELSE
TRUSTED {
mask: REF CharMaskRep.raster ← NEW[CharMaskRep.raster[MAX[NAT[bitmapWords], smallestBitmap]]];
bits: SampleMap ~ IISample.ObtainUnsafeDescriptor[size: SF.Size[bb],
bitsPerSample: 1, bitsPerLine: rast*Basics.bitsPerWord,
base: [word: @mask[0], bit: 0], ref: mask, words: bitmapWords, delta: bb.min];
mask.flag ← ALL[0];
mask.box ← bb;
WHILE p #
NIL
DO
IISample.Fill[bits, p.first, 1];
p ← p.rest;
ENDLOOP;
IISample.ReleaseDescriptor[bits];
RETURN [mask];
};
};
RunsCharMaskFromManhattan:
PROC [p: IIManhattan.Polygon, bb:
SF.Box, nRuns:
INT]
RETURNS [mask:
REF CharMaskRep.runs] ~ {
i: NAT ← 0;
s: NAT ← 0;
AppendRun:
PROC [box:
SF.Box] ~ {
check: [1..1] ~ SF.SizeS[box];
smin: NAT ~ box.min.s-bb.min.s;
fmin: NAT ~ box.min.f-bb.min.f;
IF smin # s
THEN {
mask[i-1].lastRun ← TRUE;
s ← s + 1;
WHILE smin > s
DO
mask[i] ← [fMin: 0, lastRun: TRUE, fSize: 0];
i ← i + 1;
s ← s + 1;
ENDLOOP;
};
mask[i] ← [fMin: fmin, lastRun: FALSE, fSize: SF.SizeF[box]];
i ← i + 1;
};
IF nRuns = 0 OR nRuns > 4000 THEN RETURN [NIL];
mask ← NEW[CharMaskRep.runs[nRuns]];
mask.flag ← ALL[0];
mask.box ← bb;
IIManhattan.Map[polygon: p, boxAction: AppendRun, runs: TRUE];
IF i#nRuns THEN ERROR;
IF CARDINAL[s]+1 # SF.SizeS[mask.box] THEN ERROR;
mask[i-1].lastRun ← TRUE;
};
CountRuns:
PROC [p: IIManhattan.Polygon]
RETURNS [runs:
INT ← 0] ~ {
This version includes zero-length runs needed to get the PD run representation to work.
s: INTEGER ← IF p # NIL THEN p.first.min.s ELSE 0;
WHILE p #
NIL
DO
sMin: INTEGER ~ p.first.min.s;
sSize: NAT ~ SF.SizeS[p.first];
IF sMin > s THEN {runs ← runs + (sMin - s)};
s ← sMin + sSize;
runs ← runs + sSize;
p ← p.rest;
ENDLOOP;
};
Context Class Procs
MySetT:
PROC [context: Context, m: Transformation] ~ {
SIGNAL Cant[$SetT]};
MyGetInt:
PROC [context: Context, key: IntKey]
RETURNS [
INT] ~ {
i: INT ~ IIState.StateGetInt[context, key];
IF i = INT.LAST THEN SIGNAL Cant[$UninitializedState];
RETURN [i]
};
MyGetReal:
PROC [context: Context, key: RealKey]
RETURNS [
REAL] ~ {
r: REAL ~ IIState.StateGetReal[context, key];
IF r = nullReal THEN SIGNAL Cant[$UninitializedState];
IF key = DCScpx OR key = DCScpy THEN SIGNAL Cant[$GetDCScp];
RETURN [r]
};
MyGetFont:
PROC [context: Context]
RETURNS [Font] ~ {
font: Font ~ IIState.StateGetFont[context];
IF font = NIL THEN SIGNAL Cant[$UninitializedState];
RETURN [font];
};
MyGetColor:
PROC [context: Context]
RETURNS [Color] ~ {
ERROR Cant[$GetColor]
};
MyGetClipper:
PROC [context: Context]
RETURNS [Clipper] ~ {
ERROR Cant[$GetClipper]
};
MySpace:
PROC [context: Context, x:
REAL] ~ {
IF IIState.StateGetReal[context, amplifySpace] = nullReal THEN ERROR Cant[$UninitializedState];
IIState.StateSpace[context, x];
};
RasterShow: PROC [context: Context, string: IIFont.XStringProc, xrel: BOOL] ← NIL;
MyShow:
PROC [context: Context, string: IIFont.XStringProc, xrel:
BOOL] ~ {
IF IIState.StateGetFont[context] = NIL THEN ERROR Cant[$UninitializedState];
RasterShow[context, string, xrel];
};
RasterMaskUnderline: PROC [context: Context, dy: REAL, h: REAL] ← NIL;
MyMaskUnderline:
PROC [context: Context, dy:
REAL, h:
REAL] ~ {
IF IIState.StateGetReal[context, underlineStart] = nullReal THEN ERROR Cant[$UninitializedState];
RasterMaskUnderline[context, dy, h];
};
MyCorrect:
PROC [context: Context, action:
PROC] ~ {
SIGNAL Cant[$Correct];
II.DoSaveAll[context, action];
};
MyDontCorrect:
PROC [context: Context, action:
PROC, saveCP:
BOOL] ~ {
SIGNAL Cant[$Correct];
IF saveCP THEN II.DoSaveAll[context, action] ELSE II.DoSave[context, action];
};
RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ← NIL;
MyMaskStroke:
PROC [context: Context, path: PathProc, closed:
BOOL] ~ {
IF IIState.StateGetInt[context, strokeEnd] = INT.LAST OR IIState.StateGetInt[context, strokeJoint] = INT.LAST OR IIState.StateGetReal[context, strokeWidth] = nullReal THEN ERROR Cant[$UninitializedState];
RasterMaskStroke[context, path, closed];
};
MyGetBounds:
PROC [context: Context]
RETURNS [Rectangle] ~ {
ERROR Cant[$GetBounds]};
deviceClass: IIDevice.DeviceClass ~
NEW[IIDevice.DeviceClassRep ← [
SetColor: CaptureSetColor,
MaskBoxes: CaptureMaskBoxes
]];
contextClass: IIPrivate.Class ~ MakeClass[];
MakeClass:
PROC
RETURNS [IIPrivate.Class] ~ {
new: IIPrivate.Class ~ IIRaster.CreateClass[type: $Capture, deviceClass: deviceClass];
new.SetT ← MySetT;
new.GetInt ← MyGetInt;
new.GetReal ← MyGetReal;
new.GetFont ← MyGetFont;
new.GetColor ← MyGetColor;
new.GetClipper ← MyGetClipper;
new.Space ← MySpace;
RasterMaskUnderline ← new.MaskUnderline;
new.MaskUnderline ← MyMaskUnderline;
RasterShow ← new.Show;
new.Show ← MyShow;
new.Correct ← MyCorrect;
new.DontCorrect ← MyDontCorrect;
RasterMaskStroke ← new.MaskStroke;
new.MaskStroke ← MyMaskStroke;
new.GetBounds ← MyGetBounds;
RETURN [new]
};