ImagerPDImpl.mesa
Michael Plass, October 21, 1983 12:11 pm
DIRECTORY
Environment,
Font,
Imager,
ImagerBasic USING [Color, ColorRep, ConstantColor, IntPair, IntRectangle, Pair, PathMapType, PixelArray, PixelArrayRep, PixelBuffer, PixelBufferRep, Rectangle, SampledColor, StrokeEnd, Transformation, TransformType, Visibility],
ImagerConic,
ImagerFontCache,
ImagerHalftone,
ImagerMasks,
ImagerPD,
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
EXPORTS ImagerPD
~ BEGIN OPEN ImagerBasic;
PDFileDescription: TYPE ~ ImagerPD.PDFileDescription;
PDFileDescriptionRep: TYPE ~ ImagerPD.PDFileDescriptionRep;
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, 7480, 9680, 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
]]]
};
DeviceCode: TYPE ~ PDFileWriter.DeviceCode;
FONT: TYPE ~ Font.FONT;
Mask: TYPE ~ ImagerMasks.Mask;
Name: TYPE ~ ImagerPrivate.Name;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
DeviceRectangle: TYPE ~ ImagerPixelMaps.DeviceRectangle;
ROPE: TYPE ~ Rope.ROPE;
SpecialColor: TYPE ~ REF ColorRep[special];
Tile: TYPE ~ ImagerPixelMaps.Tile;
Toner: TYPE ~ PDFileWriter.Toner;
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];
Context: TYPE ~ Imager.Context;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
currentColor: Color,
clientClipper: Mask, -- in device coords
clientClipperPresent: BOOLEAN,
clientClipperExclude: BOOLEAN,
compositeClipperValid: BOOLEAN,
leftoverMode: BOOLEAN,
doSaveCount: [0..1000) ← 0,
compositeClipper: Mask, -- in device coords
viewClipper: Mask, -- in device coords
sOriginView, fOriginView: INTEGER,
sOriginBrick, fOriginBrick: INTEGER,
currentPosition: Pair, -- in device space
transformation: Transformation,
viewToDevice: Transformation,
surfaceToDevice: Transformation,
surfaceBounds: DeviceRectangle,
devicePath: ImagerScanConverter.DevicePath,
grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference,
fontCache: ImagerFontCache.FontCache,
lineBuffer: ImagerPixelMaps.PixelMap,
pdState: PDFileWriter.PDState,
priorityImportant: INT ← 0
];
deviceBrick: ImagerHalftone.DeviceBrick ← ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255];
Init: PROC [context: Context, info: REF] ~ {
data: Data ~ NEW[DataRep];
desc: PDFileDescription ~ NARROW[info];
sScale: REAL ~ desc.sResolution/72.0;
fScale: REAL ~ desc.fResolution/72.0;
context.data ← data;
data.surfaceBounds ← [sMin: 0, fMin: 0, sSize: desc.imageSSize, fSize: desc.imageFSize];
data.surfaceToDevice ←
IF desc.imageFSize >= desc.imageSSize
THEN ImagerTransform.Create[sScale, 0, 0, 0, fScale, 0]
ELSE ImagerTransform.Create[0, -sScale, desc.imageSSize, fScale, 0, 0];
data.pdState ← PDFileWriter.Create[fileName: desc.fileName, deviceCode: desc.deviceCode, sResolution: desc.sResolution, fResolution: desc.fResolution, imageSSize: desc.imageSSize, imageFSize: desc.imageFSize, nColors: desc.nColors, 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]];
Reset[context];
};
Reset: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.devicePath ← NIL;
data.transformation ← data.viewToDevice ← data.surfaceToDevice;
data.clientClipperPresent ← data.clientClipperExclude ← data.compositeClipperValid ← FALSE;
data.currentPosition ← [0, 0];
data.viewClipper ← ImagerMasks.Box[data.surfaceBounds];
[[data.sOriginView, data.fOriginView]] ← [[data.sOriginBrick, data.fOriginBrick]] ← ImagerTransform.IntTransform[[0,0], data.surfaceToDevice];
SetColor[data, Imager.black];
};
DoSaveAll: PROC [context: Context, body: PROC] ~ {
data: Data ← NARROW[context.data];
cp: Pair ← data.currentPosition;
DoSave[context, body];
data.currentPosition ← cp;
};
DoSave: PROC [context: Context, body: PROC] ~ {
data: Data ← NARROW[context.data];
color: Color ← data.currentColor;
clipper: Clipper ← GetClipper[context];
transformation: Transformation ← data.transformation;
sOriginView: INTEGER ← data.sOriginView;
fOriginView: INTEGER ← data.fOriginView;
sOriginBrick: INTEGER ← data.sOriginBrick;
fOriginBrick: INTEGER ← data.fOriginBrick;
data.doSaveCount ← data.doSaveCount + 1;
body[];
data.doSaveCount ← data.doSaveCount - 1;
data.sOriginView ← sOriginView;
data.fOriginView ← fOriginView;
data.sOriginBrick ← sOriginBrick;
data.fOriginBrick ← fOriginBrick;
data.transformation ← transformation;
SetClipper[context, clipper];
IF data.currentColor # color THEN SetColor[data, color];
};
ConcatT: PROC [context: Context, m: Transformation] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.Concat[m, data.transformation];
};
TranslateT: PROC [context: Context, x, y: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreTranslate[x, y, data.transformation];
};
RotateT: PROC [context: Context, a: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreRotate[a, data.transformation];
};
ScaleT: PROC [context: Context, s: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreScale[s, s, data.transformation];
};
Scale2T: PROC [context: Context, sx, sy: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreScale[sx, sy, data.transformation];
};
Move: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.transformation.c ← data.currentPosition.x;
data.transformation.f ← data.currentPosition.x;
};
Trans: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.transformation.c ← Real.RoundLI[data.currentPosition.x];
data.transformation.f ← Real.RoundLI[data.currentPosition.x];
};
Clipper: TYPE ~ REF ClipperRep;
ClipperRep: TYPE ~ RECORD [
exclude: BOOLEAN,
mask: Mask
];
GetClipper: PROC [context: Context] RETURNS [Clipper] ~ {
data: Data = NARROW[context.data];
IF data.clientClipperPresent THEN {
clipper: Clipper = NEW[ClipperRep];
clipper.exclude ← data.clientClipperExclude;
clipper.mask ← data.clientClipper.InlineShift[-data.sOriginView, -data.fOriginView];
ccCount ← ccCount + 1;
RETURN [clipper]
}
ELSE RETURN [NIL]
};
SetClipper: PROC [context: Context, clipper: Clipper] ~ {
data: Data = NARROW[context.data];
IF clipper = NIL THEN {
data.clientClipperPresent ← FALSE;
data.clientClipper.refRep ← NIL;
ccCount ← ccCount + 1;
}
ELSE {
data.clientClipperPresent ← TRUE;
data.clientClipperExclude ← clipper.exclude;
data.clientClipper ← clipper.mask.Shift[data.sOriginView, data.fOriginView];
ccCount ← ccCount + 1;
};
data.compositeClipperValid ← FALSE;
};
IGet: PROC [context: Context, n: Name] RETURNS [REF] = {
data: Data = NARROW[context.data];
SELECT n FROM
$T => RETURN[ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]];
$color => RETURN[data.currentColor];
ENDCASE => ERROR;
};
ISet: PROC[context: Context, n: Name, x: REF] = {
data: Data = NARROW[context.data];
SELECT n FROM
$T => data.transformation ← ImagerTransform.Concat[x, data.viewToDevice];
$color => SetColor[data, NARROW[x, Color]];
ENDCASE => ERROR;
};
IGetReal: PROC[context: Context, n: Name] RETURNS[REAL] = {
data: Data = NARROW[context.data];
SELECT n FROM
ENDCASE => ERROR;
};
ISetReal: PROC[context: Context, n: Name, x: REAL] = {
data: Data = NARROW[context.data];
SELECT n FROM
ENDCASE => ERROR;
};
IGetInt: PROC[context: Context, n: Name] RETURNS[INT] = {
data: Data = NARROW[context.data];
SELECT n FROM
priorityImportant => {
RETURN [data.priorityImportant]
};
ENDCASE => ERROR;
};
ISetInt: PROC[context: Context, n: Name, x: INT] = {
data: Data = NARROW[context.data];
SELECT n FROM
priorityImportant => {
data.priorityImportant ← x;
[] ← data.pdState.SetPriorityImportant[data.leftoverMode AND x # 0];
};
ENDCASE => ERROR;
};
MaskFromPath: PROC [data: Data, pathMap: PathMapType, pathData: REF]
RETURNS [mask: Mask] = {
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
= {
m: Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f) — note s comes first for ScanConvert!
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 ← p };
Xline: PROC [p: Pair] ~ { q: Pair = Xform[p]; line[q.x, q.y]; lp ← p };
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 ← p3 };
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p1.x, p1.y, p1.x, p1.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair = Xform[p1]; q2: Pair = Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← p2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
Runs: PROC[
run: PROC[sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC[timesToRepeatScanline: NAT]] = {
data.devicePath.ConvertToRuns[runProc: run];
};
data.devicePath ← ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath];
mask ← ImagerMasks.Create[Runs];
};
MaskFromStroke: PROC [data: Data, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL]
RETURNS [mask: Mask] = {
Runs: PROC[
run: PROC[sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC[timesToRepeatScanline: NAT]] = {
data.devicePath.ConvertToRuns[runProc: run];
};
data.devicePath ← ImagerStroke.DevicePathFromStroke[
pathMap: pathMap,
pathData: pathData,
clientToDevice: data.transformation,
width: strokeWidth,
strokeEnd: strokeEnd,
closed: FALSE,
scratch: data.devicePath
];
mask ← ImagerMasks.Create[Runs];
};
MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ {
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
= {
m: Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
m.a * p.x + m.b * p.y + m.c,
m.d * p.x + m.e * p.y + m.f
]]};
{ q: Pair = Xform[[area.x, area.y]]; move[q.x, q.y] };
{ q: Pair = Xform[[area.x + area.w, area.y]]; line[q.x, q.y] };
{ q: Pair = Xform[[area.x + area.w, area.y + area.h]]; line[q.x, q.y] };
{ q: Pair = Xform[[area.x, area.y + area.h]]; line[q.x, q.y] };
};
Runs: PROC[
run: PROC[sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC[timesToRepeatScanline: NAT]] = {
data.devicePath.ConvertToRuns[runProc: run];
};
IF data.transformation.type # hard THEN {
p1: Pair ← ImagerTransform.Transform[[area.x, area.y], data.transformation];
p2: Pair ← ImagerTransform.Transform[[area.x+area.w, area.y+area.h], data.transformation];
sMin: INTEGER ← Real.RoundLI[MAX[MIN[p1.x, p2.x], -LAST[INTEGER]/2]];
sMax: INTEGER ← Real.RoundLI[MIN[MAX[p1.x, p2.x], LAST[INTEGER]/2]];
fMin: INTEGER ← Real.RoundLI[MAX[MIN[p1.y, p2.y], -LAST[INTEGER]/2]];
fMax: INTEGER ← Real.RoundLI[MIN[MAX[p1.y, p2.y], LAST[INTEGER]/2]];
RETURN [ImagerMasks.InlineBox[
[sMin: sMin, fMin: fMin, sSize: sMax-sMin, fSize: fMax-fMin]]];
};
data.devicePath ← ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath];
mask ← ImagerMasks.Create[Runs];
};
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]]
};
MaskFromPixelArray: PROC[data: Data, pixelArray: PixelArray] RETURNS[Mask] = {
destArea: Mask ← MaskFromRectangle[data, [0, 0, pixelArray.xPixels, pixelArray.yPixels]];
Runs: PROC[
run: PROC[sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC[timesToRepeatScanline: NAT]] = {
buffer: PixelBuffer ← NEW[PixelBufferRep[destArea.fSize+1]];
DoRun: PROC[sMin, fMin: INTEGER, fSize: CARDINAL] = {
nextPixel: Pair ← ImagerTransform.InverseTransformVec[[0, 1], data.transformation];
start: Pair ← ImagerTransform.InverseTransform[[0.5+sMin, 0.5+fMin], data.transformation];
xStart: Scaled.Value ← ScaledFromReal[start.x];
yStart: Scaled.Value ← ScaledFromReal[start.y];
xDeltaPixel: Scaled.Value ← ScaledFromReal[nextPixel.x];
yDeltaPixel: Scaled.Value ← ScaledFromReal[nextPixel.y];
runStart: NAT ← 0;
fRel: NAT ← 0;
pixelArray.get[pixelArray, buffer, fSize, 0, xStart, yStart, xDeltaPixel, yDeltaPixel];
WHILE fRel < fSize DO
buffer[fSize] ← 0;
WHILE buffer[fRel] # 0 DO fRel ← fRel + 1 ENDLOOP;
IF fRel > runStart THEN {run[sMin, fMin + fRel, fRel - runStart]};
buffer[fSize] ← 1;
WHILE buffer[fRel] = 0 DO fRel ← fRel + 1 ENDLOOP;
runStart ← fRel;
ENDLOOP;
};
destArea.MapRuns[DoRun];
};
RETURN [ImagerMasks.Create[Runs]];
};
CompositeClipper: PROC [data: Data] RETURNS [Mask] ~ {
IF NOT data.compositeClipperValid THEN {
IF data.clientClipperPresent THEN {
IF data.clientClipperExclude THEN data.compositeClipper ← data.viewClipper.Difference[data.clientClipper]
ELSE data.compositeClipper ← data.viewClipper.And[data.clientClipper];
}
ELSE data.compositeClipper ← data.viewClipper;
data.compositeClipperValid ← TRUE;
};
RETURN [data.compositeClipper]
};
ccCount: INT ← 0;
ClipMask: PROC [data: Data, mask: Mask, exclude: BOOLEAN] ~ {
data.compositeClipperValid ← FALSE;
IF NOT data.clientClipperPresent THEN {
data.clientClipperPresent ← TRUE;
data.clientClipperExclude ← exclude;
data.clientClipper ← mask;
ccCount ← ccCount + 1;
}
ELSE IF exclude THEN {
IF data.clientClipperExclude THEN {
data.clientClipper ← data.clientClipper.Or[mask];
ccCount ← ccCount + 1;
}
ELSE {
data.clientClipper ← data.clientClipper.Difference[mask];
ccCount ← ccCount + 1;
};
}
ELSE {
IF data.clientClipperExclude THEN {
data.clientClipper ← mask.Difference[data.clientClipper];
ccCount ← ccCount + 1;
data.clientClipperExclude ← FALSE;
}
ELSE {
data.clientClipper ← data.clientClipper.And[mask];
ccCount ← ccCount + 1;
};
};
};
ClipOutline: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ← NARROW[context.data];
ClipMask[data, MaskFromPath[data, pathMap, pathData], FALSE];
};
ExcludeOutline: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ← NARROW[context.data];
ClipMask[data, MaskFromPath[data, pathMap, pathData], TRUE];
};
ClipRectangle: PROC [context: Context, x, y, w, h: REAL] ~ {
data: Data = NARROW[context.data];
ClipMask[data, MaskFromRectangle[data, [x, y, w, h]], FALSE];
};
ExcludeRectangle: PROC [context: Context, x, y, w, h: REAL] ~ {
data: Data = NARROW[context.data];
ClipMask[data, MaskFromRectangle[data, [x, y, w, h]], TRUE];
};
IntegerClipRectangle: PROC[context: Context, x, y, w, h: INTEGER] ~ {
ClipRectangle[context, x, y, w, h];
};
IntegerExcludeRectangle: PROC[context: Context, x, y, w, h: INTEGER] ~ {
data: Data = NARROW[context.data];
ClipMask[data, MaskFromRectangle[data, [x, y, w, h]], TRUE];
};
InkWell: TYPE ~ ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1];
InkWellGray: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] ~ {
i, j: NAT ← 0;
FOR i: NAT IN [0..16) DO
inkWell[i] ← [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
ENDLOOP;
WHILE gray>0 DO
inkWell[i][j] ← 0;
i ← i+1; IF i>=16 THEN {i←i-16; j ← j+1};
j ← j+3; IF j>=16 THEN j←j-16;
gray←gray-1;
ENDLOOP;
};
InkWellGray45: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] ~ {
i, j: INTEGER ← 0;
FOR i: NAT IN [0..16) DO
inkWell[i] ← [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
ENDLOOP;
WHILE gray>0 DO
inkWell[i][j] ← 0;
i ← i+1; IF i>=16 THEN {i←i-16; j ← j+1};
j ← j-1; IF j<0 THEN j←j+16;
gray←gray-1;
ENDLOOP;
};
InkWellGrayS: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] ~ {
i, j: INTEGER ← 0;
FOR i: NAT IN [0..16) DO
inkWell[i] ← [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
ENDLOOP;
WHILE gray>0 DO
inkWell[i][j] ← 0;
i ← i+3; IF i>=16 THEN {i←i-16; j ← j-1};
IF j<0 THEN j←j+16;
gray←gray-1;
ENDLOOP;
};
ConstructGray: PROC [data: Data, toner: Toner, index: [0..64)] ~ {
value: [0..256] ← trc[index];
inkWell: InkWell ← SELECT toner FROM
yellow => InkWellGrayS[value],
magenta => InkWellGray45[value],
ENDCASE => InkWellGray[value];
bitsPtr: LONG POINTER;
loadReference: PDFileWriter.LoadReference;
TRUSTED {bitsPtr ← @inkWell};
loadReference ← data.pdState.LoadContiguousColorTile[phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: bitsPtr];
data.grayTileRef[toner][index] ← loadReference;
};
SetColor: PROC[data: Data, color: Color] ~ {
IF color = data.currentColor THEN RETURN;
data.currentColor ← 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 => {
intensity: INT ← constantColor.Y;
index: [0..64) ← MAX[MIN[intensity/(INT[LAST[CARDINAL]]/63), 63], 0];
DoToner: PROC [toner: Toner] = {
IF data.grayTileRef[toner][index] = dummyLoadReference THEN ConstructGray[data, toner, index];
data.pdState.SetColorTile[toner, data.grayTileRef[toner][index]]
};
data.pdState.DoForEachToner[DoToner];
};
specialColor: SpecialColor => {
ERROR;
};
sampledColor: SampledColor => NULL;
ENDCASE => SetColor[data, Imager.black];
};
MaskStroke: PROC[context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd] ~ {
data: Data = NARROW[context.data];
mask: ImagerMasks.Mask ← MaskFromStroke[data, pathMap, pathData, strokeWidth, strokeEnd, FALSE];
ApplyMask[data, mask];
};
MaskStrokeClosed: PROC[context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL] = {
data: Data = NARROW[context.data];
mask: ImagerMasks.Mask ← MaskFromStroke[data, pathMap, pathData, strokeWidth, nil, TRUE];
ApplyMask[data, mask];
};
MaskVector: PROC[context: Context, x1, y1, x2, y2: REAL] ~ {
ERROR;
};
IntegerMaskVector: PROC[context: Context, x1, y1, x2, y2: INTEGER] ~ {
MaskVector[context, x1, y1, x2, y2];
};
ApplyMask: PROC [data: Data, mask: Mask] ~ {
clipper: Mask ← CompositeClipper[data];
WITH data.currentColor SELECT FROM
sampledColor: SampledColor => TRUSTED {
transform: Transformation ← sampledColor.m;
linePointer: LONG POINTER ← data.lineBuffer.refRep.pointer;
Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]};
SampledColorRun: PROC [sMin, fMin: INTEGER, fSize: NAT] = TRUSTED {
data.lineBuffer.sMin ← sMin;
data.lineBuffer.fMin ← fMin;
data.lineBuffer.sSize ← 1;
data.lineBuffer.fSize ← fSize;
ImagerHalftone.Halftone[data.lineBuffer, ImagerMasks.Box[[sMin: sMin, sSize: 1, fMin: fMin, fSize: fSize]], sampledColor.pa, transform, deviceBrick];
PDFileWriter.ColorSamples[data.pdState, black, sMin, fMin, 1, fSize, Line, opaque];
};
mask.MapClippedRuns[clipper, SampledColorRun];
};
ENDCASE => {
DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] ~ {
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
SendRun[sMin, fMin, fSize];
};
mask.MapClippedRuns[clipper, Run];
};
PDFileWriter.MaskRunGroup[data.pdState, DeliverRuns];
};
};
MaskFill: PROC[context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromPath[data, pathMap, pathData];
ApplyMask[data, mask];
};
MaskPixel: PROC [context: Context, pa: PixelArray] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromPixelArray[data, pa];
ApplyMask[data, mask];
};
scratchPixelArray: PixelArray ← NEW[PixelArrayRep];
scratchBitmapDesc: REF ImagerPixelMaps.PixelMapRep ← NEW[ImagerPixelMaps.PixelMapRep];
MaskBits: PROC [context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle] ~ {
ERROR;
};
MaskRectangle: PROC[context: Context, x, y, w, h: REAL] = {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, [x, y, w, h]];
ApplyMask[data, mask];
};
IntegerMaskRectangle: PROC[context: Context, x, y, w, h: INTEGER] = {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, [x, y, w, h]];
ApplyMask[data, mask];
};
StartUnderline: PROC[context: Context] = {
ERROR;
};
MaskUnderline: PROC[context: Context, dy, h: REAL] = {
ERROR;
};
IntegerMaskUnderline: PROC[context: Context, dy, h: INTEGER] = {
MaskUnderline[context, dy, h];
};
SetXY: PROC [context: Context, p: Pair] = {
data: Data = NARROW[context.data];
data.currentPosition ← ImagerTransform.Transform[p, data.transformation];
};
IntegerSetXY: PROC [context: Context, x, y: INTEGER] = {
SetXY[context, [x, y]];
};
SetXYRel: PROC [context: Context, v: Pair] = {
data: Data = NARROW[context.data];
delta: Pair = ImagerTransform.TransformVec[v, data.transformation];
data.currentPosition.x ← data.currentPosition.x + delta.x;
data.currentPosition.y ← data.currentPosition.y + delta.y;
};
IntegerSetXYRel: PROC [context: Context, x, y: INTEGER] = {
SetXYRel[context, [x, y]];
};
GetCP: PROC[context: Context] RETURNS[cp: Pair] ~ {
data: Data = NARROW[context.data];
cp ← ImagerTransform.InverseTransform[data.currentPosition, data.transformation];
};
GetCPRounded: PROC[context: Context] RETURNS[cp: Pair] ~ {
data: Data = NARROW[context.data];
cpr: Pair = [x: Real.RoundLI[data.currentPosition.x], y: Real.RoundLI[data.currentPosition.y]];
cp ← ImagerTransform.InverseTransform[cpr, data.transformation];
};
rasterToRunGroupStorageRatio: INT ← 1;
MaskAndRunCount: TYPE ~ RECORD [mask: Mask, nRuns: INT];
GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR]
RETURNS [ans: MaskAndRunCount] ~ {
nRuns: INT ← 0;
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];
nRuns ← nRuns + 1;
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
nRuns ← 0;
ans.mask ← ImagerMasks.Create[Runs];
ans.nRuns ← nRuns;
};
FontCompositeTransform: PROC [data: Data, font: FONT] RETURNS [t: Transformation] ~ {
t ← data.transformation;
t.c ← t.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, t];
};
CharRepresentation: TYPE = {null, runGroup, raster};
CharLoadInfo: TYPE = RECORD[
sWidth, fWidth: Scaled.Value,
loadRef: PDFileWriter.LoadReference,
representation: CharRepresentation,
mask: Mask,
loadRepSize: INT
];
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-mask.SMin, fMin - mask.FMin, fSize];
};
mask.MapRuns[Run];
};
loadReference ← pdState.LoadRunGroup[DeliverRuns];
stats.loadRunGroups ← stats.loadRunGroups + 1;
};
LoadBitmap: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ {
m: Mask ← mask.Bitmap;
WITH m.refRep SELECT FROM
b: REF ImagerPixelMaps.PixelMapRep => {
loadReference ← pdState.LoadContiguousSampleArray[sSize: mask.sSize, fSize: mask.fSize, bitsPtr: b.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 = 0C+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];
maskN: MaskAndRunCount ← GetCharMask[font, transform, char];
runGroupSize: INT ← maskN.nRuns * 2 + 2;
rasterSize: INT ← maskN.mask.sSize * INT[(maskN.mask.fSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], clientTransform];
loadInfo.loadRef ← dummyLoadReference;
loadInfo.mask ← maskN.mask;
SELECT TRUE FROM
maskN.mask.sSize = 0 OR maskN.mask.fSize = 0 => {
loadInfo.representation ← null;
loadInfo.loadRepSize ← 0;
};
maskN.mask.fSize > 32*Environment.bitsPerWord OR
runGroupSize*rasterToRunGroupStorageRatio < rasterSize => {
loadInfo.representation ← runGroup;
loadInfo.loadRepSize ← runGroupSize;
};
ENDCASE => {
loadInfo.representation ← raster;
loadInfo.loadRepSize ← rasterSize;
};
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];
clipper: Mask ← CompositeClipper[data];
transform: Transformation ← FontCompositeTransform[data, font];
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: font]];
sCurrent: Scaled.Value ← ScaledFromReal[data.currentPosition.x];
fCurrent: Scaled.Value ← ScaledFromReal[data.currentPosition.y];
DoChar: PROC[charCode: CARDINAL, charData: REF] = {
loadInfo: REF CharLoadInfo ← NARROW[charData];
sCP: INTEGER ← sCurrent.Round;
fCP: INTEGER ← fCurrent.Round;
mask: Mask ← loadInfo.mask.Shift[sCP, fCP];
visibility: Visibility ← mask.IsVisible[clipper];
IF visibility = visible THEN WITH data.currentColor SELECT FROM
sampled: SampledColor => visibility ← partlyVisible;
ENDCASE => NULL;
IF loadInfo.loadRef = dummyLoadReference AND visibility = visible THEN {
IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + 5 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, mask.SMin, mask.FMin];
stats.rasterChars ← stats.rasterChars + 1;
};
runGroup => {
IF loadInfo.loadRef = dummyLoadReference THEN ERROR;
data.pdState.MaskRunGroupRef[loadInfo.loadRef, mask.SMin, mask.FMin];
stats.runGroupChars ← stats.runGroupChars + 1;
};
null => NULL;
ENDCASE => ERROR;
};
partlyVisible => {
ApplyMask[data, mask];
stats.clippedChars ← stats.clippedChars + 1;
};
invisible => {stats.culledChars ← stats.culledChars + 1};
ENDCASE => ERROR;
sCurrent ← sCurrent.PLUS[loadInfo.sWidth];
fCurrent ← fCurrent.PLUS[loadInfo.fWidth];
};
ImagerFontCache.GetStringData[DoChar, data.fontCache, fontCode, characters, start, length];
data.currentPosition ← [x: sCurrent.Float, y: fCurrent.Float];
};
CorrectMask: PROC[context: Context] = {
ERROR;
};
CorrectSpace: PROC[context: Context, v: Pair] = {
ERROR;
};
SetCorrectMeasure: PROC[context: Context, v: Pair] = {
ERROR;
};
SetCorrectTolerance: PROC[context: Context, v: Pair] = {
ERROR;
};
Space: PROC[context: Context, x: REAL] = {
ERROR;
};
IntegerSpace: PROC[context: Context, x: INTEGER] = {
ERROR;
};
Correct: PROC[context: Context, body: PROC] = {
ERROR;
};
DrawBitmap: PROC [context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle] ~ {
ERROR;
};
MaskFromSurfaceRectangle: PROC [data: Data, box: IntRectangle] RETURNS [Mask] ~ {
deviceBox: IntRectangle ← ImagerTransform.TransformIntRectangle[box, data.surfaceToDevice];
RETURN [ImagerMasks.InlineBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]];
};
SetView: PROC [context: Context, box: IntRectangle, halftoneOrigin: IntPair ← [0, 0]] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromSurfaceRectangle[data, box];
deviceOrigin: IntPair ← ImagerTransform.IntTransform[[box.x, box.y], data.surfaceToDevice];
sOriginView: INTEGER ← deviceOrigin.x;
fOriginView: INTEGER ← deviceOrigin.y;
sShift: INTEGER ← sOriginView-data.sOriginView;
fShift: INTEGER ← fOriginView-data.fOriginView;
newHalftoneOrigin: IntPair ← ImagerTransform.IntTransform[halftoneOrigin, data.surfaceToDevice];
IF data.doSaveCount # 0 THEN ERROR; -- Can't change a view with DoSave in progress.
IF data.viewClipper = mask AND sShift = 0 AND fShift = 0 AND newHalftoneOrigin = [data.sOriginBrick, data.fOriginBrick] THEN RETURN;
data.compositeClipperValid ← FALSE;
data.compositeClipper.refRep ← NIL;
IF data.clientClipperPresent THEN {
data.clientClipper ← data.clientClipper.Shift[sShift, fShift];
ccCount ← ccCount + 1;
};
data.viewClipper ← mask;
data.currentPosition.x ← data.currentPosition.x + sShift;
data.currentPosition.y ← data.currentPosition.y + fShift;
data.transformation.c ← data.transformation.c + sShift;
data.transformation.f ← data.transformation.f + fShift;
data.viewToDevice.c ← data.viewToDevice.c + sShift;
data.viewToDevice.f ← data.viewToDevice.f + fShift;
data.sOriginView ← sOriginView;
data.fOriginView ← fOriginView;
[data.sOriginBrick, data.fOriginBrick] ← newHalftoneOrigin;
};
ClipView: PROC [context: Context, box: IntRectangle, exclude: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromSurfaceRectangle[data, box];
data.compositeClipperValid ← FALSE;
data.compositeClipper.refRep ← NIL;
data.viewClipper ←
IF exclude THEN data.viewClipper.Difference[mask]
ELSE data.viewClipper.And[mask];
};
MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ {
ERROR Imager.Error[$NotImplementedForThisDevice];
};
TestRectangle: PROC[context: Context, x, y, w, h: REAL] RETURNS[Visibility] = {
data: Data = NARROW[context.data];
mask: Mask = MaskFromRectangle[data, [x, y, w, h]];
RETURN[mask.IsVisible[CompositeClipper[data]]];
};
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]]
};
GetViewBounds: PROC[context: Context] RETURNS[IntRectangle] = {
data: Data = NARROW[context.data];
deviceToSurface: Transformation = ImagerTransform.Invert[data.surfaceToDevice];
RETURN[ImagerTransform.TransformIntRectangle[[data.viewClipper.sMin, data.viewClipper.fMin, data.viewClipper.sSize, data.viewClipper.fSize], deviceToSurface]]
};
NewPage: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.pdState.EndPage;
};
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];
};
LFDisplayClass: ImagerPrivate.Class ← NEW [ImagerPrivate.ClassRep ← [
deviceType: $PD,
Init: Init,
IGet: IGet,
ISet: ISet,
IGetReal: IGetReal,
ISetReal: ISetReal,
IGetInt: IGetInt,
ISetInt: ISetInt,
DoSave: DoSave,
DoSaveAll: DoSaveAll,
ConcatT: ConcatT,
TranslateT: TranslateT,
RotateT: RotateT,
ScaleT: ScaleT,
Scale2T: Scale2T,
Move: Move,
Trans: Trans,
SetXY: SetXY,
IntegerSetXY: IntegerSetXY,
SetXYRel: SetXYRel,
IntegerSetXYRel: IntegerSetXYRel,
GetCP: GetCP,
GetCPRounded: GetCPRounded,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskStrokeClosed: MaskStrokeClosed,
MaskVector: MaskVector,
IntegerMaskVector: IntegerMaskVector,
MaskRectangle: MaskRectangle,
IntegerMaskRectangle: IntegerMaskRectangle,
StartUnderline: StartUnderline,
MaskUnderline: MaskUnderline,
IntegerMaskUnderline: IntegerMaskUnderline,
MaskPixel: MaskPixel,
ClipOutline: ClipOutline,
ExcludeOutline: ExcludeOutline,
ClipRectangle: ClipRectangle,
ExcludeRectangle: ExcludeRectangle,
IntegerClipRectangle: IntegerClipRectangle,
IntegerExcludeRectangle: IntegerExcludeRectangle,
ShowChar: ShowChar,
ShowCharacters: ShowCharacters,
CorrectMask: CorrectMask,
CorrectSpace: CorrectSpace,
SetCorrectMeasure: SetCorrectMeasure,
SetCorrectTolerance: SetCorrectTolerance,
Space: Space,
IntegerSpace: IntegerSpace,
Correct: Correct,
Reset: Reset,
SetView: SetView,
ClipView: ClipView,
DrawBitmap: DrawBitmap,
MaskBits: MaskBits,
MoveSurfaceRectangle: MoveSurfaceRectangle,
TestRectangle: TestRectangle,
GetSurfaceBounds: GetSurfaceBounds,
GetViewBounds: GetViewBounds,
SpecialOp: SpecialOp
]];
ImagerPrivate.RegisterDevice[LFDisplayClass];
END.