ImagerRasterShowImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, July 20, 1985 10:37:13 am PDT
Doug Wyatt, May 30, 1985 11:15:48 pm PDT
DIRECTORY
Basics USING [bitsPerWord],
Imager USING [Box, Color, ColorOperator, Context, CorrectMask, CorrectSpace, DoSave, Error, PixelArray, SetXRel, SetXYRel, StateRep, Trans],
ImagerBackdoor USING [Clipper, GetBounds, GetCP, GetReal],
ImagerBox USING [BoxFromExtents, BoxFromRect, IntersectBox],
ImagerCache USING [Fetch, Flush, Ref, Store],
ImagerDevice USING [CharMask, CharMaskRep, Class, Device, DeviceBox, RunProc],
ImagerFont USING [Amplified, BoundingBox, Correction, Font, MapText, Width, XChar, XStringProc],
ImagerFontPrivate USING [FontImpl, FontImplRep, MaskChar],
ImagerManhattan USING [CreateFromBox, Destroy, IsVisible, Polygon],
ImagerMask USING [RunsFromCharMask],
ImagerMaskCapture USING [Capture],
ImagerPath USING [PathProc],
ImagerPixelMap USING [DeviceRectangle],
ImagerPrivate USING [Class, ClassRep],
ImagerRaster USING [FontTuner],
ImagerRasterPrivate USING [AndFlags, CharArrayIndex, charArraySize, ClipBoxToMask, Data, Flags, NoteStateChanges, ValidateIfNeeded],
ImagerScanConverter USING [DevicePath],
ImagerState USING [notChanged, State, StateRep],
ImagerTransformation USING [InverseTransform, Rectangle, Transform, Transformation, TransformationRep, TranslateTo],
Real USING [],
Rope USING [ROPE],
Scaled USING [Float, FromReal, PLUS, Round, Value],
Vector2 USING [Mul, VEC];
ImagerRasterShowImpl: CEDAR PROGRAM
IMPORTS Basics, Imager, ImagerBackdoor, ImagerBox, ImagerCache, ImagerFont, ImagerFontPrivate, ImagerManhattan, ImagerMask, ImagerMaskCapture, ImagerRasterPrivate, ImagerTransformation, Real, Scaled, Vector2
EXPORTS Imager, ImagerFont, ImagerRasterPrivate
~ BEGIN OPEN ImagerState, ImagerRasterPrivate;
Class: TYPE ~ ImagerPrivate.Class;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
ROPE: TYPE ~ Rope.ROPE;
CharMask: TYPE ~ ImagerDevice.CharMask;
Clipper: TYPE ~ ImagerBackdoor.Clipper;
Color: TYPE ~ Imager.Color;
ColorOperator: TYPE ~ Imager.ColorOperator;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDevice.Device;
DeviceBox: TYPE ~ ImagerDevice.DeviceBox;
RunProc: TYPE ~ ImagerDevice.RunProc;
DevicePath: TYPE ~ ImagerScanConverter.DevicePath;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
Font: TYPE ~ ImagerFont.Font;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
PathProc: TYPE ~ ImagerPath.PathProc;
PixelArray: TYPE ~ Imager.PixelArray;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
Transformation: TYPE ~ ImagerTransformation.Transformation;
TransformationRep: TYPE ~ ImagerTransformation.TransformationRep;
VEC: TYPE ~ Vector2.VEC;
XChar: TYPE ~ ImagerFont.XChar;
XStringProc: TYPE ~ ImagerFont.XStringProc;
FontImpl: TYPE ~ ImagerFontPrivate.FontImpl;
FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont
FontTuner: TYPE ~ ImagerRaster.FontTuner;
bitsPerWord: NAT ~ Basics.bitsPerWord;
DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ {
RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]];
};
Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE {
IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state];
IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs];
};
showNeeds: Flags ~ [clientClipper: TRUE, deviceColor: TRUE, devicePriority: TRUE, clientToDevice: TRUE, fontInfo: TRUE];
CharPartlyVisible: PROC [context: Context, font: Font, char: XChar] RETURNS [BOOL] ~ {
bounds: Imager.Box ~ ImagerBox.BoxFromRect[ImagerBackdoor.GetBounds[context]];
bb: Imager.Box ~ ImagerBox.BoxFromExtents[ImagerFont.BoundingBox[font, char]];
IF bb.xmin=bb.xmax THEN RETURN[TRUE]
ELSE {
cp: VEC ~ ImagerBackdoor.GetCP[context];
cbb: Imager.Box ~ [xmin: bb.xmin+cp.x, ymin: bb.ymin+cp.y, xmax: bb.xmax+cp.x, ymax: bb.ymax+cp.y];
common: Imager.Box ~ ImagerBox.IntersectBox[bounds, cbb];
RETURN [((common.xmax > common.xmin) AND (common.ymax > common.ymin))]
};
};
BasicShowChar: PUBLIC PROC [context: Context, font: Font, char: XChar,
checkBB: BOOL, imaging: BOOL, fontTuner: FontTuner ← NIL] ~ {
width: VEC ← font.Width[char];
IF font.Amplified[char] THEN {
width ← width.Mul[ImagerBackdoor.GetReal[context, amplifySpace]];
};
Imager.Trans[context];
IF imaging AND (NOT checkBB OR CharPartlyVisible[context, font, char]) THEN {
IF fontTuner = NIL THEN {
ImagerFontPrivate.MaskChar[font, char, context];
}
ELSE {
op: PROC [ctx: Context] ~ {ImagerFontPrivate.MaskChar[font, char, ctx]};
fontTuner.proc[fontTuner, op, context];
};
};
Imager.SetXYRel[context, width];
SELECT font.Correction[char] FROM
none => NULL;
space => Imager.CorrectSpace[context, width];
mask => Imager.CorrectMask[context];
ENDCASE => ERROR;
};
worryNat: NAT ~ NAT.LAST/2-1;
worryReal: REAL ← worryNat;
RasterShow: PUBLIC PROC [context: Context, string: XStringProc, xrel: BOOL] ~ {
state: State ~ context.state;
data: Data ~ NARROW[context.data];
device: Device ~ data.device;
cache: ImagerCache.Ref ~ data.fontCache;
font: Font ~ state.font;
amplifySpace: REAL ~ state.np.amplifySpace;
testAmplified: BOOL ~ amplifySpace#1.0;
testCorrection: BOOL ~ state.np.correctPass#0;
imaging: BOOL ~ state.np.noImage=0;
basicCharAction: PROC [char: XChar] ~ {
BasicShowChar[context, font, char, TRUE, imaging, data.fontTuner];
};
scaledCPValid: BOOLFALSE;
sCP: Scaled.Value;
fCP: Scaled.Value;
UpdateCP: PROC ~ {
IF scaledCPValid THEN state.p.cp ← data.viewToDevice.InverseTransform[[sCP.Float, fCP.Float]];
};
TryScaledCP: PROC ~ {
cp: VEC ← data.viewToDevice.Transform[state.p.cp];
IF ABS[cp.x] < worryReal AND ABS[cp.y] < worryReal THEN {
sCP ← Scaled.FromReal[cp.x];
fCP ← Scaled.FromReal[cp.y];
scaledCPValid ← TRUE;
}
ELSE scaledCPValid ← FALSE;
};
cachedCharAction: PROC [char: XChar] ~ {
charIndex: CharArrayIndex ~ char.code MOD charArraySize;
prevMask: CharMask ~ data.charArray[charIndex];
charMask: CharMask ← prevMask;
Validate[data, state, showNeeds];
IF charMask#NIL AND charMask.font=data.fontAtom AND charMask.char=char THEN NULL
ELSE charMask ← ImagerCache.Fetch[cache, data.fontAtom, char];
IF charMask=NIL AND imaging AND CharPartlyVisible[context, font, char] THEN {
operator: PROC [c: Context] ~ {BasicShowChar[c, font, char, FALSE, TRUE, data.fontTuner]};
m: Transformation ← data.clientToDevice.TranslateTo[[0, 0]];
charMask ← ImagerMaskCapture.Capture[operator, m, data.rastWeight];
IF charMask#NIL THEN {
charMask.font ← data.fontAtom;
charMask.char ← char;
UNTIL ImagerCache.Store[cache, charMask].ok DO ImagerCache.Flush[cache] ENDLOOP;
};
};
IF charMask=NIL OR NOT charMask.metricsValid OR NOT scaledCPValid THEN {
UpdateCP[];
basicCharAction[char];
TryScaledCP[];
}
ELSE {
sCPOld: Scaled.Value ~ sCP;
fCPOld: Scaled.Value ~ fCP;
s: INTEGER ~ Scaled.Round[sCPOld];
f: INTEGER ~ Scaled.Round[fCPOld];
IF imaging AND charMask.sSizeBB#0 THEN {
ShowCharacterMask[data: data, mask: charMask, s: s, f: f];
};
IF testCorrection OR (testAmplified AND charMask.amplified) THEN {
width: VEC ← font.Width[char];
IF testAmplified AND font.Amplified[char] THEN {
width ← width.Mul[amplifySpace];
};
UpdateCP[];
Imager.SetXYRel[context, width];
IF testCorrection THEN SELECT font.Correction[char] FROM
none => NULL;
space => Imager.CorrectSpace[context, width];
mask => Imager.CorrectMask[context];
ENDCASE => ERROR;
TryScaledCP[];
}
ELSE {
sCPNew: Scaled.Value ~ (sCP ← sCPOld.PLUS[charMask.sWidth]);
fCPNew: Scaled.Value ~ (fCP ← fCPOld.PLUS[charMask.fWidth]);
IF sCPNew.integerPart NOT IN [-worryNat..worryNat]
OR fCPNew.integerPart NOT IN [-worryNat..worryNat] THEN {
UpdateCP[];
TryScaledCP[];
};
};
IF charMask#prevMask THEN data.charArray[charIndex] ← charMask;
};
};
oddCharIndex: BOOLFALSE;
xRelAction: PROC [char: XChar] ~ {
IF oddCharIndex THEN {
Imager.SetXRel[context, char.code-128];
}
ELSE IF cache=NIL THEN basicCharAction[char]
ELSE {
TryScaledCP[];
cachedCharAction[char];
UpdateCP[];
};
oddCharIndex ← NOT oddCharIndex;
};
saveAction: PROC ~ {
Validate[data, state, showNeeds];
IF xrel THEN string[xRelAction]
ELSE IF cache=NIL THEN string[basicCharAction]
ELSE {
TryScaledCP[];
string[cachedCharAction];
UpdateCP[];
};
};
IF state.font=NIL THEN ERROR Imager.Error[[code: $noFont,
explanation: "Failed to set font before calling Show."]];
Imager.DoSave[context, saveAction];
};
oops: CARDINAL ← 0;
ShowCharacterMask: PUBLIC PROC [data: Data, mask: CharMask, s: INTEGER, f: INTEGER] ~ {
sMin: INTEGER ~ s+mask.sMinBB;
fMin: INTEGER ~ f+mask.fMinBB;
device: Device ~ data.device;
allVisible: BOOLFALSE;
partlyVisible: BOOLTRUE;
partlyClipped: BOOLFALSE;
charBox: DeviceBox ← [
smin: LOOPHOLE[sMin],
fmin: LOOPHOLE[fMin],
smax: LOOPHOLE[sMin, CARDINAL]+mask.sSizeBB,
fmax: LOOPHOLE[fMin, CARDINAL]+mask.fSizeBB
];
IF AndFlags[data.valid, showNeeds]#showNeeds THEN oops ← oops + 1;
IF sMin < 0 OR fMin < 0 THEN {
charBox ← [
smin: MAX[sMin, 0],
fmin: MAX[fMin, 0],
smax: MAX[sMin+INTEGER[mask.sSizeBB], 0],
fmax: MAX[fMin+INTEGER[mask.fSizeBB], 0]
];
partlyClipped ← TRUE;
};
IF data.clientClipBoxOnly THEN {
dBB: DeviceBox ~ data.clientClipBox;
IF NOT partlyClipped AND charBox.smin >= dBB.smin AND charBox.fmin >= dBB.fmin AND charBox.smax <= dBB.smax AND charBox.fmax <= dBB.fmax THEN {
allVisible ← TRUE;
}
ELSE IF charBox.smax <= dBB.smin OR charBox.fmax <= dBB.fmin OR charBox.smin >= dBB.smax OR charBox.fmin > dBB.fmax THEN {
partlyVisible ← FALSE;
};
}
ELSE IF charBox.smax = 0 OR charBox.fmax = 0 THEN partlyVisible ← FALSE
ELSE IF device.class.MaskChar # NIL THEN {
p: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[sMin, fMin, mask.sSizeBB, mask.fSizeBB]];
SELECT ImagerManhattan.IsVisible[p, data.clientClipMask] FROM
invisible => partlyVisible ← FALSE;
visible => allVisible ← TRUE;
ENDCASE => NULL;
ImagerManhattan.Destroy[p];
};
IF allVisible AND (device.class.MaskChar # NIL) THEN device.class.MaskChar[device, s, f, mask]
ELSE IF partlyVisible THEN WITH mask SELECT FROM
raster: REF ImagerDevice.CharMaskRep.raster => {
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: charBox, mask: data.clientClipMask, action: action];
};
TRUSTED {device.class.MaskBits[device: device,
srcBase: @raster[0],
srcWordsPerLine: (raster.fSizeBB+(bitsPerWord-1))/bitsPerWord,
ts: sMin,
tf: fMin,
boxes: boxes
]};
};
runs: REF ImagerDevice.CharMaskRep.runs => {
FOR p: ManhattanPolygon ← data.clientClipMask, p.rest UNTIL p=NIL DO
Runs: PROC [run: ImagerDevice.RunProc] ~ {
ImagerMask.RunsFromCharMask[mask, run, s, f, p.first];
};
bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[p.first];
smin: CARDINAL ~ MAX[charBox.smin, bounds.smin];
smax: CARDINAL ~ MIN[charBox.smax, bounds.smax];
fmin: CARDINAL ~ MAX[charBox.fmin, bounds.fmin];
fmax: CARDINAL ~ MIN[charBox.fmax, bounds.fmax];
IF smin<smax AND fmin<fmax THEN {
device.class.MaskRuns[device: device, bounds: [smin: smin, fmin: fmin, smax: smax, fmax: fmax], runs: Runs];
};
ENDLOOP;
};
ENDCASE => ERROR;
};
RasterShowText: PUBLIC PROC [context: Context,
text: REF READONLY TEXT, start, len: NAT, xrel: BOOL] ~ {
class: Class ~ context.class;
string: XStringProc ~ { ImagerFont.MapText[text, start, len, charAction] };
class.Show[context, string, xrel];
};
END.