ImagerPDImpl.mesa
Copyright (C) 1983, 1984, 1985 Xerox Corporation. All rights reserved.
Michael Plass, February 4, 1985 5:17:56 pm PST
DIRECTORY
Basics,
Font,
Imager,
ImagerBasic,
ImagerBrick,
ImagerConic,
ImagerDefault,
ImagerFontCache,
ImagerHalftone,
ImagerManhattan,
ImagerMasks,
ImagerPD,
ImagerPDExtras,
ImagerPixelMaps,
ImagerPrivate,
ImagerScanConverter,
ImagerStroke,
ImagerTransform,
PDFileWriter,
Real,
RefText,
Rope,
Scaled
;
ImagerPDImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerConic, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, PDFileWriter, Real, RefText, Scaled, ImagerDefault, ImagerManhattan
EXPORTS ImagerPD, ImagerPDExtras
SHARES ImagerTransform
~
BEGIN
OPEN ImagerBasic;
PDFileDescription: TYPE ~ ImagerPD.PDFileDescription;
PDFileDescriptionRep: TYPE ~ ImagerPD.PDFileDescriptionRep;
FONT: TYPE ~ Font.FONT;
ROPE: TYPE ~ Font.ROPE;
DeviceCode: TYPE ~ PDFileWriter.DeviceCode;
Mask: TYPE ~ ImagerMasks.Mask;
Toner: TYPE ~ PDFileWriter.Toner;
TransformationRec:
TYPE ~ ImagerTransform.TransformationRec;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
Tile: TYPE ~ ImagerPixelMaps.Tile;
SpecialColor: TYPE ~ REF ColorRep[special];
Name: TYPE ~ ImagerPrivate.Name;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
Context: TYPE ~ Imager.Context;
State:
TYPE ~ ImagerDefault.State;
Procedures exported to ImagerPD
Raven:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, raven, 300, 300, 2550, 3300, 1, 16, 60000, TRUE, 1
]]]
};
Hornet:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, hornet, 384, 384, 3264, 4224, 1, 16, 60000, TRUE, 1
]]]
};
Gnat:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, gnat, 384, 384, 4224, 5376, 1, 16, 60000, TRUE, 1
]]]
};
PlateMaker:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, mig, 880, 880, 9680, 7480, 1, 16, 60000, TRUE, 1
]]]
};
ReticleMaker:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, reticleMaker, 880, 880, 8800, 8800, 1, 16, 60000, TRUE, 1
These are surely wrong.
]]]
};
Puffin:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, puffin, 384, 384, 4224, 3264, 3, 16, 60000, TRUE, 1
]]]
};
ColorVersatec:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [PDFileDescription] ~ {
RETURN [
NEW[PDFileDescriptionRep ← [
fileName, VAL[7], 200, 200, 20000, 8000, 4, 64, 100000, FALSE, 1
]]]
};
StatsRecord:
TYPE =
RECORD[
loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
Stats:
PROC
RETURNS[StatsRecord] = { x: StatsRecord = stats; stats ← [];
RETURN[x] };
trc: ARRAY [0..64) OF [0..256] ← InitTRC[];
InitTRC:
PROC
RETURNS [trc:
ARRAY [0..64)
OF [0..256]] ~ {
FOR i: [0..64)
IN [0..64)
DO
trc[i] ← Real.RoundLI[i/63.0*256.0];
ENDLOOP;
};
dummyLoadReference: PDFileWriter.LoadReference ~
LAST[PDFileWriter.LoadReference];
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
Clipper info:
viewClipper: ManhattanPolygon, -- in device coords
clientClipper: ClientClipper, -- for noticing client clipper changes
compositeClipper: ManhattanPolygon, -- in device coords; invalid if NIL or clientClipper#state.clipper
Initial Client-to-view info
xScale, yScale: REAL,
View-to-surface info
viewOrigin: IntPair,
Surface-to-device info
surfaceToDevice: Transformation,
surfaceBounds: DeviceRectangle,
Scratch storage
devicePath: ImagerScanConverter.DevicePath,
Cached color info
cachedColor: Color ← NIL,
grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference,
stipples: LIST OF StippleRep ← NIL,
Cached transformation info
cachedT: Transformation ← NIL,
compositeT: Transformation ← NIL,
The font cache
fontCache: ImagerFontCache.FontCache,
Output info
pdState: PDFileWriter.PDState,
firstToner: NAT ← 0,
lastToner: NAT ← 0,
currentToner: NAT ← 1, -- set to SUCC[lastToner] if no toner was explicitly selected.
leftoverMode: BOOLEAN,
pdStatePriorityImportant: INT ← 0
];
CurrentToner:
PROC [data: Data]
RETURNS [toner: Toner] ~
INLINE {
RETURN [IF data.currentToner > data.lastToner THEN black ELSE VAL[data.currentToner]]
};
StippleRep:
TYPE ~
RECORD [
loadReference: PDFileWriter.LoadReference,
stipple: PixelMap
];
Round:
PROC [r:
REAL]
RETURNS [
INTEGER] ~ {
IF r > LAST[INTEGER] THEN RETURN [LAST[INTEGER]];
IF r < FIRST[INTEGER] THEN RETURN [FIRST[INTEGER]];
RETURN [Real.RoundI[r]];
};
CompositeT:
PROC [data: Data, state: State]
RETURNS [Transformation] ~ {
concatenates T with the current view-to-device transformation
Cheats and treats data.compositeT as mutable
pre: Transformation ← state.T;
post: Transformation ← data.surfaceToDevice;
t: Transformation ← IF data.compositeT = NIL THEN data.compositeT ← NEW[ImagerTransform.TransformationRep] ELSE data.compositeT;
IF data.cachedT # pre
THEN {
prec: REAL ← pre.c + data.viewOrigin.x;
pref: REAL ← pre.f + data.viewOrigin.y;
t^ ← [
a: pre.a*post.a + pre.d*post.b,
d: pre.a*post.d + pre.d*post.e,
b: pre.b*post.a + pre.e*post.b,
e: pre.b*post.d + pre.e*post.e,
c: prec*post.a + pref*post.b + post.c,
f: prec*post.d + pref*post.e + post.f
];
data.cachedT ← pre;
};
RETURN [t];
};
Init:
PROC [context: Context, info:
REF] ~ {
data: Data ~ NEW[DataRep];
desc: PDFileDescription ~ NARROW[info];
sScale: REAL ~ desc.sResolution/0.0254;
fScale: REAL ~ desc.fResolution/0.0254;
context.data ← data;
data.surfaceBounds ← [sMin: 0, fMin: 0, sSize: desc.imageSSize, fSize: desc.imageFSize];
IF desc.imageFSize >= desc.imageSSize
THEN {
data.surfaceToDevice ← ImagerTransform.Create[1, 0, 0, 0, 1, 0];
data.xScale ← sScale;
data.yScale ← fScale;
}
ELSE {
data.surfaceToDevice ← ImagerTransform.Create[0, -1, desc.imageSSize, 1, 0, 0];
data.xScale ← fScale;
data.yScale ← sScale;
};
SetViewOrigin[context, [0, 0]];
SetViewBox[context, GetSurfaceBounds[context]];
data.pdState ← PDFileWriter.Create[fileName: desc.fileName, deviceCode: desc.deviceCode, sResolution: desc.sResolution, fResolution: desc.fResolution, imageSSize: desc.imageSSize, imageFSize: desc.imageFSize, bandSSize: desc.bandSSize, copies: desc.copies, leftOverMode: desc.leftovers];
data.leftoverMode ← desc.leftovers;
data.fontCache ← ImagerFontCache.Create[];
FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO data.grayTileRef[toner][i] ← dummyLoadReference ENDLOOP ENDLOOP;
data.lineBuffer ← ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]];
data.firstToner ← IF desc.nColors = 3 THEN 1 ELSE 0;
data.lastToner ← data.firstToner + desc.nColors - 1;
data.currentToner ← data.lastToner + 1;
ImagerDefault.InitState[context];
Reset[context];
};
Reset:
PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
state: State ~ NARROW[context.state];
surfaceSize: Pair ~ ImagerTransform.InverseTransformVec[
[data.surfaceBounds.sSize, data.surfaceBounds.fSize],
data.surfaceToDevice
];
ImagerDefault.Reset[context];
state.T ← ImagerTransform.Scale2[data.xScale, data.yScale];
state.fieldXMax ← state.mediumXSize ← surfaceSize.x/data.xScale;
state.fieldYMax ← state.mediumYSize ← surfaceSize.y/data.yScale;
data.compositeClipper ← NIL;
data.cachedColor ← NIL;
};
DevicePathFromViewPath:
PROC [context: Context, pathMap: PathMapType, pathData:
REF]
RETURNS [ImagerScanConverter.DevicePath] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
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] ~ {
Transforms (x, y) to (s, f)
p.y ← p.y + data.viewOrigin.y;
p.x ← p.x + data.viewOrigin.x;
RETURN[ImagerTransform.Transform[p, data.surfaceToDevice]];
};
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.surfaceBounds]];
};
DevicePathFromPath:
PROC [context: Context, pathMap: PathMapType, pathData:
REF] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
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, state].Contents;
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.surfaceBounds, scratch: data.devicePath];
};
DevicePathFromStroke:
PROC [context: Context, pathMap: PathMapType, pathData:
REF,
strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
data.devicePath ← ImagerStroke.DevicePathFromStroke[
pathMap: pathMap,
pathData: pathData,
clientToDevice: CompositeT[data, state],
width: IF LOOPHOLE[strokeWidth, LONG CARDINAL] # LOOPHOLE[Imager.defaultStrokeWidth, LONG CARDINAL] THEN strokeWidth ELSE state.strokeWidth,
strokeEnd:
IF strokeEnd # nil
THEN strokeEnd
ELSE
SELECT state.strokeEnd
FROM
0 => square,
1 => butt,
2 => round,
ENDCASE => nil,
closed: closed,
clipBox: data.surfaceBounds,
scratch: data.devicePath
];
};
CompositeClipper:
PROC [context: Context]
RETURNS [ManhattanPolygon] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
mp: ManhattanPolygon ← data.compositeClipper;
IF mp =
NIL
OR data.clientClipper # state.clipper
THEN {
ConcatClippers:
PROC [l:
LIST
OF ClientClipperItem] ~ {
IF l#
NIL
THEN {
t1, t2: ManhattanPolygon;
ConcatClippers[l.rest];
t1 ← ImagerScanConverter.ConvertToManhattanPolygon[DevicePathFromViewPath[context: context, pathMap: l.first.pathMap, pathData: l.first.pathData], data.surfaceBounds];
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 ← state.clipper;
mp ← ImagerManhattan.Copy[data.viewClipper];
ConcatClippers[data.clientClipper];
data.compositeClipper ← mp;
};
RETURN [mp]
};
MakeTileSamples:
PROC [xSize, ySize:
NAT, sample:
NAT]
RETURNS [tileSamples: PixelArray] ~ {
tileSamples ← NEW[PixelArrayRep];
tileSamples.m ← ImagerTransform.Rotate[0];
tileSamples.xPixels ← xSize;
tileSamples.yPixels ← ySize;
tileSamples.maxSampleValue ← 255;
tileSamples.samplesPerPixel ← 1;
tileSamples.get ← ConstantGet;
tileSamples.data ← NEW[NAT ← sample];
};
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;
};
deviceBrickDefault: ImagerHalftone.DeviceBrick ← ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255];
ConstructTile:
PROC [data: Data, toner: Toner, index: [0..64), customBrick: ImagerBrick.Brick ←
NIL] ~ {
deviceBrick: ImagerHalftone.DeviceBrick ←
IF customBrick # NIL THEN ImagerHalftone.MakeDeviceBrick[customBrick, 255]
ELSE ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255];
The screen angle really needs to depend on the toner.
pixelMap: ImagerPixelMaps.PixelMap ← ImagerPixelMaps.Create[0, [0, 0, deviceBrick.sPeriod, deviceBrick.fPeriod]];
loadReference: PDFileWriter.LoadReference;
Runs:
PROC [run:
PROC[sMin, fMin:
INTEGER, fSize:
NAT]] ~ {
FOR s:
INTEGER
IN [0..deviceBrick.sPeriod)
DO
run[s, 0, deviceBrick.fPeriod];
ENDLOOP;
};
ImagerHalftone.Halftone[
dest: pixelMap,
runs: Runs,
source: MakeTileSamples[16, 16, trc[index]],
transformation: ImagerTransform.Rotate[0],
deviceBrick: deviceBrick
];
loadReference ← data.pdState.LoadContiguousColorTile[phase: deviceBrick.phase, sMin: 0, fMin: 0, sSize: deviceBrick.sPeriod, fSize: deviceBrick.fPeriod, bitsPtr: pixelMap.refRep.pointer];
data.grayTileRef[toner][index] ← loadReference;
};
maxColorTileArea:
REAL ← 4*4096;
CurrentColor:
PROC [context: Context]
RETURNS [color: Color] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
SetConstantColor:
PROC [intensity:
REAL] ~ {
index: [0..64) ← Real.RoundI[MAX[MIN[intensity*63, 63], 0]];
DoToner:
PROC [toner: Toner] = {
IF index = 0 THEN data.pdState.SetColorInk[toner]
ELSE IF index = 63 THEN data.pdState.SetColorClear[toner]
ELSE {
IF data.grayTileRef[toner][index] = dummyLoadReference THEN ConstructTile[data, toner, index, NARROW[Imager.GetProp[context, $CustomBrick]]];
data.pdState.SetColorTile[toner, data.grayTileRef[toner][index]]
};
};
data.pdState.DoForEachToner[DoToner];
};
NoteColorChange:
PROC ~ {
DoToner: PROC [toner: Toner] = {data.pdState.SetColorClear[toner]};
This crock is needed to get the right priorities into the result in case of leftovers.
data.pdState.DoForEachToner[DoToner];
};
IF data.pdStatePriorityImportant # state.priorityImportant
THEN {
[] ← data.pdState.SetPriorityImportant[state.priorityImportant#0];
data.pdStatePriorityImportant ← state.priorityImportant;
};
IF data.cachedColor = state.color THEN RETURN [data.cachedColor];
color ← data.cachedColor ← state.color;
IF color = Imager.black
THEN {
DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorInk[toner]};
data.pdState.DoForEachToner[DoToner];
}
ELSE
IF color = Imager.white
THEN {
DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorClear[toner]};
data.pdState.DoForEachToner[DoToner];
}
ELSE
WITH color
SELECT FROM
constantColor: ConstantColor => {
SetConstantColor[constantColor.Y/65536.0];
};
specialColor: SpecialColor => {
WITH specialColor.ref
SELECT
FROM
stipple:
REF
CARDINAL => {
oneBits: NAT ← 0;
FOR v: CARDINAL ← stipple^, v/2 UNTIL v=0 DO oneBits ← oneBits + v MOD 2 ENDLOOP;
SetConstantColor[(16-oneBits)/16.0];
};
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
sampledColor: SampledColor => {
NoteColorChange[];
IF sampledColor.colorOperator = $SampledBlack
THEN {
transform: Transformation ←
ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[
sampledColor.pa.m, sampledColor.m],
ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]],
data.surfaceToDevice];
tileRect: Rectangle ← ImagerTransform.TransformRectangle[[0, 0, sampledColor.pa.xPixels, sampledColor.pa.yPixels], transform];
transformRec: TransformationRec ← ImagerTransform.Contents[transform];
easyTransform: BOOLEAN ~ (transformRec.a = 0 AND transformRec.e = 0) OR (transformRec.b = 0 AND transformRec.d = 0);
tileArea: REAL ← tileRect.w*tileRect.h;
IF easyTransform
AND IsInteger[tileRect.w]
AND IsInteger[tileRect.h]
AND tileArea <= maxColorTileArea
AND data.pdState.RemainingLoadSize > Real.RoundLI[tileArea/16] + 3500
THEN {
mask: Mask ← ImagerMasks.FromPixelArray[sampledColor.pa, transform];
bb: DeviceRectangle ← ImagerMasks.BoundingBox[mask];
pixels: PixelMap ← ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]];
tFlag: PDFileWriter.TFlag ← IF sampledColor.transparent THEN transparent ELSE opaque;
color ← Imager.black;
FOR s:
LIST
OF StippleRep ← data.stipples, s.rest
UNTIL s =
NIL
DO
IF s.first.stipple.Equal[pixels]
THEN {
data.pdState.SetColorTile[CurrentToner[data], s.first.loadReference, tFlag];
pixels.refRep.pointer ← NIL;
pixels.refRep.ref ← NIL;
pixels.refRep ← NIL;
RETURN;
};
ENDLOOP;
data.stipples ←
CONS[[
loadReference: data.pdState.LoadContiguousColorTile[
phase: 0,
sMin: pixels.sMin+pixels.sOrigin,
fMin: pixels.fMin+pixels.fOrigin,
sSize: pixels.sSize, fSize: pixels.fSize,
bitsPtr: pixels.refRep.pointer
],
stipple: pixels], data.stipples];
data.pdState.SetColorTile[CurrentToner[data], data.stipples.first.loadReference, tFlag];
RETURN;
};
data.pdState.SetColorInk[CurrentToner[data]];
};
};
ENDCASE => state.color ← Imager.black;
};
IsInteger:
PROC [r:
REAL]
RETURNS [
BOOLEAN] ~ {
IF ABS[r] > 32000 THEN RETURN [FALSE];
RETURN [Real.RoundLI[r] = (r+1000.0)-1000.0];
};
ApplyMask:
PROC [context: Context, mask:
REF, sTranslate, fTranslate:
INTEGER ← 0] ~ {
state: State ~ NARROW[context.state];
IF state.noImage = 0
THEN {
data: Data ~ NARROW[context.data];
color: Color ~ CurrentColor[context];
clipper: ManhattanPolygon ← CompositeClipper[context];
WITH color
SELECT
FROM
sampledColor: SampledColor =>
TRUSTED {
lineBuffer: ImagerPixelMaps.PixelMap ← ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]];
customBrick: ImagerBrick.Brick ← NARROW[Imager.GetProp[context, $CustomBrick]];
brick: ImagerHalftone.DeviceBrick ←
IF customBrick #
NIL THEN
ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue]
ELSE
ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, sampledColor.pa.maxSampleValue];
transform: Transformation ←
ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[
sampledColor.pa.m, sampledColor.m],
ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]],
data.surfaceToDevice];
linePointer: LONG POINTER ← lineBuffer.refRep.pointer;
Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]};
SampledColorRun:
PROC [sMin, fMin:
INTEGER, fSize:
NAT] ~
TRUSTED {
Runs:
PROC [run:
PROC[sMin, fMin:
INTEGER, fSize:
NAT]] ~
TRUSTED {
run[sMin, fMin, fSize];
};
lineBuffer.sOrigin ← sMin;
lineBuffer.fOrigin ← fMin;
lineBuffer.fSize ← fSize;
ImagerHalftone.Halftone[lineBuffer, Runs, sampledColor.pa, transform, brick, invertOutput];
PDFileWriter.ColorSamples[
pdState: data.pdState,
toner: CurrentToner[data],
sMin: sMin,
fMin: fMin,
sSize: 1,
fSize: fSize,
deliverProc: Line,
tFlag: IF sampledColor.transparent THEN transparent ELSE opaque
];
};
invertOutput: BOOLEAN ← FALSE;
SELECT sampledColor.colorOperator
FROM
$SampledBlack => invertOutput ← TRUE;
$Intensity => NULL;
ENDCASE => Imager.Error[$UnknownColorModel];
ImagerMasks.GenerateRuns[mask, clipper, SampledColorRun, sTranslate, fTranslate];
};
ENDCASE => {
DeliverRuns:
PROC [SendRun:
PROC [sMin, fMin, fSize:
CARDINAL]] ~ {
Run:
PROC [sMin, fMin:
INTEGER, fSize:
NAT] ~ {
SendRun[sMin, fMin, fSize];
};
ImagerMasks.GenerateRuns[mask, clipper, Run, sTranslate, fTranslate];
};
PDFileWriter.MaskRunGroup[data.pdState, DeliverRuns];
};
};
};
MaskStroke:
PROC [context: Context, pathMap: PathMapType, pathData:
REF,
strokeWidth: REAL, strokeEnd: StrokeEnd] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE];
ApplyMask[context, data.devicePath];
};
MaskStrokeClosed:
PROC [context: Context, pathMap: PathMapType, pathData:
REF,
strokeWidth: REAL] = {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE];
ApplyMask[context, data.devicePath];
};
MaskFill:
PROC[context: Context, pathMap: PathMapType, pathData:
REF] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromPath[context, pathMap, pathData];
ApplyMask[context, data.devicePath];
};
MaskPixel:
PROC [context: Context, pa: PixelArray] ~ {
data: Data ← NARROW[context.data];
state: State ~ NARROW[context.state];
trans: Transformation ~ CompositeT[data, state];
mask: REF ← ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]];
ApplyMask[context, mask];
mask ← NIL;
};
specialCaseRectangles:
BOOLEAN ←
TRUE;
MaskRectangle:
PROC [context: Context, x, y, w, h:
REAL] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
compositeT: Transformation ~ CompositeT[data, state];
IF specialCaseRectangles
AND Easy[compositeT]
THEN {
clipper: ManhattanPolygon ~ CompositeClipper[context];
color: Color ~ CurrentColor[context];
IF clipper#
NIL
AND clipper.rest =
NIL
AND color.tag = constant
THEN {
drect: ImagerPixelMaps.DeviceRectangle ← DeviceRectangleFromEasyBox[compositeT, x, y, w, h];
drect ← ImagerPixelMaps.Intersect[drect, clipper.first];
IF drect.sSize # 0 THEN PDFileWriter.MaskRectangle[data.pdState, drect.sMin, drect.fMin, drect.sSize, drect.fSize];
}
ELSE {
t1: ManhattanPolygon ← ManhattanPolygonFromEasyBox[compositeT, x, y, w, h];
clipped: ManhattanPolygon ← t1.Intersection[clipper];
IF clipped = NIL THEN NULL
ELSE
IF clipped.rest =
NIL
AND color.tag = constant
THEN {
PDFileWriter.MaskRectangle[data.pdState, clipped.first.sMin, clipped.first.fMin, clipped.first.sSize, clipped.first.fSize];
}
ELSE ApplyMask[context, t1, 0, 0];
ImagerManhattan.Destroy[t1];
ImagerManhattan.Destroy[clipped];
};
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
MaskFill[context, PathMap, NIL];
};
};
IntegerMaskRectangle:
PROC[context: Context, x, y, w, h:
INTEGER] = {
MaskRectangle[context, x, y, w, h];
};
rasterToRunGroupStorageRatio:
INT ← 1;
CharRepresentation: TYPE ~ {null, runGroup, raster};
CharLoadInfo:
TYPE ~
RECORD[
sWidth, fWidth: Scaled.Value,
loadRef: PDFileWriter.LoadReference,
representation: CharRepresentation,
mask: Mask,
loadRepSize: INT
];
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 [context: Context, font:
FONT]
RETURNS [t: Transformation] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
r: TransformationRec ← CompositeT[data, state].Contents;
r.c ← r.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, r.FromRec];
};
bigRectangle: LIST OF DeviceRectangle ~ LIST[[0, 0, LAST[NAT]/2, LAST[NAT]/2]];
LoadRunGroup:
PROC [pdState: PDFileWriter.PDState, mask: Mask]
RETURNS [loadReference: PDFileWriter.LoadReference] ~ {
DeliverRuns:
PROC [SendRun:
PROC [sMin, fMin, fSize:
CARDINAL]] = {
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {SendRun[sMin, fMin, fSize]};
ImagerMasks.GenerateRuns[mask, bigRectangle, Run, -bb.sMin, -bb.fMin];
};
bb: DeviceRectangle ← ImagerMasks.BoundingBox[mask];
loadReference ← pdState.LoadRunGroup[DeliverRuns];
stats.loadRunGroups ← stats.loadRunGroups + 1;
};
LoadBitmap:
PROC [pdState: PDFileWriter.PDState, mask: Mask]
RETURNS [loadReference: PDFileWriter.LoadReference] ~ {
WITH mask
SELECT
FROM
b:
REF ImagerPixelMaps.PixelMap => {
loadReference ← pdState.LoadContiguousSampleArray[sSize: b^.sSize, fSize: b^.fSize, bitsPtr: b^.refRep.pointer];
};
ENDCASE => ERROR;
stats.loadRasters ← stats.loadRasters + 1;
};
LoadCharData:
PROC[self: ImagerFontCache.FontObject, charCode:
CARDINAL]
RETURNS [charData: REF, memoryCost: INT] ~ {
font: FONT ~ NARROW[self.fontAnyData];
char: CHAR ~ VAL[charCode];
transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device
clientTransform: Transformation ~ ImagerTransform.Concat[
ImagerTransform.Invert[font.actualTransformation], transform];
loadInfo: REF CharLoadInfo ~ NEW[CharLoadInfo];
mask: ImagerManhattan.Polygon ~ GetCharMask[font, transform, char];
nRuns: INT ~ ImagerManhattan.CountRuns[mask];
bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask];
runGroupSize: INT ← 2 * nRuns;
rasterSize: INT ← bb.sSize * INT[(bb.fSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], clientTransform];
loadInfo.loadRef ← dummyLoadReference;
loadInfo.mask ← mask;
SELECT
TRUE
FROM
bb.sSize = 0
OR bb.fSize = 0 => {
loadInfo.representation ← null;
loadInfo.loadRepSize ← 0;
};
bb.fSize > 32*Basics.bitsPerWord OR
runGroupSize*rasterToRunGroupStorageRatio < rasterSize => {
loadInfo.representation ← runGroup;
loadInfo.loadRepSize ← runGroupSize;
};
ENDCASE => {
loadInfo.representation ← raster;
loadInfo.loadRepSize ← rasterSize;
loadInfo.mask ← ImagerMasks.FromBitmap[ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]]];
ImagerManhattan.Destroy[mask];
};
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
RETURN [loadInfo, 0];
};
ShowChar:
PROC [context: Context, char:
CHAR, font:
FONT] ~ {
text: REF TEXT ← RefText.ObtainScratch[1];
text.length ← 1;
text[0] ← char;
ShowCharacters[context, text, font, 0, 1];
RefText.ReleaseScratch[text];
};
loadSpaceReservedForColorTiles:
INT ← 3500;
biggishInt:
REAL ←
INT.
LAST/2;
ShowCharacters:
PROC [
context: Context,
characters: REF, -- may be a ROPE or a REF TEXT
font: FONT,
start: INT ← 0,
length: INT ← LAST[INT]
] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
showVec: FONT ← IF font = NIL THEN NARROW[state.showVec] ELSE font;
surfaceToDevice: TransformationRec ← data.surfaceToDevice.Contents;
TransformDeviceToViewVec:
PROC [s, f: Scaled.Value]
RETURNS [v: Pair] ~ {
v ←
IF surfaceToDevice.a = 0
THEN [x: Scaled.Float[f], y: -Scaled.Float[s]]
ELSE [x: Scaled.Float[s], y: Scaled.Float[f]]
};
TransformViewToDevice:
PROC [v: Pair]
RETURNS [Pair] ~ {
RETURN [
[t.a*(v.x+data.viewOrigin.x) + t.b*(v.y+data.viewOrigin.y) + t.c,
t.d*(v.x+data.viewOrigin.x) + t.e*(v.y+data.viewOrigin.y) + t.f], where t=data.surfaceToDevice.
IF surfaceToDevice.a = 0
THEN
a=e=0, b=-1, d=1
[-(v.y+data.viewOrigin.y) + surfaceToDevice.c,
(v.x+data.viewOrigin.x) + surfaceToDevice.f]
ELSE
a=e=1, b=d=0
[(v.x+data.viewOrigin.x) + surfaceToDevice.c,
(v.y+data.viewOrigin.y) + surfaceToDevice.f]
]};
clipper: ManhattanPolygon ← CompositeClipper[context];
transform: TransformationRec ~ FontCompositeTransform[context, showVec].Contents;
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,
fontScalarData: 0, -- unused
fontAnyData: showVec]];
bbScratch: LIST OF DeviceRectangle ← LIST[[0,0,0,0]];
noImage: BOOLEAN ~ state.noImage # 0;
currentColor: Color ~ CurrentColor[context];
DoChar:
PROC[charCode:
CARDINAL, charData:
REF] = {
loadInfo: REF CharLoadInfo ~ NARROW[charData];
delta: Pair ← TransformDeviceToViewVec[loadInfo.sWidth, loadInfo.fWidth];
cp: Pair ← TransformViewToDevice[[state.cpx, state.cpy]];
bbScratch.first ← ImagerMasks.BoundingBox[loadInfo.mask];
IF bbScratch.first.sSize > 0
AND
NOT noImage
AND
ABS[cp.x] <= biggishInt
AND
ABS[cp.y] < biggishInt
THEN {
sCP: INT ← Real.RoundLI[cp.x];
fCP: INT ← Real.RoundLI[cp.y];
sMinTranslated: INT ← bbScratch.first.sMin + sCP;
fMinTranslated: INT ← bbScratch.first.fMin + fCP;
visibility: Visibility;
IF
ABS[sMinTranslated] <=
INTEGER.
LAST
AND
ABS[fMinTranslated] <=
INTEGER.
LAST
THEN {
bbScratch.first.sMin ← sMinTranslated;
bbScratch.first.fMin ← fMinTranslated;
visibility ← ImagerManhattan.IsVisible[bbScratch, clipper];
}
ELSE {
visibility ← invisible;
};
IF visibility = visible
THEN
WITH currentColor
SELECT
FROM
sampled: SampledColor => visibility ← partlyVisible;
ENDCASE => NULL;
IF loadInfo.loadRef = dummyLoadReference
AND visibility = visible
THEN {
IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + loadSpaceReservedForColorTiles
THEN {
SELECT loadInfo.representation
FROM
raster => loadInfo.loadRef ← LoadBitmap[data.pdState, loadInfo.mask];
runGroup => loadInfo.loadRef ← LoadRunGroup[data.pdState, loadInfo.mask];
ENDCASE;
}
ELSE visibility ← partlyVisible;
};
SELECT visibility
FROM
visible => {
SELECT loadInfo.representation
FROM
raster => {
IF loadInfo.loadRef = dummyLoadReference THEN ERROR;
data.pdState.MaskSamplesRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin];
stats.rasterChars ← stats.rasterChars + 1;
};
runGroup => {
IF loadInfo.loadRef = dummyLoadReference THEN ERROR;
data.pdState.MaskRunGroupRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin];
stats.runGroupChars ← stats.runGroupChars + 1;
};
null => NULL;
ENDCASE => ERROR;
};
partlyVisible => {
ApplyMask[context, loadInfo.mask, sCP, fCP];
stats.clippedChars ← stats.clippedChars + 1;
};
invisible => {stats.culledChars ← stats.culledChars + 1};
ENDCASE => ERROR;
};
IF charCode =
ORD[' ]
THEN {
IF state.amplifySpace # 1.0
THEN {
delta.x ← delta.x * state.amplifySpace;
delta.y ← delta.y * state.amplifySpace;
};
ImagerDefault.CorrectSpaceView[context, delta];
}
ELSE {
ImagerDefault.CorrectMask[context];
};
state.cpx ← state.cpx + delta.x;
state.cpy ← state.cpy + delta.y;
};
ImagerFontCache.GetStringData[DoChar, data.fontCache, fontCode, characters, start, length];
};
ManhattanPolygonFromSurfaceRectangle:
PROC [context: Context, box: IntRectangle]
RETURNS [
LIST
OF DeviceRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceBox: IntRectangle ← ImagerTransform.TransformIntRectangle[[x: box.x, y: box.y, w: box.w, h: box.h], data.surfaceToDevice];
RETURN [ImagerManhattan.CreateFromBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]];
};
SetViewOrigin:
PROC [context: Context, viewOrigin: IntPair] ~ {
data: Data ← NARROW[context.data];
data.viewOrigin ← viewOrigin;
data.cachedT ← NIL;
};
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];
deviceToSurface: Transformation ← ImagerTransform.Invert[data.surfaceToDevice];
surfaceBox: IntRectangle ← ImagerTransform.TransformIntRectangle[[deviceBox.sMin, deviceBox.fMin, deviceBox.sSize, deviceBox.fSize], deviceToSurface];
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] ~ {
ERROR Imager.Error[$NotImplementedForThisDevice];
};
Zero:
PROC [real:
REAL]
RETURNS [
BOOLEAN] ~
INLINE {
RETURN [real=0.0]};
Easy:
PROC [trans: Transformation]
RETURNS [
BOOLEAN] ~ {
RETURN [(Zero[trans.Contents.b] AND Zero[trans.Contents.d]) OR (Zero[trans.Contents.a] AND Zero[trans.Contents.e])]
};
ManhattanPolygonFromEasyBox:
PROC [compositeT: Transformation, x, y, w, h:
REAL]
RETURNS [manhattanPolygon: ImagerManhattan.Polygon] ~ {
manhattanPolygon ← ImagerManhattan.CreateFromBox[DeviceRectangleFromEasyBox[compositeT, x, y, w, h]];
};
DeviceRectangleFromEasyBox:
PROC [compositeT: Transformation, x, y, w, h:
REAL]
RETURNS [deviceRectangle: DeviceRectangle] ~ {
trans: TransformationRec ~ compositeT.Contents;
x1: REAL ~ x+w;
y1: REAL ~ y+h;
s0: INTEGER ~ Round[trans.a * x + trans.b * y + trans.c];
s1: INTEGER ~ Round[trans.a * x1 + trans.b * y1 + trans.c];
f0: INTEGER ~ Round[trans.d * x + trans.e * y + trans.f];
f1: INTEGER ~ Round[trans.d * x1 + trans.e * y1 + trans.f];
sMin: INTEGER ~ MIN[s0, s1];
sMax: INTEGER ~ MAX[s0, s1];
fMin: INTEGER ~ MIN[f0, f1];
fMax: INTEGER ~ MAX[f0, f1];
deviceRectangle ← [sMin, fMin, sMax-sMin, fMax-fMin];
};
TestRectangle:
PROC [context: Context, x, y, w, h:
REAL]
RETURNS [visibility: Visibility] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
compositeT: Transformation ~ CompositeT[data, state];
manhattanPolygon: ImagerManhattan.Polygon ← NIL;
IF specialCaseRectangles
AND Easy[compositeT]
THEN
manhattanPolygon ← ManhattanPolygonFromEasyBox[compositeT, x, y, w, h]
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.surfaceBounds];
};
visibility ← ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]];
ImagerManhattan.Destroy[manhattanPolygon];
};
GetSurfaceBounds:
PROC[context: Context]
RETURNS[IntRectangle] = {
data: Data ~ NARROW[context.data];
b: DeviceRectangle ~ data.surfaceBounds;
deviceToSurface: Transformation ~ ImagerTransform.Invert[data.surfaceToDevice];
RETURN [ImagerTransform.TransformIntRectangle[[b.sMin, b.fMin, b.sSize, b.fSize], deviceToSurface]]
};
NewToner:
PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
toners: PDFileWriter.TonerSet ← ALL[FALSE];
IF data.currentToner = data.lastToner THEN Imager.Error[$TooManyToners];
IF data.currentToner > data.lastToner THEN data.currentToner ← data.firstToner
ELSE {
data.currentToner ← SUCC[data.currentToner];
data.pdState.EndPage;
};
toners[VAL[data.currentToner]] ← TRUE;
data.pdState.StartImage[toners: toners, feed: data.currentToner = data.firstToner, strip: data.currentToner = data.lastToner];
data.cachedColor ← NIL;
};
NewPage:
PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
WHILE data.currentToner < data.lastToner DO NewToner[context] ENDLOOP;
data.pdState.EndPage;
Reset[context];
data.pdStatePriorityImportant ← 0;
data.currentToner ← SUCC[data.lastToner];
};
Close:
PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
WHILE data.currentToner < data.lastToner DO NewToner[context] ENDLOOP;
data.pdState.EndPage;
data.pdState.Close;
};
SpecialOp:
PROC[context: Context, op:
ATOM, data:
REF]
RETURNS [
REF ←
NIL] = {
SELECT op
FROM
$NewPage => NewPage[context];
$NewToner => NewToner[context];
$Close => Close[context];
ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp];
};
PDClass: ImagerPrivate.Class ←
NEW [ImagerPrivate.ClassRep ← [
deviceType: $PD,
Init: Init,
ISet: ImagerDefault.ISet,
ISetReal: ImagerDefault.ISetReal,
ISetInt: ImagerDefault.ISetInt,
SetSampledColor: ImagerDefault.SetSampledColor,
SetSampledBlack: ImagerDefault.SetSampledBlack,
DoSave: ImagerDefault.DoSave,
DoSaveAll: ImagerDefault.DoSaveAll,
ConcatT: ImagerDefault.ConcatT,
TranslateT: ImagerDefault.TranslateT,
RotateT: ImagerDefault.RotateT,
ScaleT: ImagerDefault.ScaleT,
Scale2T: ImagerDefault.Scale2T,
Move: ImagerDefault.Move,
Trans: ImagerDefault.Trans,
SetXY: ImagerDefault.SetXY,
IntegerSetXY: ImagerDefault.IntegerSetXY,
SetXYRel: ImagerDefault.SetXYRel,
IntegerSetXYRel: ImagerDefault.IntegerSetXYRel,
GetCP: ImagerDefault.GetCP,
GetCPRounded: ImagerDefault.GetCPRounded,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskStrokeClosed: MaskStrokeClosed,
MaskVector: ImagerDefault.MaskVector,
IntegerMaskVector: ImagerDefault.IntegerMaskVector,
MaskRectangle: MaskRectangle,
IntegerMaskRectangle: IntegerMaskRectangle,
StartUnderline: ImagerDefault.StartUnderline,
MaskUnderline: ImagerDefault.MaskUnderline,
IntegerMaskUnderline: ImagerDefault.IntegerMaskUnderline,
MaskPixel: MaskPixel,
ClipOutline: ImagerDefault.ClipOutline,
ExcludeOutline: ImagerDefault.ExcludeOutline,
ClipRectangle: ImagerDefault.ClipRectangle,
ExcludeRectangle: ImagerDefault.ExcludeRectangle,
IntegerClipRectangle: ImagerDefault.IntegerClipRectangle,
IntegerExcludeRectangle: ImagerDefault.IntegerExcludeRectangle,
ShowChar: ShowChar,
ShowCharacters: ShowCharacters,
CorrectMask: ImagerDefault.CorrectMask,
CorrectSpace: ImagerDefault.CorrectSpace,
SetCorrectMeasure: ImagerDefault.SetCorrectMeasure,
SetCorrectTolerance: ImagerDefault.SetCorrectTolerance,
Space: ImagerDefault.Space,
IntegerSpace: ImagerDefault.IntegerSpace,
Correct: ImagerDefault.Correct,
Reset: Reset,
SetViewOrigin: SetViewOrigin,
GetViewOrigin: GetViewOrigin,
SetViewBox: SetViewBox,
GetViewBox: GetViewBox,
ClipView: ClipView,
MoveSurfaceRectangle: MoveSurfaceRectangle,
TestRectangle: TestRectangle,
GetSurfaceBounds: GetSurfaceBounds,
SpecialOp: SpecialOp
]];
ImagerPrivate.RegisterDevice[PDClass];
END.