ImagerPDImpl.mesa
Michael Plass, June 22, 1984 9:41:51 am PDT
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
~ 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: StatsRecord ← [];
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,
The font cache
fontCache: ImagerFontCache.FontCache,
Output info
pdState: PDFileWriter.PDState,
leftoverMode: BOOLEAN,
pdStatePriorityImportant: INT ← 0
];
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
t: TransformationRec ← state.T.Contents;
t.c ← t.c + data.viewOrigin.x;
t.f ← t.f + data.viewOrigin.y;
RETURN [ImagerTransform.Concat[t.FromRec, data.surfaceToDevice]];
};
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]];
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;
};
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 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];
};
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 => {
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 < 4096
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[black, 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[black, data.stipples.first.loadReference, tFlag];
RETURN;
};
data.pdState.SetColorInk[black];
};
};
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[data.pdState, black, sMin, fMin, 1, fSize, Line, IF sampledColor.transparent THEN transparent ELSE opaque];
};
invertOutput: BOOLEANFALSE;
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: BOOLEANTRUE;
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 {
t1: ManhattanPolygon ← ManhattanPolygonFromEasyBox[compositeT, x, y, w, h];
color: Color ~ CurrentColor[context];
clipped: ManhattanPolygon ← t1.Intersection[CompositeClipper[context]];
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];
};
ShowCharacters: PROC [
context: Context,
characters: REF, -- may be a ROPE or a REF TEXT
font: FONT,
start: INT ← 0,
length: INTLAST[INT]
] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
showVec: FONTIF 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]];
IF NOT noImage AND ABS[cp.x] < 32000.0 AND ABS[cp.y] < 32000.0 THEN {
sCP: INTEGER ← Real.RoundI[cp.x];
fCP: INTEGER ← Real.RoundI[cp.y];
visibility: Visibility;
bbScratch.first ← ImagerMasks.BoundingBox[loadInfo.mask];
bbScratch.first.sMin ← bbScratch.first.sMin + sCP;
bbScratch.first.fMin ← bbScratch.first.fMin + fCP;
visibility ← ImagerManhattan.IsVisible[bbScratch, clipper];
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 + 3500 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;
};
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 [LOOPHOLE[real, LONG CARDINAL] = LOOPHOLE[0.0, LONG CARDINAL]]
};
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] ~ {
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];
manhattanPolygon ← ImagerManhattan.CreateFromBox[[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]]
};
NewPage: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.pdState.EndPage;
Reset[context];
data.pdStatePriorityImportant ← 0;
};
Close: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.pdState.EndPage;
data.pdState.Close;
};
SpecialOp: PROC[context: Context, op: ATOM, data: REF] RETURNS [REFNIL] = {
SELECT op FROM
$NewPage => NewPage[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.