ImagerDisplayImpl.mesa
Copyright © 1984 Xerox Corporation. All rights reserved.
Michael Plass, March 19, 1984 5:16:59 pm PST
Doug Wyatt, October 9, 1984 4:22:42 pm PDT
DIRECTORY
Font,
Imager USING [Class, ClassRep, Context, ContextRep, Error, IntKey, RealKey],
ImagerBasic USING [IntPair],
ImagerColor USING [black, Color, ColorRep, MakeGray],
ImagerDisplayPrivate,
ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Difference, Intersection, Polygon],
ImagerMask USING [GenerateRuns, Mask],
ImagerPath USING [Clipper, PathProc],
ImagerPixelArray USING [PixelArray],
ImagerPixelMap USING [DeviceRectangle, PixelMap],
ImagerScanConverter USING [ConvertToManhattanPolygon, CreatePath, DevicePath],
ImagerStroke USING [DevicePathFromStroke, StrokeStyle],
ImagerTransformation USING [Concat, DRound, InverseTransform, PreMultiply, PreRotate, PreScale2, PreTranslate, Scale, SetTrans, Transform, Transformation, TransformationRep, TransformVec],
ImagerUtils USING [ClipRectangleIViaClipRectangle, MaskUnderlineIViaMaskUnderline, MaskVectorIViaMaskStroke, MaskVectorViaMaskStroke, SpaceIViaSpace],
Real USING [RoundI],
RefText,
Rope,
Vector2 USING [Add, Div, InlineAdd, Length, Mul, Sub, VEC];
ImagerDisplayImpl: CEDAR PROGRAM
IMPORTS Font, Imager, ImagerColor, ImagerManhattan, ImagerMask, ImagerDisplayPrivate, ImagerScanConverter, ImagerStroke, ImagerTransformation, ImagerUtils, Real, RefText, Rope, Vector2
EXPORTS ImagerDisplay
~ BEGIN
Context: TYPE ~ Imager.Context;
Display: TYPE ~ ImagerDisplayPrivate.Display;
BYTE: TYPE ~ [0..377B];
CARD: TYPE ~ LONG CARDINAL;
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Vector2.VEC;
IVEC: TYPE ~ ImagerBasic.IntPair;
FONT: TYPE ~ Font.FONT;
Transformation: TYPE ~ ImagerTransformation.Transformation;
TransformationRep: TYPE ~ ImagerTransformation.TransformationRep;
Color: TYPE ~ ImagerColor.Color;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
PathProc: TYPE ~ ImagerPath.PathProc;
Clipper: TYPE ~ ImagerPath.Clipper;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
PersistentVariables: TYPE ~ RECORD[
cp: VEC ← [0, 0], -- current position
correctMeasure: VEC ← [0, 0], -- target line measure for Correct
correctMaskCount: INT ← 0, -- tally number of CorrectMask calls in pass 1
correctSum: VEC ← [0, 0], -- tally adjustable space from CorrectSpace calls in pass 1
correctMask: VEC ← [0, 0], -- amount of space added by each CorrectMask call in pass 2
correctSpace: VEC ← [0, 0] -- fraction of space added by each CorrectSpace call in pass 2
];
NonPersistentVariables: TYPE ~ RECORD[
priorityImportant: INT ← 0, -- if nonzero, priority of masks is important
mediumSize: VEC ← [0, 0], -- size of medium, in meters
fieldMin: VEC ← [0, 0], -- minimum x and y of field, in meters
fieldMax: VEC ← [0, 0], -- maximum x and y of field, in meters
noImage: INT ← 0, -- if nonzero, don't image masks
strokeWidth: REAL ← 0, -- stroke width
strokeEnd: INT ← 0, -- stroke end treatment
underlineStart: REAL ← 0, -- starting x position for underline
amplifySpace: REAL ← 1.0, -- multiplies width of amplified characters
correctPass: INT ← 0, -- which pass, during Correct
correctShrink: REAL ← 0.5, -- allowable space shrink
correctTolerance: VEC ← [0, 0] -- tolerable deviation from correctMeasure
];
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
p: PersistentVariables ← [], -- persistent variables
np: NonPersistentVariables ← [], -- non-persistent variables, except T, font, color, clipper
font: FONTNIL, -- current font ("showVec")
color: Color ← NIL, -- current color
clipper: Clipper ← NIL, -- current clipping outline
clientToView: Transformation ← NIL, -- current client-to-view transformation ("T")
clientToViewID: CARD ← 0, -- unique id for contents of clientToView transformation
lastID: CARD ← 0, -- last value used for clientToViewID
devicePath: ImagerScanConverter.DevicePath ← NIL, -- scratch storage for scan converter
viewToDisplayValid: BOOLFALSE,
viewClipperValid: BOOLFALSE,
clientToDisplayValid: BOOLFALSE,
clientClipperValid: BOOLFALSE,
charToDisplayValid: BOOLFALSE,
displayColorValid: BOOLFALSE,
viewToDisplayEasy: BOOLFALSE, -- valid iff viewToDisplayValid
clientToDisplayEasy: BOOLFALSE, -- valid iff clientToDisplayValid
viewToDisplay: Transformation ← NIL, -- always valid
clientToDisplay: Transformation ← NIL, -- valid iff clientToDisplayValid
charToDisplay: Transformation ← NIL, -- valid iff charToDisplayValid
viewOrigin: IVEC ← [0, 0], -- valid iff viewToDisplayValid AND viewToDisplayEasy
clientOrigin: IVEC ← [0, 0], -- valid iff clientToDisplayValid AND clientToDisplayEasy
viewClipMask: ManhattanPolygon ← NIL, -- display coords; always valid
clientClipMask: ManhattanPolygon ← NIL, -- display coords; valid iff clientClipperValid
viewClipBox: DeviceRectangle ← [0, 0, 0, 0], -- display coords; valid iff viewClipperValid
clientClipBox: DeviceRectangle ← [0, 0, 0, 0], -- display coords; valid iff clientClipperValid
display: Display ← NIL -- the particular display; its color setting is valid iff displayColorValid
];
Create: PROC[display: Display] RETURNS[Context] ~ {
data: Data ~ NEW[DataRep ← []];
data.color ← ImagerColor.black;
data.clientToView ← ImagerTransformation.Scale[1];
data.viewToDisplay ← ImagerTransformation.Scale[1];
data.clientToDisplay ← ImagerTransformation.Scale[1];
data.charToDisplay ← ImagerTransformation.Scale[1];
data.display ← display;
ViewReset[data];
RETURN[NEW[Imager.ContextRep ← [class: displayClass, data: data, props: NIL]]];
};
GetOriginI: PROC[m: Transformation] RETURNS[origin: IVEC] ~ {
origin.x ← Real.RoundI[MIN[MAX[m.c, INTEGER.FIRST], INTEGER.LAST]];
origin.y ← Real.RoundI[MIN[MAX[m.f, INTEGER.FIRST], INTEGER.LAST]];
};
ComputeViewToDisplay: PROC[data: Data] ~ {
m: Transformation ~ data.viewToDisplay;
origin: IVEC ~ GetOriginI[m];
data.viewOrigin ← origin;
data.viewToDisplayEasy ←
m.a=0 AND m.b=-1 AND m.c=origin.y -- s = (0) x + (-1) y + (origin.y) = origin.y-y
AND m.d=1 AND m.e=0 AND m.f=origin.x; -- f = (1) x + (0) y + (origin.x) = origin.x+x
data.viewToDisplayValid ← TRUE;
};
ComputeClientToDisplay: PROC[data: Data] ~ {
m: Transformation ~ data.clientToDisplay;
origin: IVEC;
ValidateViewToDisplay[data];
m^ ← data.viewToDisplay^;
m.PreMultiply[data.clientToView];
origin ← GetOriginI[m];
data.clientOrigin ← origin;
data.clientToDisplayEasy ←
m.a=0 AND m.b=-1 AND m.c=origin.y -- s = (0) x + (-1) y + (origin.y) = origin.y-y
AND m.d=1 AND m.e=0 AND m.f=origin.x; -- f = (1) x + (0) y + (origin.x) = origin.x+x
data.clientToDisplayValid ← TRUE;
};
ComputeCharToDisplay: PROC[data: Data] ~ {
m: Transformation ~ data.charToDisplay;
ValidateViewToDisplay[data];
m^ ← data.viewToDisplay^;
m.PreMultiply[data.clientToView];
m.SetTrans[[0, 0]];
m.PreMultiply[data.font.charToClient];
data.charToDisplayValid ← TRUE;
};
ComputeViewClipper: PROC[data: Data] ~ {
data.viewClipBox ← ImagerManhattan.BoundingBox[data.viewClipMask];
data.viewClipperValid ← TRUE;
};
ComputeClientClipper: PROC[data: Data] ~ {
CAUTION: this may clobber data.devicePath!
ImagerManhattan.Destroy[data.clientClipMask];
ValidateViewClipper[data];
data.clientClipMask ← ImagerManhattan.Copy[data.viewClipMask];
FOR list: Clipper ← data.clipper, list.rest UNTIL list=NIL DO
old, this, new: ManhattanPolygon;
old ← data.clientClipMask;
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: list.first.outline.pathProc, pathData: list.first.outline.pathData,
transformation: data.viewToDisplay,
clipBox: data.viewClipBox, scratch: data.devicePath];
this ← ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.clientClipBox];
IF list.first.exclude THEN new ← old.Difference[this] ELSE new ← old.Intersection[this];
data.clientClipMask ← new;
ImagerManhattan.Destroy[old];
ImagerManhattan.Destroy[this];
ENDLOOP;
data.clientClipBox ← ImagerManhattan.BoundingBox[data.clientClipMask];
data.clientClipperValid ← TRUE;
};
SetDisplayColor: PROC[data: Data] ~ {
data.display.SetColor[data.color, data.viewOrigin];
data.displayColorValid ← TRUE;
};
ValidateViewToDisplay: PROC[data: Data] ~ INLINE {
IF data.viewToDisplayValid THEN NULL ELSE ComputeViewToDisplay[data];
};
ValidateClientToDisplay: PROC[data: Data] ~ INLINE {
IF data.clientToDisplayValid THEN NULL ELSE ComputeClientToDisplay[data];
};
ValidateCharToDisplay: PROC[data: Data] ~ INLINE {
IF data.charToDisplayValid THEN NULL ELSE ComputeCharToDisplay[data];
};
ValidateViewClipper: PROC[data: Data] ~ INLINE {
IF data.viewClipperValid THEN NULL ELSE ComputeViewClipper[data];
};
ValidateClientClipper: PROC[data: Data] ~ INLINE {
IF data.clientClipperValid THEN NULL ELSE ComputeClientClipper[data];
};
ValidateDisplayColor: PROC[data: Data] ~ INLINE {
IF data.displayColorValid THEN NULL ELSE SetDisplayColor[data];
};
DoSave: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
np: NonPersistentVariables ~ data.np;
clientToViewID: CARD ~ data.clientToViewID;
clientToViewRep: TransformationRep ~ data.clientToView^;
font: FONT ~ data.font;
color: Color ~ data.color;
clipper: Clipper ~ data.clipper;
Restore: PROC ~ {
data.np ← np;
IF clientToViewID#data.clientToViewID THEN {
data.clientToView^ ← clientToViewRep;
data.clientToViewID ← clientToViewID;
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
};
IF font#data.font THEN {
data.font ← font;
data.charToDisplayValid ← FALSE;
};
IF color#data.color THEN {
data.color ← color;
data.displayColorValid ← FALSE;
};
IF clipper#data.clipper THEN {
data.clipper ← clipper;
data.clientClipperValid ← FALSE;
};
};
body[! UNWIND => Restore[]];
Restore[];
};
DoSaveAll: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
p: PersistentVariables ~ data.p;
DoSave[context, body ! UNWIND => data.p ← p];
data.p ← p;
};
ChangedClientToView: PROC[data: Data] ~ INLINE {
data.clientToViewID ← data.lastID ← data.lastID+1;
};
SetTransformation: PROC[context: Context, transformation: Transformation] ~ {
data: Data ~ NARROW[context.data];
data.clientToView^ ← transformation^;
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
};
SetFont: PROC[context: Context, font: FONT] ~ {
data: Data ~ NARROW[context.data];
IF font#data.font THEN {
data.font ← font;
data.charToDisplayValid ← FALSE;
};
};
SetColor: PROC[context: Context, color: Color] ~ {
data: Data ~ NARROW[context.data];
IF color#data.color THEN {
data.color ← color;
data.displayColorValid ← FALSE;
};
};
SetClipper: PROC[context: Context, clipper: Clipper] ~ {
data: Data ~ NARROW[context.data];
IF clipper#data.clipper THEN {
data.clipper ← clipper;
data.clientClipperValid ← FALSE;
};
};
SetReal: PROC[context: Context, key: Imager.RealKey, value: REAL] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => data.p.cp.x ← value;
$DCScpy => data.p.cp.y ← value;
$correctMX => data.p.correctMeasure.x ← value;
$correctMY => data.p.correctMeasure.y ← value;
$mediumXSize => data.np.mediumSize.x ← value;
$mediumYSize => data.np.mediumSize.y ← value;
$fieldXMin => data.np.fieldMin.x ← value;
$fieldYMin => data.np.fieldMin.y ← value;
$fieldXMax => data.np.fieldMax.x ← value;
$fieldYMax => data.np.fieldMax.y ← value;
$strokeWidth => data.np.strokeWidth ← value;
$underlineStart => data.np.underlineStart ← value;
$amplifySpace => data.np.amplifySpace ← value;
$correctShrink => data.np.correctShrink ← value;
$correctTX => data.np.correctTolerance.x ← value;
$correctTY => data.np.correctTolerance.y ← value;
ENDCASE => ERROR Imager.Error[$Bug];
};
SetInt: PROC[context: Context, key: Imager.IntKey, value: INT] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$priorityImportant => data.np.priorityImportant ← value;
$noImage => data.np.noImage ← value;
$strokeEnd => data.np.strokeEnd ← value;
$correctPass => data.np.correctPass ← value;
ENDCASE => ERROR Imager.Error[$Bug];
};
GetTransformation: PROC[context: Context] RETURNS[Transformation] ~ {
data: Data ~ NARROW[context.data];
RETURN[NEW[TransformationRep ← data.clientToView^]];
};
GetFont: PROC[context: Context] RETURNS[FONT] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.font];
};
GetColor: PROC[context: Context] RETURNS[Color] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.color];
};
GetClipper: PROC[context: Context] RETURNS[Clipper] ~ {
data: Data ~ NARROW[context.data];
ERROR Imager.Error[$Unimplemented];
};
GetReal: PROC[context: Context, key: Imager.RealKey] RETURNS[REAL] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => RETURN[data.p.cp.x];
$DCScpy => RETURN[data.p.cp.y];
$correctMX => RETURN[data.p.correctMeasure.x];
$correctMY => RETURN[data.p.correctMeasure.y];
$mediumXSize => RETURN[data.np.mediumSize.x];
$mediumYSize => RETURN[data.np.mediumSize.y];
$fieldXMin => RETURN[data.np.fieldMin.x];
$fieldYMin => RETURN[data.np.fieldMin.y];
$fieldXMax => RETURN[data.np.fieldMax.x];
$fieldYMax => RETURN[data.np.fieldMax.y];
$strokeWidth => RETURN[data.np.strokeWidth];
$underlineStart => RETURN[data.np.underlineStart];
$amplifySpace => RETURN[data.np.amplifySpace];
$correctShrink => RETURN[data.np.correctShrink];
$correctTX => RETURN[data.np.correctTolerance.x];
$correctTY => RETURN[data.np.correctTolerance.y];
ENDCASE => ERROR Imager.Error[$Bug];
};
GetInt: PROC[context: Context, key: Imager.IntKey] RETURNS[INT] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$priorityImportant => RETURN[data.np.priorityImportant];
$noImage => RETURN[data.np.noImage];
$strokeEnd => RETURN[data.np.strokeEnd];
$correctPass => RETURN[data.np.correctPass];
ENDCASE => ERROR Imager.Error[$Bug];
};
GetCP: PROC[context: Context] RETURNS[VEC] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.clientToView.InverseTransform[data.p.cp]];
};
GetCPRounded: PROC[context: Context] RETURNS[VEC] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.clientToView.InverseTransform[ImagerTransformation.DRound[data.p.cp]]];
};
ConcatT: PROC[context: Context, m: Transformation] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreMultiply[m];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
};
Scale2T: PROC[context: Context, sx, sy: REAL] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreScale2[sx, sy];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
};
RotateT: PROC[context: Context, a: REAL] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreRotate[a];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
};
TranslateT: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreTranslate[x, y];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
};
Move: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.SetTrans[data.p.cp];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
};
Trans: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.SetTrans[ImagerTransformation.DRound[data.p.cp]];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
};
SetGray: PROC[context: Context, f: REAL] ~ {
data: Data ~ NARROW[context.data];
data.color ← ImagerColor.MakeGray[f];
data.displayColorValid ← FALSE;
};
SetSampledColor: PROC[context: Context, pa: PixelArray, m: Transformation, op: ATOM] ~ {
data: Data ~ NARROW[context.data];
colorToView: Transformation ~ m.Concat[data.clientToView];
color: Color ~ NEW[ImagerColor.ColorRep[sampled] ← [sampled[
pa: pa, m: colorToView, transparent: FALSE, colorOperator: op]]];
data.color ← color;
data.displayColorValid ← FALSE;
};
StartUnderline: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
data.np.underlineStart ← data.clientToView.InverseTransform[data.p.cp].x;
};
MaskUnderline: PROC[context: Context, dy, h: REAL] ~ {
data: Data ~ NARROW[context.data];
end: VEC ~ data.clientToView.InverseTransform[data.p.cp]; -- current position (client space)
start: VEC ~ [data.np.underlineStart, end.y-dy-h]; -- corner of underline (client space)
underline: PROC ~ {
data.clientToView.SetTrans[ImagerTransformation.DRound[start]];
ChangedClientToView[data];
data.clientToDisplayValid ← FALSE;
MaskRectangle[context, 0, 0, end.x-start.x, h];
};
DoSave[context, underline];
};
SetXY: PROC[context: Context, p: VEC] ~ {
data: Data ~ NARROW[context.data];
data.p.cp ← data.clientToView.Transform[p];
};
SetXYI: PROC[context: Context, x, y: INTEGER] ~ {
data: Data ~ NARROW[context.data];
data.p.cp ← data.clientToView.Transform[[x, y]];
};
SetXYRel: PROC[context: Context, v: VEC] ~ {
data: Data ~ NARROW[context.data];
data.p.cp ← data.p.cp.InlineAdd[data.clientToView.TransformVec[v]];
};
SetXYRelI: PROC[context: Context, x, y: INTEGER] ~ {
data: Data ~ NARROW[context.data];
data.p.cp ← data.p.cp.InlineAdd[data.clientToView.TransformVec[[x, y]]];
};
ClipOutline: PROC[context: Context, pathProc: PathProc, pathData: REF, exclude: BOOL] ~ {
ERROR Imager.Error[$Unimplemented];
};
ClipRectangle: PROC[context: Context, x, y, w, h: REAL, exclude: BOOL] ~ {
ERROR Imager.Error[$Unimplemented];
};
CorrectMask: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
IF data.np.correctPass=0 THEN NULL
ELSE {
SELECT data.np.correctPass FROM
1 => data.p.correctMaskCount ← data.p.correctMaskCount+1;
2 => IF data.p.correctMaskCount#0 THEN {
data.p.cp ← data.p.cp.InlineAdd[data.p.correctMask];
data.p.correctMaskCount ← data.p.correctMaskCount-1;
};
ENDCASE;
};
};
VMul: PROC[a, b: VEC] RETURNS[VEC] ~ INLINE { RETURN[[a.x*b.x, a.y*b.y]] };
multiplies vectors element-by-element
CorrectSpace: PROC[context: Context, v: VEC] ~ {
data: Data ~ NARROW[context.data];
IF data.np.correctPass=0 THEN NULL
ELSE {
s: VEC ~ data.clientToView.TransformVec[v];
SELECT data.np.correctPass FROM
1 => data.p.correctSum ← data.p.correctSum.InlineAdd[s];
2 => data.p.cp ← data.p.cp.InlineAdd[VMul[s, data.p.correctSpace]];
ENDCASE;
};
};
Space: PROC[context: Context, x: REAL] ~ {
data: Data ~ NARROW[context.data];
s: VEC ~ data.clientToView.TransformVec[[x, 0]];
data.p.cp ← data.p.cp.InlineAdd[s];
SELECT data.np.correctPass FROM
0 => NULL;
1 => data.p.correctSum ← data.p.correctSum.InlineAdd[s];
2 => data.p.cp ← data.p.cp.InlineAdd[VMul[s, data.p.correctSpace]];
ENDCASE;
};
SetCorrectMeasure: PROC[context: Context, v: VEC] ~ {
data: Data ~ NARROW[context.data];
data.p.correctMeasure ← data.clientToView.TransformVec[v];
};
SetCorrectTolerance: PROC[context: Context, v: VEC] ~ {
data: Data ~ NARROW[context.data];
data.np.correctTolerance ← data.clientToView.TransformVec[v];
};
Correct: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
shrink: REAL ~ data.np.correctShrink;
tolerance: VEC ~ data.np.correctTolerance;
start, end, measure, target, correction: VEC;
mask, space: VEC ← [0, 0];
data.p.correctMaskCount ← 0;
data.p.correctSum ← [0, 0];
data.np.noImage ← 1;
data.np.correctPass ← 1;
start ← data.p.cp; -- starting position
DoSave[context, body]; -- pass 1
end ← data.p.cp; -- ending position
measure ← data.p.correctMeasure; -- desired measure (note: may be set by pass 1)
target ← start.Add[measure]; -- target position
correction ← target.Sub[end]; -- amount of correction needed (end + correction = target)
IF correction.Length<=tolerance.Length THEN NULL -- close enough
ELSE IF end.Sub[start].Length<measure.Length THEN { -- must expand
space ← correction;
}
ELSE { -- must shrink
space ← correction;
IF space.Length>(shrink*data.p.correctSum.Length) THEN {
mask ← correction.Add[data.p.correctSum.Mul[shrink]];
space ← correction.Sub[mask];
};
};
IF data.p.correctSum.x#0 THEN space.x ← space.x/data.p.correctSum.x
ELSE IF space.x#0 THEN { mask.x ← mask.x+space.x; space.x ← 0 };
IF data.p.correctSum.y#0 THEN space.y ← space.y/data.p.correctSum.y
ELSE IF space.y#0 THEN { mask.y ← mask.y+space.y; space.y ← 0 };
IF data.p.correctMaskCount#0 THEN {
IF mask.x=0 AND mask.y=0 THEN data.p.correctMaskCount ← 0
ELSE IF data.p.correctMaskCount>1 THEN {
data.p.correctMaskCount ← data.p.correctMaskCount-1;
mask ← mask.Div[data.p.correctMaskCount];
};
};
data.p.correctMask ← mask;
data.p.correctSpace ← space;
data.np.noImage ← 0;
data.np.correctPass ← 2;
data.p.cp ← start;
DoSave[context, body]; -- pass 2
end ← data.p.cp; -- ending position
data.p.cp ← target;
IF target.Sub[end].Length>tolerance.Length THEN
ERROR Imager.Error[$UnableToProperlyAdjustMaskPositions];
};
MaskDevicePath: PROC[data: Data, parity: BOOLFALSE] ~ {
IF data.clientClipperValid THEN {
Runs: PROC[run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ {
ImagerMask.GenerateRuns[
mask: data.devicePath, clipper: data.clientClipMask, runProc: run];
};
ValidateDisplayColor[data]; -- notify display if color changed
data.display.MaskRuns[Runs];
}
ELSE ERROR Imager.Error[$Bug]; -- clipper should have been validated
};
MaskFill: PROC[context: Context, pathProc: PathProc, pathData: REF, parity: BOOL] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage]
IF data.np.noImage=0 THEN {
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: pathProc, pathData: pathData,
transformation: data.clientToDisplay,
clipBox: data.clientClipBox, scratch: data.devicePath];
MaskDevicePath[data, parity];
};
};
MaskStroke: PROC[context: Context, pathProc: PathProc, pathData: REF, closed: BOOL] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage, strokeWidth, strokeEnd]
IF data.np.noImage=0 THEN {
style: ImagerStroke.StrokeStyle ~ SELECT data.np.strokeEnd FROM
0 => square, 1 => butt, 2 => round, ENDCASE => roundWithRoundJoints;
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
data.devicePath ← ImagerStroke.DevicePathFromStroke[
pathProc: pathProc, pathData: pathData,
width: data.np.strokeWidth, style: style, closed: closed,
clientToDevice: data.clientToDisplay,
clipBox: data.clientClipBox, scratch: data.devicePath];
MaskDevicePath[data];
};
};
MaskRectangle: PROC[context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage]
IF data.np.noImage=0 THEN {
rectangle: PathProc ~ {
moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]];
};
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: rectangle, pathData: NIL,
transformation: data.clientToDisplay,
clipBox: data.clientClipBox, scratch: data.devicePath];
MaskDevicePath[data];
};
};
MaskRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage]
IF data.np.noImage=0 THEN {
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
IF data.clientToDisplayEasy THEN {
origin: IVEC ~ data.clientOrigin;
xmin, xmax: INTEGER ← x;
ymin, ymax: INTEGER ← y;
mask: ManhattanPolygon;
IF w<0 THEN xmin ← xmax+w ELSE xmax ← xmin+w;
IF h<0 THEN ymin ← ymax+h ELSE ymax ← ymin+h;
mask ← ImagerManhattan.CreateFromBox[[
sMin: origin.y-ymax, fMin: origin.x+xmin,
sSize: ymax-ymin, fSize: xmax-xmin]];
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ValidateDisplayColor[data]; -- notify display if color changed
data.display.ApplyMask[data.display, mask, data.clientClipMask];
}
ELSE
MaskRectangle[context, x, y, w, h];
};
};
MaskPixel: PROC[context: Context, pa: PixelArray] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage]
IF data.np.noImage=0 THEN {
paToDisplay: Transformation ← NIL;
pixelMask: ImagerMask.Mask ← NIL;
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ERROR;
paToDisplay ← pa.m.Concat[data.clientToDisplay];
pixelMask ← ImagerMask.FromPixelArray[pa, paToDisplay];
ValidateDisplayColor[data]; -- notify display if color changed
data.display.ApplyMask[data.display, pixelMask, data.clientClipMask];
};
};
MaskBits: PROC[context: Context, base: LONG POINTER, wordsPerLine: NAT,
sMin, fMin, sSize, fSize: NAT, sOffset, fOffset: INTEGER ← 0] ~ {
data: Data ~ NARROW[context.data];
USING [T, color, clipper, noImage]
IF data.np.noImage=0 THEN {
pixelMask: ImagerMask.Mask ← NIL;
ValidateClientToDisplay[data]; -- validate data.clientToDisplay
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ERROR;
pixelMap ← xxx;
pixelMask ← ImagerMask.FromBitmap[pixelMap];
ValidateDisplayColor[data]; -- notify display if color changed
data.display.ApplyMask[data.display, pixelMask, data.clientClipMask];
};
};
ShowChar: PROC[context: Context, char: CHAR, charSet: BYTE ← 0] ~ {
data: Data ~ NARROW[context.data];
USING [T, font, color, clipper, noImage]
font: FONT ~ data.font;
code: Font.Char ~ charSet*256+ORD[char];
action: PROC ~ {
width: VEC ← font.Width[code];
IF font.Amplified[code] THEN width ← width.Mul[data.np.amplifySpace];
Trans[context];
font.MaskChar[code, context];
SetXYRel[context, width];
SELECT font.Correction[code] FROM
none => NULL;
space => CorrectSpace[context, width];
mask => CorrectMask[context];
ENDCASE => ERROR Imager.Error[$Bug];
};
DoSave[context, action];
};
ShowRope: PROC[context: Context, rope: ROPE, start, len: INT, charSet: BYTE] ~ {
action: Rope.ActionType ~ { ShowChar[context, c] };
[] ← Rope.Map[base: rope, start: start, len: len, action: action];
};
ShowText: PROC[context: Context, text: REF READONLY TEXT, start, len: NAT, charSet: BYTE] ~ {
action: Rope.ActionType ~ { ShowChar[context, c] };
[] ← RefText.Map[s: text, start: start, len: len, action: action];
};
StringState: TYPE ~ RECORD[mode: StringMode, offset: CARDINAL];
StringMode: TYPE ~ {run, esc, esc2, ext, ext2};
ShowText: PROC[context: Context, text: REF READONLY TEXT, start, len: NAT, charSet: BYTE] ~ {
data: Data ~ NARROW[context.data];
USING [cp, T, font, color, clipper, noImage, amplifySpace]
rem: NAT ~ text.length-start;
offset: CARDINAL ← set;
state: {run, esc, esc2, ext, ext2} ← run;
deviceCP: VEC;
Set: PROC[set: CARDINAL] ~ INLINE { IF set#offset THEN NewFontSet[offset ← set] };
Char: PROC[char: CHAR] ~ {
info: CharInfo ~ GetCharInfo[char, offset];
width: VEC;
IF data.np.noImage=0 THEN {
data.display.ApplyMask[data.display, info.mask, data.clientClipMask];
};
width ← info.width; -- display coords
IF info.amplified THEN width ← width.InlineMul[data.np.amplifySpace];
data.p.cp ← data.p.cp.InlineAdd[width];
IF data.np.correctPass#0 THEN SELECT info.correction FROM
none => NULL;
mask => { -- correct mask -- };
space => { -- correct space -- };
ENDCASE;
};
Pass1: PROC[char: CHAR] ~ {
info: CharInfo ~ GetCharInfo[char];
width: VEC ← info.width; -- in view coords!
IF info.amplified THEN width ← width.InlineMul[data.np.amplifySpace];
data.p.cp ← data.p.cp.InlineAdd[width];
SELECT info.correction FROM
none => NULL;
mask => data.p.correctMaskCount ← data.p.correctMaskCount+1;
space => data.p.correctSum ← data.p.correctSum.InlineAdd[width];
ENDCASE;
};
ValidateCharToDisplay[data];
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ValidateDisplayColor[data]; -- notify display if color changed
FOR i: NAT IN[start..start+MIN[rem, len]) DO
char: CHAR ~ text[i];
SELECT state FROM
run => IF char='\377 THEN state ← esc ELSE Char[char];
esc => IF char='\377 THEN state ← esc2 ELSE { Set[ORD[char]]; state ← run };
esc2 => IF char='\000 THEN state ← ext ELSE ERROR Imager.Error[$InvalidString];
ext => IF char='\377 THEN state ← esc ELSE { Set[ORD[char]]; state ← ext2 };
ext2 => { Char[char]; state ← ext };
ENDCASE => ERROR;
ENDLOOP;
IF NOT(state=run OR state=ext) THEN ERROR Imager.Error[$InvalidString];
};
ViewReset: PROC[data: Data] ~ {
data.viewToDisplay^ ← data.display.surfaceToDisplay^;
data.viewToDisplayValid ← FALSE;
data.clientToDisplayValid ← FALSE;
data.charToDisplayValid ← FALSE;
ImagerManhattan.Destroy[data.viewClipMask];
data.viewClipMask ← ImagerManhattan.CreateFromBox[data.display.clipBox];
data.viewClipperValid ← FALSE;
data.clientClipperValid ← FALSE;
};
ViewTranslate: PUBLIC PROC[context: Context, x, y: INTEGER] ~ {
WITH context.data SELECT FROM
data: Data => {
data.viewToDisplay.PreTranslate[x, y];
data.viewToDisplayValid ← FALSE;
data.clientToDisplayValid ← FALSE;
Note: a translation doesn't invalidate fontTransformation
data.clientClipperValid ← FALSE;
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ViewClip: PUBLIC PROC[context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ {
WITH context.data SELECT FROM
data: Data => {
old, this, new: ManhattanPolygon;
old ← data.viewClipMask;
ValidateViewToDisplay[data];
IF data.viewToDisplayEasy THEN {
origin: IVEC ~ data.viewOrigin;
xmin, xmax: INTEGER ← x;
ymin, ymax: INTEGER ← y;
IF w<0 THEN xmin ← xmax+w ELSE xmax ← xmin+w;
IF h<0 THEN ymin ← ymax+h ELSE ymax ← ymin+h;
this ← ImagerManhattan.CreateFromBox[[
sMin: origin.y-ymax, fMin: origin.x+xmin,
sSize: ymax-ymin, fSize: xmax-xmin]];
}
ELSE {
rectangle: PathProc ~ {
moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]];
};
displayClipBox: DeviceRectangle ~ data.display.clipBox;
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: rectangle, pathData: NIL,
transformation: data.viewToDisplay,
clipBox: displayClipBox, scratch: data.devicePath];
this ← ImagerScanConverter.ConvertToManhattanPolygon[
devicePath: data.devicePath, clipBox: displayClipBox];
};
IF exclude THEN new ← old.Difference[this] ELSE new ← old.Intersection[this];
data.viewClipMask ← new;
ImagerManhattan.Destroy[old];
ImagerManhattan.Destroy[this];
data.viewClipperValid ← FALSE;
data.clientClipperValid ← FALSE;
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
MoveSurfaceRectangle: PROC[context: Context, source: IntRectangle, dest: IntPair] ~ {
WITH context.data SELECT FROM
data: Data => {
m: Transformation ~ SurfaceToDevice[data];
sMinDest, fMinDest, sMinSource, fMinSource, sSize, fSize: INTEGER;
DoMove: PROC ~ {
IF data.class.viewUnitsPerPixel # 1 THEN {
sMinDest ← sMinDest / data.class.viewUnitsPerPixel;
fMinDest ← fMinDest / data.class.viewUnitsPerPixel;
sMinSource ← sMinSource / data.class.viewUnitsPerPixel;
fMinSource ← fMinSource / data.class.viewUnitsPerPixel;
sSize ← sSize / data.class.viewUnitsPerPixel;
fSize ← fSize / data.class.viewUnitsPerPixel;
};
FOR i: NAT IN [0..data.numberOfSeparations) DO
data[i].Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data[i].ShiftMap[sMinSource-sMinDest, fMinSource-fMinDest]];
ENDLOOP;
};
[[sMinDest, fMinDest, sSize, fSize]] ←
m.TransformIntRectangle[[dest.x, dest.y, source.w, source.h]];
[[sMinSource, fMinSource, sSize, fSize]] ←
m.TransformIntRectangle[[source.x, source.y, source.w, source.h]];
data.class.DoUnderLock[data, DoMove, [MIN[sMinDest, sMinSource], MIN[fMinDest, fMinSource], sSize+ABS[sMinSource-sMinDest], fSize+ABS[fMinSource-fMinDest]]];
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ {
data: Data ~ NARROW[context.data];
data: Data ~ NARROW[context.data];
trans: TransformationRec ~ CompositeT[data, data];
manhattanPolygon: ImagerManhattan.Polygon ← NIL;
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: INTEGER ~ Round[trans.b * y + trans.c];
s1: INTEGER ~ Round[trans.b * (y+h) + trans.c];
f0: INTEGER ~ Round[trans.d * x + trans.f];
f1: INTEGER ~ Round[trans.d * (x+w) + trans.f];
sMin: INTEGER ~ MIN[s0, s1];
sMax: INTEGER ~ MAX[s0, s1];
fMin: INTEGER ~ MIN[f0, f1];
fMax: INTEGER ~ MAX[f0, f1];
IF sMax<=sMin OR fMax<=fMin THEN RETURN [invisible];
manhattanPolygon ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
bb: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipMask];
devicePath: DevicePath ← DevicePathFromPath[PathMap, NIL, CompositeT[data, context.data], bb];
manhattanPolygon ← ImagerScanConverter.ConvertToManhattanPolygon[devicePath, bb];
};
ValidateClientClipper[context, data];
visibility ← ImagerManhattan.IsVisible[manhattanPolygon, data.clientClipMask];
ImagerManhattan.Destroy[manhattanPolygon];
};
GetSurfaceBounds: PROC[context: Context] RETURNS[IntRectangle] ~ {
WITH context.data SELECT FROM
data: Data => RETURN[[0, 0, data.surfaceWidth, data.surfaceHeight]];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
displayClass: Imager.Class ~ NEW[Imager.ClassRep ← [
type: $Display,
DoSave: DoSave,
DoSaveAll: DoSaveAll,
SetTransformation: SetTransformation,
SetFont: SetFont,
SetColor: SetColor,
SetClipper: SetClipper,
SetReal: SetReal,
SetInt: SetInt,
ConcatT: ConcatT,
Scale2T: Scale2T,
RotateT: RotateT,
TranslateT: TranslateT,
Move: Move,
Trans: Trans,
SetGray: SetGray,
SetSampledColor: SetSampledColor,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskRectangle: MaskRectangle,
MaskRectangleI: MaskRectangleI,
MaskVector: ImagerUtils.MaskVectorViaMaskStroke,
MaskVectorI: ImagerUtils.MaskVectorIViaMaskStroke,
StartUnderline: StartUnderline,
MaskUnderline: MaskUnderline,
MaskUnderlineI: ImagerUtils.MaskUnderlineIViaMaskUnderline,
MaskPixel: MaskPixel,
MaskBits: MaskBits,
SetXY: SetXY,
SetXYI: SetXYI,
SetXYRel: SetXYRel,
SetXYRelI: SetXYRelI,
ShowChar: ShowChar,
ShowRope: ShowRope,
ShowText: ShowText,
ClipOutline: ClipOutline,
ClipRectangle: ClipRectangle,
ClipRectangleI: ImagerUtils.ClipRectangleIViaClipRectangle,
CorrectMask: CorrectMask,
CorrectSpace: CorrectSpace,
Space: Space,
SpaceI: ImagerUtils.SpaceIViaSpace,
SetCorrectMeasure: SetCorrectMeasure,
SetCorrectTolerance: SetCorrectTolerance,
Correct: Correct,
GetTransformation: GetTransformation,
GetFont: GetFont,
GetColor: GetColor,
GetClipper: GetClipper,
GetReal: GetReal,
GetInt: GetInt,
GetCP: GetCP,
GetCPRounded: GetCPRounded,
props: NIL
]];
END.