ImagerRasterImpl.mesa
Copyright © 1984 Xerox Corporation. All rights reserved.
Michael Plass, March 19, 1984 5:16:59 pm PST
Doug Wyatt, November 30, 1984 2:32:08 pm PST
DIRECTORY
Imager USING [Class, ClassRep, Context, ContextRep, Error, IntKey, metersPerInch, RealKey],
ImagerColor USING [black, Color, ColorOperator, ColorRep, MakeGray, MakeSampledColor],
ImagerDevice USING [Device, MaskRuns, SetColor, SetPriority],
ImagerFont USING [Amplified, Char, CharSet, Correction, Font, MaskChar, Width],
ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Difference, Intersection, Polygon],
ImagerPath USING [Clipper, Outline, PathProc, TransformToOutline],
ImagerPixelArray USING [PixelArray],
ImagerPixelMap USING [DeviceRectangle, PixelMap],
ImagerRaster USING [],
ImagerRasterPrivate USING [CARD, Data, DataRep, IVEC, NonPersistentVariables, PersistentVariables],
ImagerScanConverter USING [ConvertToManhattanPolygon, ConvertToRuns, CreatePath, DevicePath],
ImagerStroke USING [DevicePathFromStroke, StrokeStyle],
ImagerTransformation USING [Concat, DRound, InverseTransform, PreMultiply, PreRotate, PreScale2, PreTranslate, Scale, Scale2, SetTrans, Transform, Transformation, TransformationRep, TransformVec],
ImagerUtils USING [ClipRectangleViaClipOutline, ClipRectangleIViaClipOutline, MaskUnderlineIViaMaskUnderline, MaskVectorIViaMaskStroke, MaskVectorViaMaskStroke, SpaceIViaSpace],
Real USING [RoundI],
Rope USING [ROPE],
Vector2 USING [Add, Div, InlineAdd, Length, Mul, Sub, VEC];
ImagerRasterImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerColor, ImagerFont, ImagerManhattan, ImagerPath, ImagerDevice, ImagerScanConverter, ImagerStroke, ImagerTransformation, ImagerUtils, Real, Vector2
EXPORTS ImagerRaster, ImagerRasterPrivate
~ BEGIN OPEN ImagerRasterPrivate;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDevice.Device;
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Vector2.VEC;
Transformation: TYPE ~ ImagerTransformation.Transformation;
TransformationRep: TYPE ~ ImagerTransformation.TransformationRep;
Font: TYPE ~ ImagerFont.Font;
Char: TYPE ~ ImagerFont.Char;
CharSet: TYPE ~ ImagerFont.CharSet;
Color: TYPE ~ ImagerColor.Color;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
PathProc: TYPE ~ ImagerPath.PathProc;
Clipper: TYPE ~ ImagerPath.Clipper;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
DeviceRectangle:
TYPE ~ ImagerPixelMap.DeviceRectangle;
Create:
PUBLIC
PROC[device: Device]
RETURNS[Context] ~ {
RETURN[NEW[Imager.ContextRep ← [class: rasterClass, data: CreateData[device]]]];
};
CreateClass:
PUBLIC
PROC[type:
ATOM]
RETURNS[Imager.Class] ~ {
class: Imager.Class ~ NEW[Imager.ClassRep ← rasterClass^];
class.type ← type;
RETURN[class];
};
CreateData:
PUBLIC
PROC[device: Device]
RETURNS[Data] ~ {
data: Data ~ NEW[DataRep ← [device: device]];
metersToSurface: VEC ~ device.surfaceUnitsPerInch.Div[Imager.metersPerInch];
data.clientToView ← ImagerTransformation.Scale2[metersToSurface];
data.viewToDevice ← ImagerTransformation.Scale[1];
data.clientToDevice ← ImagerTransformation.Scale[1];
data.charToDevice ← ImagerTransformation.Scale[1];
data.color ← ImagerColor.black;
InternalViewReset[data];
RETURN[data];
};
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]];
};
ComputeViewToDevice:
PROC[data: Data] ~ {
m: Transformation ~ data.viewToDevice;
origin: IVEC ~ GetOriginI[m];
data.viewOrigin ← origin;
data.viewToDeviceEasy ←
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.viewToDeviceValid ← TRUE;
};
ComputeClientToDevice:
PROC[data: Data] ~ {
m: Transformation ~ data.clientToDevice;
origin: IVEC;
ValidateViewToDevice[data];
m^ ← data.viewToDevice^;
m.PreMultiply[data.clientToView];
origin ← GetOriginI[m];
data.clientOrigin ← origin;
data.clientToDeviceEasy ←
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.clientToDeviceValid ← TRUE;
};
ComputeCharToDevice:
PROC[data: Data] ~ {
m: Transformation ~ data.charToDevice;
ValidateViewToDevice[data];
m^ ← data.viewToDevice^;
m.PreMultiply[data.clientToView];
m.SetTrans[[0, 0]];
m.PreMultiply[data.font.charToClient];
data.charToDeviceValid ← 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.viewToDevice,
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;
};
SetDeviceColor:
PROC[data: Data] ~ {
data.device.SetColor[data.color, data.viewToDevice];
data.deviceColorValid ← TRUE;
};
SetDevicePriority:
PROC[data: Data] ~ {
data.device.SetPriority[data.np.priorityImportant#0];
data.devicePriorityValid ← TRUE;
};
ValidateViewToDevice:
PROC[data: Data] ~
INLINE {
IF data.viewToDeviceValid THEN NULL ELSE ComputeViewToDevice[data];
};
ValidateClientToDevice:
PROC[data: Data] ~
INLINE {
IF data.clientToDeviceValid THEN NULL ELSE ComputeClientToDevice[data];
};
ValidateCharToDevice:
PROC[data: Data] ~
INLINE {
IF data.charToDeviceValid THEN NULL ELSE ComputeCharToDevice[data];
};
ValidateViewClipper:
PROC[data: Data] ~
INLINE {
IF data.viewClipperValid THEN NULL ELSE ComputeViewClipper[data];
};
ValidateClientClipper:
PROC[data: Data] ~
INLINE {
CAUTION: this may clobber data.devicePath!
IF data.clientClipperValid THEN NULL ELSE ComputeClientClipper[data];
};
ValidateDeviceState:
PROC[data: Data] ~
INLINE {
IF data.deviceColorValid THEN NULL ELSE SetDeviceColor[data];
IF data.devicePriorityValid THEN NULL ELSE SetDevicePriority[data];
};
DoSave:
PROC[context: Context, action:
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 ~ {
IF np.priorityImportant#data.np.priorityImportant
THEN {
data.devicePriorityValid ← FALSE;
};
data.np ← np;
IF clientToViewID#data.clientToViewID
THEN {
data.clientToView^ ← clientToViewRep;
data.clientToViewID ← clientToViewID;
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
};
IF font#data.font
THEN {
data.font ← font;
data.charToDeviceValid ← FALSE;
};
IF color#data.color
THEN {
data.color ← color;
data.deviceColorValid ← FALSE;
};
IF clipper#data.clipper
THEN {
data.clipper ← clipper;
data.clientClipperValid ← FALSE;
};
};
action[! UNWIND => Restore[]];
Restore[];
};
DoSaveAll:
PROC[context: Context, action:
PROC] ~ {
data: Data ~ NARROW[context.data];
p: PersistentVariables ~ data.p;
DoSave[context, action ! UNWIND => data.p ← p];
data.p ← p;
};
ChangedClientToView:
PROC[data: Data] ~
INLINE {
data.clientToViewID ← data.lastID ← data.lastID+1;
};
SetT:
PROC[context: Context, m: Transformation] ~ {
data: Data ~ NARROW[context.data];
data.clientToView^ ← m^;
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
};
SetFont:
PROC[context: Context, font: Font] ~ {
data: Data ~ NARROW[context.data];
IF font#data.font
THEN {
data.font ← font;
data.charToDeviceValid ← FALSE;
};
};
SetColor:
PROC[context: Context, color: Color] ~ {
data: Data ~ NARROW[context.data];
IF color#data.color
THEN {
data.color ← color;
data.deviceColorValid ← 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;
$strokeStyle => data.np.strokeStyle ← value;
$correctPass => data.np.correctPass ← value;
ENDCASE => ERROR Imager.Error[$Bug];
};
GetT:
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];
$strokeStyle => RETURN[data.np.strokeStyle];
$correctPass => RETURN[data.np.correctPass];
ENDCASE => ERROR Imager.Error[$Bug];
};
GetCP:
PROC[context: Context, round:
BOOL]
RETURNS[
VEC] ~ {
data: Data ~ NARROW[context.data];
cp: VEC ← data.p.cp;
IF round THEN cp ← ImagerTransformation.DRound[cp];
RETURN[data.clientToView.InverseTransform[cp]];
};
ConcatT:
PROC[context: Context, m: Transformation] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreMultiply[m];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
};
Scale2T:
PROC[context: Context, s:
VEC] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreScale2[s];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
};
RotateT:
PROC[context: Context, a:
REAL] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreRotate[a];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
};
TranslateT:
PROC[context: Context, t:
VEC] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.PreTranslate[t];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
};
Move:
PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.SetTrans[data.p.cp];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
};
Trans:
PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
data.clientToView.SetTrans[ImagerTransformation.DRound[data.p.cp]];
ChangedClientToView[data];
data.clientToDeviceValid ← FALSE;
};
SetGray:
PROC[context: Context, f:
REAL] ~ {
data: Data ~ NARROW[context.data];
data.color ← ImagerColor.MakeGray[f];
data.deviceColorValid ← FALSE;
};
SetSampledColor:
PROC[context: Context, pa: PixelArray,
m: Transformation, colorOperator: ColorOperator] ~ {
data: Data ~ NARROW[context.data];
um: Transformation ~ m.Concat[data.clientToView]; -- color to view
data.color ← ImagerColor.MakeSampledColor[pa: pa, um: um, colorOperator: colorOperator];
data.deviceColorValid ← 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.clientToDeviceValid ← 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] ~ {
data: Data ~ NARROW[context.data];
outline: ImagerPath.Outline ~ ImagerPath.TransformToOutline[
pathProc: pathProc, pathData: pathData, m: data.clientToView];
data.clipper ← CONS[[outline: outline, exclude: exclude], data.clipper];
data.clientClipperValid ← FALSE;
};
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, action:
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, action]; -- 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, action]; -- pass 2
end ← data.p.cp; -- ending position
data.p.cp ← target;
IF target.Sub[end].Length>tolerance.Length THEN
ERROR Imager.Error[$UnableToProperlyAdjustMaskPositions];
};
RunProc: TYPE ~ PROC[sMin, fMin: INTEGER, fSize: NAT];
MaskDevicePath:
PROC[data: Data, parity:
BOOL ←
FALSE] ~ {
runs:
PROC[run:
PROC[sMin, fMin:
INTEGER, fSize:
NAT]] ~ {
clientClipMask: ManhattanPolygon ~ data.clientClipMask;
IF clientClipMask#
NIL
AND clientClipMask.rest=
NIL
THEN {
ImagerScanConverter.ConvertToRuns[devicePath: data.devicePath,
runProc: run, clipBox: clientClipMask.first, parityFill: parity];
}
ELSE {
rem: ManhattanPolygon ← clientClipMask;
clip:
PROC[sMin, fMin:
INTEGER, fSize:
NAT] ~ {
WHILE rem#NIL AND rem.first.sMin+rem.first.sSize<=sMin DO rem ← rem.rest ENDLOOP;
FOR l:
LIST
OF DeviceRectangle ← rem, l.rest
UNTIL l=
NIL
OR l.first.sMin>sMin
DO
fMinRun: INTEGER ~ MAX[l.first.fMin, fMin];
fMaxRun: INTEGER ~ MIN[l.first.fMin+l.first.fSize, fMin+fSize];
IF fMaxRun > fMinRun THEN run[sMin, fMinRun, fMaxRun-fMinRun];
ENDLOOP;
};
ImagerScanConverter.ConvertToRuns[devicePath: data.devicePath,
runProc: clip, clipBox: data.clientClipBox, parityFill: parity];
};
};
IF NOT data.clientClipperValid THEN ERROR Imager.Error[$Bug]; -- should have been valid
ValidateDeviceState[data]; -- notify device if color or priority changed
data.device.MaskRuns[runs];
};
MaskFill:
PROC[context: Context, pathProc: PathProc, pathData:
REF, parity:
BOOL] ~ {
data: Data ~
NARROW[context.data];
USING [T, color, clipper, priorityImportant, noImage]
IF data.np.noImage=0
THEN {
ValidateClientToDevice[data]; -- validate data.clientToDevice
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: pathProc, pathData: pathData,
transformation: data.clientToDevice,
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, priorityImportant, noImage, strokeWidth, strokeStyle]
IF data.np.noImage=0
THEN {
style: ImagerStroke.StrokeStyle ~
SELECT data.np.strokeStyle
FROM
0 => square, 1 => butt, 2 => round, ENDCASE => roundWithRoundJoints;
ValidateClientToDevice[data]; -- validate data.clientToDevice
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.clientToDevice,
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, priorityImportant, 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]];
};
ValidateClientToDevice[data]; -- validate data.clientToDevice
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: rectangle, pathData: NIL,
transformation: data.clientToDevice,
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, priorityImportant, noImage]
IF data.np.noImage=0
THEN {
ValidateClientToDevice[data]; -- validate data.clientToDevice
IF data.clientToDeviceEasy 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
ValidateDeviceState[data]; -- notify device if color changed
data.applyMask[data.device, 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, priorityImportant, noImage]
IF data.np.noImage=0
THEN {
paToDevice: Transformation ← NIL;
pixelMask: ImagerMask.Mask ← NIL;
ValidateClientToDevice[data]; -- validate data.clientToDevice
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ERROR;
paToDevice ← pa.m.Concat[data.clientToDevice];
pixelMask ← ImagerMask.FromPixelArray[pa, paToDevice];
ValidateDeviceState[data]; -- notify device if color changed
data.device.ApplyMask[data.device, 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, priorityImportant, noImage]
IF data.np.noImage=0
THEN {
pixelMask: ImagerMask.Mask ← NIL;
ValidateClientToDevice[data]; -- validate data.clientToDevice
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ERROR;
pixelMap ← xxx;
pixelMask ← ImagerMask.FromBitmap[pixelMap];
ValidateDeviceState[data]; -- notify device if color changed
data.device.ApplyMask[data.device, pixelMask, data.clientClipMask];
};
};
Show:
PROC[context: Context, chars:
PROC[
PROC[Char]], xrel:
BOOL] ~ {
data: Data ~ NARROW[context.data];
font: Font ~ data.font;
DoChar:
PROC[code: ImagerFont.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];
};
chars[DoChar];
};
StringPosition: TYPE ~ RECORD[mode: StringMode, offset: BYTE, index: INT];
StringMode: TYPE ~ {run, esc, esc2, ext, ext2};
ShowChars: PROC[context: Context, chars: PROC[PROC[Char]]] ~ {
data: Data ~ NARROW[context.data];
USING [cp, T, font, color, clipper, noImage, amplifySpace]
char: PROC[code: Char] ~ {
info
};
chars[char];
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.device.ApplyMask[data.device, info.mask, data.clientClipMask];
};
width ← info.width; -- device 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;
};
ValidateCharToDevice[data];
ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox
ValidateDeviceState[data]; -- notify device 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];
};
InternalViewReset:
PROC[data: Data] ~ {
data.viewToDevice^ ← data.device.surfaceToDevice^;
data.viewToDeviceValid ← FALSE;
data.clientToDeviceValid ← FALSE;
data.charToDeviceValid ← FALSE;
data.viewClipBox ← data.device.clipBox;
ImagerManhattan.Destroy[data.viewClipMask];
data.viewClipMask ← ImagerManhattan.CreateFromBox[data.viewClipBox];
data.viewClipperValid ← FALSE;
data.clientClipperValid ← FALSE;
};
ViewReset:
PUBLIC
PROC[context: Context] ~ {
WITH context.data
SELECT
FROM
data: Data => InternalViewReset[data];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ViewTranslate:
PUBLIC
PROC[context: Context, x, y:
INTEGER] ~ {
WITH context.data
SELECT
FROM
data: Data => {
data.viewToDevice.PreTranslate[[x, y]];
data.viewToDeviceValid ← FALSE;
data.clientToDeviceValid ← FALSE;
Note: a translation doesn't invalidate charToDevice
data.clientClipperValid ← FALSE;
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
InternalViewClip:
PROC[data: Data, x, y, w, h:
INTEGER, exclude:
BOOL] ~ {
old, this, new: ManhattanPolygon;
ValidateViewToDevice[data];
ValidateViewClipper[data];
old ← data.viewClipMask;
IF data.viewToDeviceEasy
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]];
};
data.devicePath ← ImagerScanConverter.CreatePath[
pathProc: rectangle, pathData: NIL,
transformation: data.viewToDevice,
clipBox: data.viewClipBox, scratch: data.devicePath];
this ← ImagerScanConverter.ConvertToManhattanPolygon[
devicePath: data.devicePath, clipBox: data.viewClipBox];
};
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;
};
ViewClipRectangle:
PUBLIC
PROC[context: Context, x, y, w, h:
INTEGER] ~ {
WITH context.data
SELECT
FROM
data: Data => InternalViewClip[data, x, y, w, h, FALSE];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ViewExcludeRectangle:
PUBLIC
PROC[context: Context, x, y, w, h:
INTEGER] ~ {
WITH context.data
SELECT
FROM
data: Data => InternalViewClip[data, x, y, w, h, TRUE];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ClientFromView:
PUBLIC
PROC[context: Context, p:
VEC]
RETURNS[
VEC] ~ {
WITH context.data
SELECT
FROM
data: Data => RETURN[data.clientToView.InverseTransform[p]];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ViewFromClient:
PUBLIC
PROC[context: Context, p:
VEC]
RETURNS[
VEC] ~ {
WITH context.data
SELECT
FROM
data: Data => RETURN[data.clientToView.Transform[p]];
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
ClientFromDevice:
PUBLIC
PROC[context: Context, p:
VEC]
RETURNS[
VEC] ~ {
WITH context.data
SELECT
FROM
data: Data => {
ValidateClientToDevice[data];
RETURN[data.clientToDevice.InverseTransform[p]];
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
DeviceFromClient:
PUBLIC
PROC[context: Context, p:
VEC]
RETURNS[
VEC] ~ {
WITH context.data
SELECT
FROM
data: Data => {
ValidateClientToDevice[data];
RETURN[data.clientToDevice.Transform[p]];
};
ENDCASE => ERROR Imager.Error[$Unimplemented];
};
rasterClass: Imager.Class ~
NEW[Imager.ClassRep ← [
type: $Raster,
DoSave: DoSave,
DoSaveAll: DoSaveAll,
SetT: SetT,
SetFont: SetFont,
SetColor: SetColor,
SetClipper: SetClipper,
SetReal: SetReal,
SetInt: SetInt,
GetT: GetT,
GetFont: GetFont,
GetColor: GetColor,
GetClipper: GetClipper,
GetReal: GetReal,
GetInt: GetInt,
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,
ClipOutline: ClipOutline,
ClipRectangle: ImagerUtils.ClipRectangleViaClipOutline,
ClipRectangleI: ImagerUtils.ClipRectangleIViaClipOutline,
SetXY: SetXY,
SetXYI: SetXYI,
SetXYRel: SetXYRel,
SetXYRelI: SetXYRelI,
GetCP: GetCP,
Show: Show,
CorrectMask: CorrectMask,
CorrectSpace: CorrectSpace,
Space: Space,
SpaceI: ImagerUtils.SpaceIViaSpace,
SetCorrectMeasure: SetCorrectMeasure,
SetCorrectTolerance: SetCorrectTolerance,
Correct: Correct,
props: NIL
]];
END.