ImagerLFImpl.mesa
Michael Plass, June 19, 1984 1:19:53 pm PDT
Doug Wyatt, August 1, 1984 5:52:56 pm PDT
DIRECTORY
Basics USING [bitsPerWord],
Font USING [FONT],
Imager USING [black, Class, ClassRep, Context, Error, GetProp, State, white],
ImagerBasic USING [ClientClipperItem, Color, ColorRep, ConstantColor, DeviceRectangle, IntPair, IntRectangle, Pair, PathMapType, PixelArray, PixelArrayRep, PixelBuffer, SampledColor, Transformation, Visibility],
ImagerBrick USING [Brick],
ImagerConic USING [ToCurves],
ImagerDefault USING [ClipOutline, ClipRectangle, ClipRectangleI, ConcatT, Correct, CorrectMask, CorrectSpace, CorrectSpaceView, DoSave, DoSaveAll, ExcludeOutline, ExcludeRectangle, ExcludeRectangleI, MaskUnderline, MaskUnderlineI, MaskVector, MaskVectorI, Move, RotateT, Scale2T, ScaleT, SetAmplifySpace, SetColor, SetCorrectMeasure, SetCorrectShrink, SetCorrectTolerance, SetFont, SetGray, SetNoImage, SetPriorityImportant, SetSampledBlack, SetSampledColor, SetStrokeEnd, SetStrokeWidth, SetXY, SetXYI, SetXYRel, SetXYRelI, Space, SpaceI, StartUnderline, Trans, TranslateT],
ImagerFontCache USING [Create, FontCache, FontCode, FontObject, GetFontCode, GetRopeData, GetTextData],
ImagerFrameBuffer USING [],
ImagerHalftone USING [DeviceBrick, Halftone, MakeDeviceBrick, MakeSquareBrick],
ImagerLF USING [DataRep, State],
ImagerManhattan USING [BoundingBox, Copy, CountBoxes, CreateFromBox, CreateFromRuns, Destroy, Difference, Intersection, IsVisible, Polygon],
ImagerMasks USING [ApplyConstant, ApplyTile, FromBitmap, FromPixelArray, GenerateRuns],
ImagerOps USING [XOR],
ImagerPixelMaps USING [BoundedWindow, Clear, Clip, Create, Fill, PixelMap, ShiftMap, Tile, TileFromStipple, Transfer],
ImagerScanConverter USING [ConvertToManhattanPolygon, CreatePath, DevicePath, PathProc],
ImagerStroke USING [DevicePathFromStroke],
ImagerTransform USING [Concat, Contents, Create, FromRec, Invert, Rotate, TransformationRec, TransformIntRectangle, TransformVec],
Real USING [RoundC, RoundI, RoundLI],
RefText USING [ObtainScratch, ReleaseScratch],
Rope USING [ROPE, Size],
Scaled USING [Float, Floor, FromInt, FromReal, MINUS, PLUS, Round, Value];
ImagerLFImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerConic, ImagerDefault, ImagerFontCache, ImagerHalftone, ImagerManhattan, ImagerMasks, ImagerOps, ImagerPixelMaps, ImagerScanConverter, ImagerStroke, ImagerTransform, Real, RefText, Rope, Scaled
~ BEGIN OPEN ImagerBasic;
ROPE: TYPE ~ Rope.ROPE;
StatsRecord: TYPE ~ RECORD [
loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
stats: StatsRecord ← [];
Stats: PROC RETURNS [StatsRecord] ~ { x: StatsRecord ~ stats; stats ← []; RETURN[x] };
FONT: TYPE ~ Font.FONT;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
Tile: TYPE ~ ImagerPixelMaps.Tile;
SpecialColor: TYPE ~ REF ColorRep[special];
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
TransformationRec: TYPE ~ ImagerTransform.TransformationRec;
Context: TYPE ~ Imager.Context;
State: TYPE ~ ImagerLF.State;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
cpx, cpy: REAL, -- current position (persistent)
correctMX, correctMY: REAL, -- correct measure (persistent)
T: Transformation, -- current transformation (client to view)
priorityImportant: INT, -- if nonzero, priority is important
mediumXSize, mediumYSize: REAL, -- medium size
fieldXMin, fieldYMin, fieldXMax, fieldYMax: REAL, -- field location
font: FONT, -- current font ("showVec")
color: Color, -- current color
noImage: INT, -- if nonzero, don't image masks
strokeWidth: REAL, -- current stroke width
strokeEnd: INT, -- current stroke end treatment
underlineStart: REAL, -- starting x position for underline
amplifySpace: REAL,
clipper: ClientClipper,
correctPass: INT,
correctShrink: REAL,
correctTX, correctTY: REAL, -- correct tolerance
correctMaskCount: INT, -- CORRECT variables (all persistent)
correctMaskX, correctMaskY: REAL,
correctSumX, correctSumY: REAL,
correctSpaceX, correctSpaceY: REAL,
correctcpx, correctcpy: REAL,
correctTargetX, correctTargetY: REAL,
Clipper info:
viewClipper: ImagerManhattan.Polygon, -- in device coords
clientClipper: ImagerBasic.ClientClipper, -- for noticing client clipper changes
compositeClipper: ImagerManhattan.Polygon, -- in device coords; invalid if NIL or clientClipper#clipper
View-to-surface info
viewOrigin: ImagerBasic.IntPair,
Scratch storage
devicePath: ImagerScanConverter.DevicePath,
tileSamples: ImagerBasic.SampledColor,
deviceBrick: ImagerHalftone.DeviceBrick ← NIL,
deviceBrickMaxSampleValue: INT ← -1,
Cached color info
cachedColor: ImagerBasic.Color ← NIL,
tile: ImagerPixelMaps.Tile,
The bitmap
canvas: ImagerPixelMaps.PixelMap
];
DoSave: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
T: Transformation ~ data.T;
priorityImportant: INT ~ data.priorityImportant;
mediumXSize: REAL ~ data.mediumXSize;
mediumYSize: REAL ~ data.mediumYSize;
fieldXMin: REAL ~ data.fieldXMin;
fieldYMin: REAL ~ data.fieldYMin;
fieldXMax: REAL ~ data.fieldXMax;
fieldYMax: REAL ~ data.fieldYMax;
showVec: REF ~ data.showVec;
color: Color ~ data.color;
noImage: INT ~ data.noImage;
strokeWidth: REAL ~ data.strokeWidth;
strokeEnd: INT ~ data.strokeEnd;
underlineStart: REAL ~ data.underlineStart;
amplifySpace: REAL ~ data.amplifySpace;
clipper: ClientClipper ~ data.clipper;
correctPass: INT ~ data.correctPass;
correctShrink: REAL ~ data.correctShrink;
correctTX: REAL ~ data.correctTX;
correctTY: REAL ~ data.correctTY;
Restore: PROC ~ {
data.TT;
data.priorityImportant ← priorityImportant;
data.mediumXSize ← mediumXSize;
data.mediumYSize ← mediumYSize;
data.fieldXMin ← fieldXMin;
data.fieldYMin ← fieldYMin;
data.fieldXMax ← fieldXMax;
data.fieldYMax ← fieldYMax;
data.showVec ← showVec;
data.color ← color;
data.noImage ← noImage;
data.strokeWidth ← strokeWidth;
data.strokeEnd ← strokeEnd;
data.underlineStart ← underlineStart;
data.amplifySpace ← amplifySpace;
data.clipper ← clipper;
data.correctPass ← correctPass;
data.correctShrink ← correctShrink;
data.correctTX ← correctTX;
data.correctTY ← correctTY;
};
body[! UNWIND => Restore[]];
Restore[];
};
DoSaveAll: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
cpx: REAL ~ data.cpx;
cpy: REAL ~ data.cpy;
correctMX: REAL ~ data.correctMX;
correctMY: REAL ~ data.correctMY;
correctMaskCount: INT ~ data.correctMaskCount;
correctMaskX: REAL ~ data.correctMaskX;
correctMaskY: REAL ~ data.correctMaskY;
correctSumX: REAL ~ data.correctSumX;
correctSumY: REAL ~ data.correctSumY;
correctSpaceX: REAL ~ data.correctSpaceX;
correctSpaceY: REAL ~ data.correctSpaceY;
correctcpx: REAL ~ data.correctcpx;
correctcpy: REAL ~ data.correctcpy;
correctTargetX: REAL ~ data.correctTargetX;
correctTargetY: REAL ~ data.correctTargetY;
Restore: PROC ~ {
data.cpx ← cpx;
data.cpy ← cpy;
data.correctMX ← correctMX;
data.correctMY ← correctMY;
data.correctMaskCount ← correctMaskCount;
data.correctMaskX ← correctMaskX;
data.correctMaskY ← correctMaskY;
data.correctSumX ← correctSumX;
data.correctSumY ← correctSumY;
data.correctSpaceX ← correctSpaceX;
data.correctSpaceY ← correctSpaceY;
data.correctcpx ← correctcpx;
data.correctcpy ← correctcpy;
data.correctTargetX ← correctTargetX;
data.correctTargetY ← correctTargetY;
};
DoSave[context, body ! UNWIND => Restore[]];
Restore[];
};
SetRef: PROC[context: Context, key: Variable, value: REF] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => data.cpx ← value;
$DCScpy => data.cpy ← value;
$correctMX => data.correctMX ← value;
$correctMY => data.correctMY ← value;
$T => data.TNARROW[value];
$priorityImportant => data.priorityImportant ← value;
$mediumXSize => data.mediumXSize ← value;
$mediumYSize => data.mediumYSize ← value;
$fieldXMin => data.fieldXMin ← value;
$fieldYMin => data.fieldYMin ← value;
$fieldXMax => data.fieldXMax ← value;
$fieldYMax => data.fieldYMax ← value;
$showVec => data.font ← NARROW[value];
$color => data.color ← NARROW[value];
$noImage => data.noImage ← value;
$strokeWidth => data.strokeWidth ← value;
$strokeEnd => data.strokeEnd ← value;
$underlineStart => data.underlineStart ← value;
$amplifySpace => data.amplifySpace ← value;
$correctPass => data.correctPass ← value;
$correctShrink => data.correctShrink ← value;
$correctTX => data.correctTX ← value;
$correctTY => data.correctTY ← value;
$clipper => data.clipper ← NARROW[value];
ENDCASE => ERROR Imager.Error[$WrongType];
};
SetReal: PROC[context: Context, key: Variable, value: REAL] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => data.cpx ← value;
$DCScpy => data.cpy ← value;
$correctMX => data.correctMX ← value;
$correctMY => data.correctMY ← value;
$mediumXSize => data.mediumXSize ← value;
$mediumYSize => data.mediumYSize ← value;
$fieldXMin => data.fieldXMin ← value;
$fieldYMin => data.fieldYMin ← value;
$fieldXMax => data.fieldXMax ← value;
$fieldYMax => data.fieldYMax ← value;
$strokeWidth => data.strokeWidth ← value;
$underlineStart => data.underlineStart ← value;
$amplifySpace => data.amplifySpace ← value;
$correctShrink => data.correctShrink ← value;
$correctTX => data.correctTX ← value;
$correctTY => data.correctTY ← value;
ENDCASE => ERROR Imager.Error[$WrongType];
};
SetInt: PROC[context: Context, key: Variable, value: INT] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$priorityImportant => data.priorityImportant ← value;
$noImage => data.noImage ← value;
$strokeEnd => data.strokeEnd ← value;
$correctPass => data.correctPass ← value;
ENDCASE => ERROR Imager.Error[$WrongType];
};
GetRef: PROC[context: Context, key: Variable] RETURNS[REF] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => RETURN[data.cpx];
$DCScpy => RETURN[data.cpy];
$correctMX => RETURN[data.correctMX];
$correctMY => RETURN[data.correctMY];
$T => RETURN[data.T];
$priorityImportant => RETURN[data.priorityImportant];
$mediumXSize => RETURN[data.mediumXSize];
$mediumYSize => RETURN[data.mediumYSize];
$fieldXMin => RETURN[data.fieldXMin];
$fieldYMin => RETURN[data.fieldYMin];
$fieldXMax => RETURN[data.fieldXMax];
$fieldYMax => RETURN[data.fieldYMax];
$showVec => RETURN[data.font];
$color => RETURN[data.color];
$noImage => RETURN[data.noImage];
$strokeWidth => RETURN[data.strokeWidth];
$strokeEnd => RETURN[data.strokeEnd];
$underlineStart => RETURN[data.underlineStart];
$amplifySpace => RETURN[data.amplifySpace];
$correctPass => RETURN[data.correctPass];
$correctShrink => RETURN[data.correctShrink];
$correctTX => RETURN[data.correctTX];
$correctTY => RETURN[data.correctTY];
$clipper => RETURN[data.clipper];
ENDCASE => ERROR Imager.Error[$WrongType];
};
GetReal: PROC[context: Context, key: Variable] RETURNS[REAL] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$DCScpx => RETURN[data.cpx];
$DCScpy => RETURN[data.cpy];
$correctMX => RETURN[data.correctMX];
$correctMY => RETURN[data.correctMY];
$mediumXSize => RETURN[data.mediumXSize];
$mediumYSize => RETURN[data.mediumYSize];
$fieldXMin => RETURN[data.fieldXMin];
$fieldYMin => RETURN[data.fieldYMin];
$fieldXMax => RETURN[data.fieldXMax];
$fieldYMax => RETURN[data.fieldYMax];
$strokeWidth => RETURN[data.strokeWidth];
$underlineStart => RETURN[data.underlineStart];
$amplifySpace => RETURN[data.amplifySpace];
$correctShrink => RETURN[data.correctShrink];
$correctTX => RETURN[data.correctTX];
$correctTY => RETURN[data.correctTY];
ENDCASE => ERROR Imager.Error[$WrongType];
};
GetInt: PROC[context: Context, key: Variable] RETURNS[INT] ~ {
data: Data ~ NARROW[context.data];
SELECT key FROM
$priorityImportant => RETURN[data.priorityImportant];
$noImage => RETURN[data.noImage];
$strokeEnd => RETURN[data.strokeEnd];
$correctPass => RETURN[data.correctPass];
ENDCASE => ERROR Imager.Error[$WrongType];
};
SetGray: PROC[context: Context, f: REAL] ~ {
};
SetSampledColor: PROC[context: Context, pa: PixelArray, pixelT: Transformation, colorOperator: ATOM] ~ {
};
SetSampledBlack: PROC[context: Context, pa: PixelArray, pixelT: Transformation, transparent: BOOL] ~ {
};
ConcatT: PROC[context: Context, m: Transformation] ~ {
data: Data ~ NARROW[context.data];
data.T ← ImagerTransform.Concat[m, data.T];
};
ScaleT: PROC[context: Context, s: REAL] ~ {
data: Data ~ NARROW[context.data];
data.T ← ImagerTransform.PreScale[s, data.T];
};
Scale2T: PROC[context: Context, sx, sy: REAL] ~ {
data: Data ~ NARROW[context.data];
data.T ← ImagerTransform.PreScale2[sx, sy, data.T];
};
RotateT: PROC[context: Context, a: REAL] ~ {
data: Data ~ NARROW[context.data];
data.T ← ImagerTransform.PreRotate[a, data.T];
};
TranslateT: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
data.T ← ImagerTransform.PreTranslate[x, y, data.T];
};
Move: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
new: Pair ~ [data.cpx, data.cpy];
t: Transformation ~ data.T;
data.T ← ImagerTransform.Create[t.a, t.b, new.x, t.d, t.e, new.y];
};
Trans: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
new: Pair ~ ImagerOps.DRound[[data.cpx, data.cpy]];
t: Transformation ~ data.T;
data.T ← ImagerTransform.Create[t.a, t.b, new.x, t.d, t.e, new.y];
};
SetFont: PROC[context: Context, font: FONT] ~ {
data: Data ~ NARROW[context.data];
};
ShowRope: PROC[context: Context, rope: ROPE, start, len: INT] ~ {
data: Data ~ NARROW[context.data];
};
ShowText: PROC[context: Context, text: REF READONLY TEXT, start, len: NAT] ~ {
data: Data ~ NARROW[context.data];
};
ShowCharCode: PROC[context: Context, charCode: CARDINAL] ~ {
data: Data ~ NARROW[context.data];
};
SetXY: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
[[data.cpx, data.cpy]] ← ImagerTransform.Transform[[x, y], data.T];
};
SetXYI: PROC[context: Context, x, y: INTEGER] ~ {
data: Data ~ NARROW[context.data];
[[data.cpx, data.cpy]] ← ImagerTransform.Transform[[x, y], data.T];
};
SetXYRel: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
delta: Pair ~ ImagerTransform.TransformVec[[x, y], data.T];
data.cpx ← data.cpx + delta.x;
data.cpy ← data.cpy + delta.y;
};
SetXYRelI: PROC[context: Context, x, y: INTEGER] ~ {
data: Data ~ NARROW[context.data];
delta: Pair ~ ImagerTransform.TransformVec[[x, y], data.T];
data.cpx ← data.cpx + delta.x;
data.cpy ← data.cpy + delta.y;
};
MaskFill: PROC[context: Context, pathProc: PathProc, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
};
MaskStroke: PROC[context: Context, pathProc: PathProc, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
};
MaskStrokeClosed: PROC[context: Context, pathProc: PathProc, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
};
MaskVector: PROC[context: Context, x1, y1, x2, y2: REAL] ~ {
vector: PathProc ~ { move[[x1, y1]]; line[[x2, y2]] };
MaskStroke[context, vector, NIL];
};
MaskVectorI: PROC[context: Context, x1, y1, x2, y2: INTEGER] ~ {
vector: PathProc ~ { move[[x1, y1]]; line[[x2, y2]] };
MaskStroke[context, vector, NIL];
};
MaskRectangle: PROC[context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
};
MaskRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ~ {
data: Data ~ NARROW[context.data];
};
StartUnderline: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
cp: Pair ~ ImagerTransform.InverseTransform[[data.cpx, data.cpy], data.T];
data.underlineStart ← cp.x;
};
MaskUnderline: PROC[context: Context, dy, h: REAL] ~ {
data: Data ~ NARROW[context.data];
cpx: REAL ~ data.cpx;
cpy: REAL ~ data.cpy;
T: Transformation ~ data.T;
cp: Pair ~ ImagerTransform.InverseTransform[[cpx, cpy], T];
SetXY[context, data.underlineStart, cp.y-dy-h];
Trans[context];
MaskRectangle[context, 0, 0, cp.x-data.underlineStart, h];
data.cpx ← cpx;
data.cpy ← cpy;
data.TT;
};
MaskUnderlineI: PROC[context: Context, dy, h: INTEGER] ~ {
MaskUnderline[context, dy, h];
};
MaskPixel: PROC[context: Context, pa: PixelArray] ~ {
data: Data ~ NARROW[context.data];
};
ClipOutline: PROC[context: Context, pathProc: PathProc, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
};
ExcludeOutline: PROC[context: Context, pathProc: PathProc, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
};
ClipRectangle: PROC[context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
};
ClipRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ~ {
data: Data ~ NARROW[context.data];
};
ExcludeRectangle: PROC[context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
};
ExcludeRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ~ {
data: Data ~ NARROW[context.data];
};
CorrectMask: PROC[context: Context] ~ {
data: Data ~ NARROW[context.data];
SELECT data.correctPass FROM
1 => data.correctMaskCount ← data.correctMaskCount + 1;
2 => IF data.correctMaskCount > 0 THEN {
spx: REAL ~ data.correctMaskX/data.correctMaskCount;
spy: REAL ~ data.correctMaskY/data.correctMaskCount;
data.correctMaskX ← data.correctMaskX - spx;
data.correctMaskY ← data.correctMaskY - spy;
data.correctMaskCount ← data.correctMaskCount - 1;
data.cpx ← data.cpx + spx;
data.cpy ← data.cpy + spy;
};
ENDCASE => NULL;
};
CorrectSpace: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
};
Correct: PROC[context: Context, body: PROC] ~ {
data: Data ~ NARROW[context.data];
};
SetCorrectMeasure: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
[[data.correctMX, data.correctMY]] ← ImagerTransform.TransformVec[[x, y], data.T];
};
SetCorrectTolerance: PROC[context: Context, x, y: REAL] ~ {
data: Data ~ NARROW[context.data];
[[data.correctTX, data.correctTY]] ← ImagerTransform.TransformVec[[x, y], data.T];
};
Space: PROC[context: Context, x: REAL] ~ {
Equivalent to {SetXYRel[context, [x, 0]]; CorrectSpace[context, [x, 0]]}
data: Data ~ NARROW[context.data];
v: Pair ~ ImagerTransform.TransformVec[[x, 0], data.T];
data.cpx ← data.cpx + v.x;
data.cpy ← data.cpy + v.y;
SELECT data.correctPass FROM
1 => {
data.correctSumX ← data.correctSumX + v.x;
data.correctSumY ← data.correctSumY + v.y;
};
2 => IF data.correctMaskCount > 0 THEN {
Div: PROC [num, denom: REAL] RETURNS [REAL] ~ {
IF denom = 0.0 THEN {
IF num = 0.0 THEN RETURN [0.0]
ELSE ERROR Imager.Error[$ZeroDivideInCorrectSpace]
}
ELSE RETURN [num/denom]
};
spx: REAL ~ Div[v.x*data.correctSpaceX, data.correctSumX];
spy: REAL ~ Div[v.y*data.correctSpaceY, data.correctSumY];
data.correctSumX ← data.correctSumX - v.x;
data.correctSumY ← data.correctSumY - v.y;
data.correctSpaceX ← data.correctSpaceX - spx;
data.correctSpaceY ← data.correctSpaceY - spy;
data.cpx ← data.cpx + spx;
data.cpy ← data.cpy + spy;
};
ENDCASE => NULL;
};
SpaceI: PROC[context: Context, x: INTEGER] ~ {
Space[context, x];
};
SurfaceOriginS: PROC[data: Data] RETURNS[INTEGER] ~ INLINE { RETURN[data.canvas.sSize] };
CompositeT: PROC [data: Data] RETURNS [TransformationRec] ~ {
concatenates T with the current view-to-device transformation
t: TransformationRec ~ data.T.Contents;
RETURN [[
a: -t.d, d: t.a,
b: -t.e, e: t.b,
c: -t.f+SurfaceOriginS[data]-data.viewOrigin.y,
f: t.c+data.viewOrigin.x
]]
};
ViewToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: SurfaceOriginS[data]-data.viewOrigin.y,
f: data.viewOrigin.x
]]
};
SurfaceToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: SurfaceOriginS[data],
f: 0
]]
};
DevicePair: TYPE ~ RECORD [s, f: Scaled.Value];
TransformDeviceToViewVec: PROC [d: DevicePair] RETURNS [v: Pair] ~ {
v ← [x: Scaled.Float[d.f], y: -Scaled.Float[d.s]];
};
GetDeviceCP: PROC [data: Data] RETURNS [DevicePair] ~ {
RETURN [[
s: ScaledFromReal[-data.cpy].PLUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]],
f: ScaledFromReal[data.cpx].PLUS[Scaled.FromInt[data.viewOrigin.x]]
]]
};
SetDeviceCP: PROC [data: Data, cp: DevicePair] ~ {
data.cpy ← -Scaled.Float[cp.s.MINUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]]];
data.cpx ← Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.viewOrigin.x]]];
};
Init: PROC [context: Context, info: REF] ~ {
tileStorage: PixelMap ~ ImagerPixelMaps.Create[lgBitsPerPixel: 0, bounds: [0, 0, 16, 16]];
data: Data ~ NEW[DataRep];
context.data ← data;
data.tile ← [sOrigin: 0, fOrigin: 0, sSize: 16, fSize: 16, phase: 0, refRep: tileStorage.refRep];
data.tileSamples ← MakeTileSamples[16, 16];
data.devicePath ← NIL;
WITH info SELECT FROM
pm: REF PixelMap => data.canvas ← pm^;
ENDCASE => TRUSTED {data.canvas ← ImagerFrameBuffer.LFDisplay[]};
ImagerDefault.InitState[context];
SetViewOrigin[context, [0, 0]];
SetViewBox[context, GetSurfaceBounds[context]];
Reset[context];
};
pixelsPerInch: REAL ~ 72.0;
mmPerInch: REAL ~ 25.4;
inchesPerMeter: REAL ~ 1000.0/mmPerInch;
pixelsPerMeter: REAL ← pixelsPerInch*inchesPerMeter;
Reset: PROC [context: Context] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
ImagerDefault.Reset[context];
ImagerDefault.ScaleT[context, pixelsPerMeter];
state.fieldXMax ← state.mediumXSize ← data.canvas.fSize/pixelsPerMeter;
state.fieldYMax ← state.mediumYSize ← data.canvas.sSize/pixelsPerMeter;
data.compositeClipper ← NIL;
data.cachedColor ← NIL;
};
MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: SampledColor] ~ {
tileSamples ← NEW[ColorRep[sampled]];
tileSamples.transparent ← FALSE;
tileSamples.pa ← NEW[PixelArrayRep];
tileSamples.m ← tileSamples.pa.m ← ImagerTransform.Rotate[0];
tileSamples.colorOperator ← $Intensity;
tileSamples.pa.xPixels ← xSize;
tileSamples.pa.yPixels ← ySize;
tileSamples.pa.maxSampleValue ← 255;
tileSamples.pa.samplesPerPixel ← 1;
tileSamples.pa.get ← ConstantGet;
tileSamples.pa.data ← NEW[NAT ← 0];
};
ConstantGet: PROC [self: PixelArray, buffer: PixelBuffer, nSamples: NAT, layer: INT, xStart, yStart: Scaled.Value, xDelta, yDelta: Scaled.Value] ~ {
WITH self.data SELECT FROM
n: REF NAT => {
value: NAT ← n^;
FOR i: NAT IN [0..nSamples) DO buffer[i] ← value ENDLOOP;
};
ENDCASE => ERROR;
};
SetTileSamples: PROC [tileSamples: SampledColor, intensity: [0..255]] ~ {
WITH tileSamples.pa.data SELECT FROM
n: REF NAT => n^ ← intensity;
ENDCASE => ERROR;
};
DevicePathFromViewPath: PROC[data: Data, pathMap: PathMapType, pathData: REF] RETURNS [ImagerScanConverter.DevicePath] ~ {
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
~ {
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f)
-p.y + SurfaceOriginS[data]-data.viewOrigin.y,
p.x + data.viewOrigin.x
]]};
Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp ← q };
Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp ← q };
Xcurve: PROC [p1, p2, p3: Pair] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3];
curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp ← q3 };
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← q2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
RETURN [ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.canvas.BoundedWindow]];
};
DevicePathFromPath: PROC[data: Data, pathMap: PathMapType, pathData: REF] ~ {
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
~ {
m: TransformationRec ~ CompositeT[data];
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f)
m.a * p.x + m.b * p.y + m.c,
m.d * p.x + m.e * p.y + m.f
]]};
Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp ← q };
Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp ← q };
Xcurve: PROC [p1, p2, p3: Pair] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3];
curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp ← q3 };
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← q2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
data.devicePath ← ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.canvas.BoundedWindow, scratch: data.devicePath];
};
DevicePathFromStroke: PROC [data: Data,
pathMap: PathMapType, pathData: REF, closed: BOOL] ~ {
data.devicePath ← ImagerStroke.DevicePathFromStroke[
pathMap: pathMap,
pathData: pathData,
clientToDevice: CompositeT[data].FromRec,
width: data.strokeWidth,
strokeEnd: SELECT data.strokeEnd FROM 0 => square, 1 => butt, ENDCASE => round,
closed: closed,
clipBox: data.canvas.BoundedWindow,
scratch: data.devicePath
];
};
Floor: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int < real DO int ← int + 1 ENDLOOP;
WHILE int > real DO int ← int - 1 ENDLOOP;
};
Ceiling: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int > real DO int ← int - 1 ENDLOOP;
WHILE int < real DO int ← int + 1 ENDLOOP;
};
ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED {
Because of an apparent bug in Real.FScale.
i: INT ← Real.RoundLI[real * 65536.0];
RETURN[LOOPHOLE[i]]
};
CompositeClipper: PROC [data: Data] RETURNS [ManhattanPolygon] ~ {
mp: ManhattanPolygon ← data.compositeClipper;
IF mp = NIL OR data.clientClipper # data.clipper THEN {
ConcatClippers: PROC [l: LIST OF ClientClipperItem] ~ {
IF l#NIL THEN {
t1, t2: ManhattanPolygon;
ConcatClippers[l.rest];
t1 ← ImagerScanConverter.ConvertToManhattanPolygon[DevicePathFromViewPath[data: data, pathMap: l.first.pathMap, pathData: l.first.pathData], [data.canvas.sOrigin+data.canvas.sMin, data.canvas.fOrigin+data.canvas.fMin, data.canvas.sSize, data.canvas.fSize]];
t2 ← mp;
mp ← IF l.first.exclude THEN mp.Difference[t1] ELSE mp.Intersection[t1];
ImagerManhattan.Destroy[t1];
ImagerManhattan.Destroy[t2];
};
};
ImagerManhattan.Destroy[data.compositeClipper];
data.clientClipper ← data.clipper;
mp ← ImagerManhattan.Copy[data.viewClipper];
ConcatClippers[data.clientClipper];
data.compositeClipper ← mp;
};
RETURN [mp]
};
lfBrick: ImagerHalftone.DeviceBrick ← ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255];
IntensityFromColor: PROC[c: ConstantColor] RETURNS[REAL] ~ {
WITH c SELECT FROM
c: REF ColorRep[constant][gray] => RETURN[c.f];
c: REF ColorRep[constant][cie] => RETURN[c.Y];
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
CurrentColor: PROC [data: Data] RETURNS [color: Color] ~ {
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ {
FOR s: INTEGER IN [data.tile.sOrigin..data.tile.sOrigin + data.tile.sSize) DO
run[s, data.tile.fOrigin, data.tile.fSize];
ENDLOOP;
};
IF data.cachedColor = data.color THEN RETURN [data.cachedColor];
color ← data.cachedColor ← data.color;
IF color = Imager.black OR color = Imager.white THEN RETURN;
WITH color SELECT FROM
constantColor: ConstantColor => {
SetTileSamples[data.tileSamples, Real.RoundC[IntensityFromColor[constantColor]*255]];
ImagerHalftone.Halftone[
dest: [sOrigin: data.tile.sOrigin, fOrigin: data.tile.fOrigin, sMin: 0, fMin: 0, sSize: data.tile.sSize, fSize: data.tile.fSize, refRep: data.tile.refRep],
runs: Runs,
source: data.tileSamples.pa,
transformation: ImagerTransform.Rotate[0],
deviceBrick: lfBrick
];
};
specialColor: SpecialColor => {
WITH specialColor.ref SELECT FROM
stipple: REF CARDINAL =>
data.tile ← ImagerPixelMaps.TileFromStipple[stipple: stipple^, scratch: data.tile.refRep];
atom: ATOM => {
SELECT atom FROM
$XOR => NULL;
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
sampledColor: SampledColor => NULL;
ENDCASE => data.color ← Imager.black;
};
ApplyMask: PROC [context: Context, mask: REF, sTranslate, fTranslate: INTEGER ← 0] ~ {
data: Data ~ NARROW[context.data];
IF data.noImage = 0 THEN {
color: Color ~ CurrentColor[data];
clipper: ManhattanPolygon ~ CompositeClipper[data];
SELECT color FROM
Imager.black => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [null, null], sTranslate, fTranslate];
Imager.white => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 0, [null, null], sTranslate, fTranslate];
ImagerOps.XOR => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [xor, null], sTranslate, fTranslate];
ENDCASE => {
WITH color SELECT FROM
sampledColor: SampledColor => {
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ {
ImagerMasks.GenerateRuns[mask, clipper, run, sTranslate, fTranslate];
};
transform: Transformation ← sampledColor.pa.m.Concat[sampledColor.m].Concat[ViewToDevice[data].FromRec];
invertOutput: BOOLEANFALSE;
customBrick: ImagerBrick.Brick ← NARROW[Imager.GetProp[context, $CustomBrick]];
deviceBrick: ImagerHalftone.DeviceBrick;
IF customBrick # NIL THEN
deviceBrick ← ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue]
ELSE {
IF data.deviceBrickMaxSampleValue # sampledColor.pa.maxSampleValue THEN {
data.deviceBrickMaxSampleValue ← sampledColor.pa.maxSampleValue;
data.deviceBrick ← ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, sampledColor.pa.maxSampleValue];
};
deviceBrick ← data.deviceBrick;
};
SELECT sampledColor.colorOperator FROM
$SampledBlack => invertOutput ← TRUE;
$Intensity => NULL;
ENDCASE => Imager.Error[$UnknownColorModel];
ImagerHalftone.Halftone[data.canvas, Runs, sampledColor.pa, transform, deviceBrick, invertOutput, sampledColor.transparent];
};
ENDCASE => { -- constant (other than black or white) or stipple
tile: ImagerPixelMaps.Tile ← data.tile;
tile.sOrigin ← tile.sOrigin + SurfaceOriginS[data]-data.viewOrigin.y;
tile.fOrigin ← tile.fOrigin + data.viewOrigin.x;
ImagerMasks.ApplyTile[mask, clipper, data.canvas, tile, [null, null], sTranslate, fTranslate];
};
};
};
};
MaskStroke: PROC[context: Context, pathProc: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[data, pathProc, pathData, FALSE];
ApplyMask[context, data.devicePath];
};
MaskStrokeClosed: PROC[context: Context, pathProc: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[data, pathProc, pathData, TRUE];
ApplyMask[context, data.devicePath];
};
MaskFill: PROC [context: Context, pathProc: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromPath[data, pathProc, pathData];
ApplyMask[context, data.devicePath];
};
specialCaseRectangles: BOOLEANTRUE;
MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
trans: TransformationRec ~ CompositeT[data];
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: REAL ~ trans.b * y + trans.c;
s1: REAL ~ trans.b * (y+h) + trans.c;
f0: REAL ~ trans.d * x + trans.f;
f1: REAL ~ trans.d * (x+w) + trans.f;
sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]];
sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]];
fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]];
fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]];
t1: ManhattanPolygon;
IF sMax<=sMin OR fMax<=fMin THEN RETURN;
t1 ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
ApplyMask[context, t1, 0, 0];
ImagerManhattan.Destroy[t1];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
MaskFill[context, PathMap, NIL];
};
};
MaskRectangleI: PROC [context: Context, x, y, w, h: INTEGER] ~ {
MaskRectangle[context, x, y, w, h];
};
MaskPixel: PROC [context: Context, pa: PixelArray] ~ {
data: Data ~ NARROW[context.data];
trans: Transformation ~ CompositeT[data].FromRec;
mask: REF ← ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]];
ApplyMask[context, mask, 0, 0];
mask ← NIL;
};
rasterToRunGroupStorageRatio: INT ← 1;
CharLoadInfo: TYPE ~ RECORD[sWidth, fWidth: Scaled.Value, mask: REF];
GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR]
RETURNS [ImagerManhattan.Polygon] ~ {
Runs: PROC [
run: PROC [sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC [timesToRepeatScanline: NAT]] ~ {
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
run[sMin: sMin, fMin: fMin, fSize: fSize];
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
RETURN [ImagerManhattan.CreateFromRuns[Runs]]
};
FontCompositeTransform: PROC[data: Data] RETURNS[t: TransformationRec] ~ {
font: FONT ~ data.font;
r: TransformationRec ← CompositeT[data];
r.c ← r.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, r.FromRec].Contents;
};
fontCache: ImagerFontCache.FontCache ← ImagerFontCache.Create[];
LoadCharData: PROC [self: ImagerFontCache.FontObject, charCode: CARDINAL]
RETURNS [charData: REF, memoryCost: INT] ~ {
font: FONT ~ NARROW[self.fontAnyData];
char: CHAR ~ 0C+charCode; -- does this bounds check properly?
transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device
clientTransform: Transformation ~ font.actualTransformation.Invert.Concat[transform];
loadInfo: REF CharLoadInfo ← NEW[CharLoadInfo];
mask: ImagerManhattan.Polygon ← GetCharMask[font, transform, char];
nBoxes: INT ~ ImagerManhattan.CountBoxes[mask];
bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask];
boxesSize: INT ← 7 * nBoxes;
rasterSize: INT ← bb.sSize * INT[(bb.fSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char] , clientTransform];
IF bb.fSize > 32*Basics.bitsPerWord OR boxesSize*rasterToRunGroupStorageRatio < rasterSize THEN loadInfo.mask ← mask
ELSE {
pixelMap: PixelMap ← ImagerPixelMaps.Create[0, bb];
ImagerPixelMaps.Clear[pixelMap];
FOR l: LIST OF DeviceRectangle ← mask, l.rest UNTIL l = NIL DO
ImagerPixelMaps.Fill[pixelMap, l.first, 1];
ENDLOOP;
loadInfo.mask ← ImagerMasks.FromBitmap[pixelMap];
ImagerManhattan.Destroy[mask]; mask ← NIL;
};
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
RETURN[loadInfo, 0];
};
ShowChar: PROC[context: Context, char: CHAR] ~ {
text: REF TEXT ~ RefText.ObtainScratch[1];
text[0] ← char;
text.length ← 1;
ShowText[context, text];
RefText.ReleaseScratch[text];
};
ShowText: PROC[context: Context,
text: REF READONLY TEXT, start: NAT ← 0, len: NATNAT.LAST] ~ {
data: Data ~ NARROW[context.data];
transform: TransformationRec ← FontCompositeTransform[data];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[[
CharDataProc: LoadCharData,
r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f,
fontAnyData: data.font]];
residual: NAT ← text.length;
start ← MIN[residual, start]; len ← MIN[residual-start, len];
residual ← len;
WHILE residual > 0 DO
IF data.correctPass=0 AND ABS[data.cpx]<NAT.LAST AND ABS[data.cpy]<NAT.LAST THEN {
cp: DevicePair ← GetDeviceCP[data];
DoChar: ImagerFontCache.CharAction ~ {
PROC[charCode: CARDINAL, charData: REF] RETURNS[quit: BOOLFALSE]
loadInfo: REF CharLoadInfo ~ NARROW[charData];
sDelta: Scaled.Value ← loadInfo.sWidth;
fDelta: Scaled.Value ← loadInfo.fWidth;
IF ABS[cp.s.Floor]>=NAT.LAST OR ABS[cp.s.Floor]>=NAT.LAST THEN RETURN[TRUE];
IF data.noImage=0 THEN ApplyMask[context, loadInfo.mask, cp.s.Round, cp.f.Round];
IF charCode = ORD[' ] AND data.amplifySpace # 1.0 THEN {
sDelta ← Scaled.FromReal[data.amplifySpace*sDelta.Float];
fDelta ← Scaled.FromReal[data.amplifySpace*fDelta.Float];
};
cp.s ← cp.s.PLUS[sDelta];
cp.f ← cp.f.PLUS[fDelta];
residual ← residual - 1;
};
ImagerFontCache.GetTextData[DoChar, fontCache, fontCode, text, start, len];
SetDeviceCP[data, cp];
start ← start + (len-residual); len ← residual;
}
ELSE {
CorrectingDoChar: ImagerFontCache.CharAction ~ {
PROC[charCode: CARDINAL, charData: REF] RETURNS[quit: BOOLFALSE]
loadInfo: REF CharLoadInfo ~ NARROW[charData];
delta: Pair ← TransformDeviceToViewVec[[loadInfo.sWidth, loadInfo.fWidth]];
IF data.noImage=0 THEN {
cp: DevicePair ← GetDeviceCP[data];
ApplyMask[context, loadInfo.mask, cp.s.Round, cp.f.Round];
};
IF charCode = ORD[' ] THEN {
IF data.amplifySpace # 1.0 THEN {
delta.x ← delta.x * data.amplifySpace;
delta.y ← delta.y * data.amplifySpace;
};
ImagerDefault.CorrectSpaceView[context, delta];
}
ELSE ImagerDefault.CorrectMask[context];
data.cpx ← data.cpx + delta.x;
data.cpy ← data.cpy + delta.y;
residual ← residual - 1;
};
ImagerFontCache.GetTextData[CorrectingDoChar, fontCache, fontCode, text, start, len];
};
ENDLOOP;
};
ShowRope: PROC[context: Context, rope: ROPE, start: INT ← 0, len: INTINT.LAST] ~ {
data: Data ~ NARROW[context.data];
transform: TransformationRec ← FontCompositeTransform[data];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[[
CharDataProc: LoadCharData,
r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f,
fontAnyData: data.font]];
residual: INT ← Rope.Size[rope];
start ← MIN[residual, MAX[0,start]];
len ← MIN[residual-start, MAX[len, 0]];
residual ← len;
WHILE residual > 0 DO
IF data.correctPass=0 AND ABS[data.cpx]<NAT.LAST AND ABS[data.cpy]<NAT.LAST THEN {
cp: DevicePair ← GetDeviceCP[data];
DoChar: ImagerFontCache.CharAction ~ {
PROC[charCode: CARDINAL, charData: REF] RETURNS[quit: BOOLFALSE]
loadInfo: REF CharLoadInfo ~ NARROW[charData];
sDelta: Scaled.Value ← loadInfo.sWidth;
fDelta: Scaled.Value ← loadInfo.fWidth;
IF ABS[cp.s.Floor]>=NAT.LAST OR ABS[cp.s.Floor]>=NAT.LAST THEN RETURN[TRUE];
IF data.noImage=0 THEN ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
IF charCode = ORD[' ] AND data.amplifySpace # 1.0 THEN {
sDelta ← Scaled.FromReal[data.amplifySpace*Scaled.Float[sDelta]];
fDelta ← Scaled.FromReal[data.amplifySpace*Scaled.Float[fDelta]];
};
cp.s ← cp.s.PLUS[sDelta];
cp.f ← cp.f.PLUS[fDelta];
residual ← residual - 1;
};
ImagerFontCache.GetRopeData[DoChar, fontCache, fontCode, rope, start, len];
SetDeviceCP[data, cp];
start ← start + (len-residual); len ← residual;
}
ELSE {
CorrectingDoChar: ImagerFontCache.CharAction ~ {
PROC[charCode: CARDINAL, charData: REF] RETURNS[quit: BOOLFALSE]
loadInfo: REF CharLoadInfo ~ NARROW[charData];
delta: Pair ← TransformDeviceToViewVec[[loadInfo.sWidth, loadInfo.fWidth]];
IF data.noImage=0 THEN {
cp: DevicePair ← GetDeviceCP[data];
ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
};
IF charCode = ORD[' ] THEN {
IF data.amplifySpace # 1.0 THEN {
delta.x ← delta.x * data.amplifySpace;
delta.y ← delta.y * data.amplifySpace;
};
ImagerDefault.CorrectSpaceView[context, delta];
}
ELSE ImagerDefault.CorrectMask[context];
data.cpx ← data.cpx + delta.x;
data.cpy ← data.cpy + delta.y;
residual ← residual - 1;
};
ImagerFontCache.GetRopeData[CorrectingDoChar, fontCache, fontCode, rope, start, len];
};
ENDLOOP;
};
ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ {
data: Data ~ NARROW[context.data];
s0: INTEGER ← SurfaceOriginS[data]-box.y;
s1: INTEGER ← SurfaceOriginS[data]-(box.y+box.h);
f0: INTEGER ← box.x;
f1: INTEGER ← box.x+box.w;
RETURN [ImagerManhattan.CreateFromBox[[MIN[s0, s1], MIN[f0, f1], ABS[s1-s0], ABS[f1-f0]]]];
};
SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ {
data: Data ← NARROW[context.data];
data.viewOrigin ← viewOrigin;
};
GetViewOrigin: PROC [context: Context] RETURNS [viewOrigin: IntPair] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.viewOrigin];
};
SetViewBox: PROC [context: Context, viewBox: IntRectangle] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: viewBox.x+data.viewOrigin.x,
y: viewBox.y+data.viewOrigin.y,
w: viewBox.w,
h: viewBox.h
];
mask: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
ImagerManhattan.Destroy[data.viewClipper];
data.viewClipper ← mask;
};
GetViewBox: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceBox: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipper];
surfaceBox: IntRectangle ← [
x: deviceBox.fMin,
y: SurfaceOriginS[data]-(deviceBox.sMin+deviceBox.sSize),
w: deviceBox.fSize,
h: deviceBox.sSize
];
RETURN[[
x: surfaceBox.x-data.viewOrigin.x,
y: surfaceBox.y-data.viewOrigin.y,
w: surfaceBox.w,
h: surfaceBox.h
]];
};
ClipView: PROC [context: Context, clipBox: IntRectangle, exclude: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: clipBox.x+data.viewOrigin.x,
y: clipBox.y+data.viewOrigin.y,
w: clipBox.w,
h: clipBox.h
];
newBox: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
old: ImagerManhattan.Polygon ← data.viewClipper;
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
data.viewClipper ←
IF exclude THEN ImagerManhattan.Difference[old, newBox]
ELSE ImagerManhattan.Intersection[old, newBox];
newBox.rest ← old;
ImagerManhattan.Destroy[newBox];
};
MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ {
data: Data ~ NARROW[context.data];
m: Transformation ~ SurfaceToDevice[data].FromRec;
sMinDest, fMinDest, sSize, fSize: INTEGER;
shift: Pair ← ImagerTransform.TransformVec[[dest.x-source.x, dest.y-source.y], m];
[[sMinDest, fMinDest, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[[dest.x, dest.y, source.x, source.h], m];
data.canvas.Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data.canvas.ShiftMap[Real.RoundI[shift.x], Real.RoundI[shift.y]]];
};
TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ {
data: Data ~ NARROW[context.data];
state: State ~ data.state;
trans: TransformationRec ~ CompositeT[data, state];
manhattanPolygon: ImagerManhattan.Polygon ← NIL;
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: REAL ~ trans.b * y + trans.c;
s1: REAL ~ trans.b * (y+h) + trans.c;
f0: REAL ~ trans.d * x + trans.f;
f1: REAL ~ trans.d * (x+w) + trans.f;
sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]];
sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]];
fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]];
fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]];
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]];
};
DevicePathFromPath[context, PathMap, NIL];
manhattanPolygon ← ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.canvas.BoundedWindow];
};
visibility ← ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]];
ImagerManhattan.Destroy[manhattanPolygon];
};
GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceToSurface: Transformation ~ ImagerTransform.Invert[SurfaceToDevice[data].FromRec];
RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]]
};
SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [REF] ~ {
SELECT op FROM
ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp];
};
LFDisplayClass: Imager.Class ← NEW [Imager.ClassRep ← [
type: $LFDisplay,
DoSave: ImagerDefault.DoSave,
DoSaveAll: ImagerDefault.DoSaveAll,
SetPriorityImportant: ImagerDefault.SetPriorityImportant,
SetFont: ImagerDefault.SetFont,
SetColor: ImagerDefault.SetColor,
SetNoImage: ImagerDefault.SetNoImage,
SetStrokeWidth: ImagerDefault.SetStrokeWidth,
SetStrokeEnd: ImagerDefault.SetStrokeEnd,
SetAmplifySpace: ImagerDefault.SetAmplifySpace,
SetCorrectShrink: ImagerDefault.SetCorrectShrink,
ConcatT: ImagerDefault.ConcatT,
ScaleT: ImagerDefault.ScaleT,
Scale2T: ImagerDefault.Scale2T,
RotateT: ImagerDefault.RotateT,
TranslateT: ImagerDefault.TranslateT,
Move: ImagerDefault.Move,
Trans: ImagerDefault.Trans,
ShowRope: ShowRope,
ShowText: ShowText,
ShowChar: ShowChar,
SetXY: ImagerDefault.SetXY,
SetXYI: ImagerDefault.SetXYI,
SetXYRel: ImagerDefault.SetXYRel,
SetXYRelI: ImagerDefault.SetXYRelI,
SetSampledColor: ImagerDefault.SetSampledColor,
SetSampledBlack: ImagerDefault.SetSampledBlack,
SetGray: ImagerDefault.SetGray,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskStrokeClosed: MaskStrokeClosed,
MaskVector: ImagerDefault.MaskVector,
MaskVectorI: ImagerDefault.MaskVectorI,
MaskRectangle: MaskRectangle,
MaskRectangleI: MaskRectangleI,
StartUnderline: ImagerDefault.StartUnderline,
MaskUnderline: ImagerDefault.MaskUnderline,
MaskUnderlineI: ImagerDefault.MaskUnderlineI,
MaskPixel: MaskPixel,
ClipOutline: ImagerDefault.ClipOutline,
ExcludeOutline: ImagerDefault.ExcludeOutline,
ClipRectangle: ImagerDefault.ClipRectangle,
ClipRectangleI: ImagerDefault.ClipRectangleI,
ExcludeRectangle: ImagerDefault.ExcludeRectangle,
ExcludeRectangleI: ImagerDefault.ExcludeRectangleI,
CorrectMask: ImagerDefault.CorrectMask,
CorrectSpace: ImagerDefault.CorrectSpace,
Correct: ImagerDefault.Correct,
SetCorrectMeasure: ImagerDefault.SetCorrectMeasure,
SetCorrectTolerance: ImagerDefault.SetCorrectTolerance,
Space: ImagerDefault.Space,
SpaceI: ImagerDefault.SpaceI
]];
ImagerPrivate.RegisterDevice[LFDisplayClass];
END.