ImagerPDImpl.mesa
Michael Plass, July 15, 1983 10:01 am
DIRECTORY Environment, Imager, ImagerBasic, ImagerBrick, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPD, ImagerTransform, PDFileWriter, Real, Rope, RopeInline, Scaled, ScanConverter, UnifiedFonts;
ImagerPDImpl: CEDAR PROGRAM
IMPORTS ImagerFontCache, ImagerBrick, ImagerHalftone, ImagerMasks, ImagerTransform, PDFileWriter, Real, Rope, RopeInline, Scaled, ScanConverter
EXPORTS ImagerPD, Imager
SHARES UnifiedFonts
~ BEGIN OPEN Imager;
stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = {
loadRunGroups ← stats.loadRunGroups; stats.loadRunGroups ← 0;
loadRasters ← stats.loadRasters; stats.loadRasters ← 0;
rasterChars ← stats.rasterChars; stats.rasterChars ← 0;
runGroupChars ← stats.runGroupChars; stats.runGroupChars ← 0;
clippedChars ← stats.clippedChars; stats.clippedChars ← 0;
culledChars ← stats.culledChars; stats.culledChars ← 0;
};
ROPE: TYPE ~ Rope.ROPE;
FONT: TYPE ~ UnifiedFonts.FONT;
DeviceCode: TYPE ~ PDFileWriter.DeviceCode;
Mask: TYPE ~ ImagerMasks.Mask;
Toner: TYPE ~ PDFileWriter.Toner;
InkWell: TYPE ~ ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1];
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];
tryLeftoverMode: BOOLEANTRUE;
tryBandSize: NAT ← 16;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
currentColor: ImagerBasic.Color,
sampledColor: ImagerBasic.SampledColor,
currentFont: FONT,
currentClipper: Mask,
currentPosition: ImagerBasic.Pair,
in device space
transformation: Transformation,
viewToDevice: Transformation,
pdState: PDFileWriter.PDState,
devicePath: ScanConverter.DevicePath,
grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference,
fontCache: ImagerFontCache.FontCache,
lineBuffer: Mask,
deviceBrick: ImagerHalftone.DeviceBrick
];
Create: PUBLIC PROC [fileName: ROPE, deviceCode: DeviceCode, copies: CARDINAL ← 1] RETURNS [context: Context] ~ {
data: Data ~ NEW[DataRep];
context ← NEW[Imager.ContextRep];
context.data ← data;
context.procs ← procs;
data.currentColor ← black;
data.sampledColor ← NIL;
data.currentFont ← NIL;
data.devicePath ← ScanConverter.Allocate[];
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;
SELECT deviceCode FROM
hornet => {
scale: REAL ~ 384/72.0;
data.currentClipper ← ImagerMasks.Box[sMin: 0, fMin: 0, sSize: 3264, fSize: 4224];
data.transformation ← data.viewToDevice ← [scale, 0, 0, 0, scale, 0, hard];
data.pdState ← PDFileWriter.Create[fileName: fileName, deviceCode: hornet, sResolution: 384, fResolution: 384, imageSSize: 3264, imageFSize: 4224, nColors: 1, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode];
data.deviceBrick ← ImagerHalftone.MakeDeviceBrick[ImagerBrick.BuildBrick[14, 45, ImagerHalftone.DotScreen], 255];
};
puffin => {
scale: REAL ~ 384/72.0;
data.currentClipper ← ImagerMasks.Box[sMin: 0, fMin: 0, sSize: 4224, fSize: 3264];
data.transformation ← data.viewToDevice ← [0, -scale, 4224, scale, 0, 0, hard];
data.pdState ← PDFileWriter.Create[fileName: fileName, deviceCode: puffin, sResolution: 384, fResolution: 384, imageSSize: 4224, imageFSize: 3264, nColors: 3, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode];
data.deviceBrick ← ImagerHalftone.MakeDeviceBrick[ImagerBrick.BuildBrick[9, 60, ImagerHalftone.LineScreen], 255];
};
ENDCASE => ERROR;
data.lineBuffer ← ImagerMasks.NewBitmap[sMin: 0, fMin: 0, sSize: 1, fSize: data.pdState.GetBounds.fMax];
};
DoSaveAll: PUBLIC PROC [context: Context, action: PROC] ~ {
data: Data ← NARROW[context.data];
cp: Pair ← data.currentPosition;
DoSave[context, action];
data.currentPosition ← cp;
};
DoSave: PUBLIC PROC [context: Context, action: PROC] ~ {
data: Data ← NARROW[context.data];
color: Color ← data.currentColor;
font: Font ← data.currentFont;
clipper: Mask ← data.currentClipper;
transformation: Transformation ← data.transformation;
action[];
data.transformation ← transformation;
data.currentClipper ← clipper;
data.currentFont ← font;
IF data.currentColor # color THEN SetColor[context, color];
};
Flush: PUBLIC PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.pdState.EndPage;
};
Close: PUBLIC PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.pdState.EndPage;
data.pdState.Close;
};
TranslateT: PUBLIC PROC [context: Context, dx, dy: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreTranslate[dx, dy, data.transformation];
};
RotateT: PUBLIC PROC [context: Context, degrees: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreRotate[degrees, data.transformation];
};
ScaleT: PUBLIC PROC [context: Context, sx, sy: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreScale[sx, sy, data.transformation];
};
ConcatT: PUBLIC PROC [context: Context, transformation: Transformation] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.Concat[transformation, data.transformation];
};
IntTranslateT: PUBLIC PROC [context: Context, dx, dy: INTEGER] ~ {TranslateT[context, dx, dy]};
GetT: PUBLIC PROC [context: Context] RETURNS [Transformation] ~ {
data: Data ← NARROW[context.data];
RETURN [ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]]
};
SetT: PUBLIC PROC [context: Context, transformation: Transformation] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.Concat[transformation, data.viewToDevice];
};
MaskFromPath: PROC [data: Data, outline: Path] RETURNS [mask: Mask] ~ {
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
Xmove: PROC [p: Pair] ~ {move[Xform[p]]};
Xline: PROC [p: Pair] ~ {line[Xform[p]]};
Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]};
outline.generateProc[outline, Xmove, Xline, Xcurve];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize];
};
data.devicePath.Reset;
data.devicePath.PushPath[GenPath];
mask ← ImagerMasks.Create[Runs];
data.devicePath.Reset;
};
MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ {
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
move[Xform[[area.x, area.y]]];
line[Xform[[area.x + area.w, area.y]]];
line[Xform[[area.x + area.w, area.y + area.h]]];
line[Xform[[area.x, area.y + area.h]]];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize];
};
data.devicePath.Reset;
data.devicePath.PushPath[GenPath];
mask ← ImagerMasks.Create[Runs];
data.devicePath.Reset;
};
ClipPath: PUBLIC PROC [context: Context, outline: Path, exclude: BOOLEANFALSE] ~ {
data: Data ← NARROW[context.data];
BBoxPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
move[[data.currentClipper.FMin, data.currentClipper.SMin]];
line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin]];
line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin + data.currentClipper.SSize]];
line[[data.currentClipper.FMin, data.currentClipper.SMin + data.currentClipper.SSize]];
};
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
Xmove: PROC [p: Pair] ~ {move[Xform[p]]};
Xline: PROC [p: Pair] ~ {line[Xform[p]]};
Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]};
outline.generateProc[outline, Xmove, Xline, Xcurve];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize];
};
data.devicePath.Reset;
IF exclude THEN data.devicePath.PushPath[BBoxPath];
data.devicePath.PushPath[GenPath, exclude];
data.currentClipper ← ImagerMasks.Create[Runs].And[data.currentClipper];
data.devicePath.Reset;
};
ClipRectangle: PUBLIC PROC [context: Context, outline: Rectangle, exclude: BOOLEANFALSE] ~ {
data: Data ← NARROW[context.data];
BBoxPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
move[[data.currentClipper.FMin, data.currentClipper.SMin]];
line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin]];
line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin + data.currentClipper.SSize]];
line[[data.currentClipper.FMin, data.currentClipper.SMin + data.currentClipper.SSize]];
};
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
move[Xform[[outline.x, outline.y]]];
line[Xform[[outline.x + outline.w, outline.y]]];
line[Xform[[outline.x + outline.w, outline.y + outline.h]]];
line[Xform[[outline.x, outline.y + outline.h]]];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize];
};
data.devicePath.Reset;
IF exclude THEN data.devicePath.PushPath[BBoxPath];
data.devicePath.PushPath[GenPath, exclude];
data.currentClipper ← ImagerMasks.Create[Runs].And[data.currentClipper];
data.devicePath.Reset;
};
TestRectangle: PUBLIC PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, area];
RETURN [mask.IsVisible[data.currentClipper]];
};
ClipIntRectangle: PUBLIC PROC [context: Context, outline: IntRectangle, exclude: BOOLEANFALSE] ~ {
ClipRectangle[context, [outline.x, outline.y, outline.w, outline.h], exclude];
};
TestIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ {
RETURN [TestRectangle[context, [area.x, area.y, area.w, area.h]]];
};
DoWithoutClipping: PUBLIC PROC [context: Context, bounds: IntRectangle, callBack: PUBLIC PROC[Context]] ~ {
callBack[context];
};
SetFont: PUBLIC PROC [context: Context, font: Font] ~ {
data: Data ← NARROW[context.data];
data.currentFont ← font;
};
GetFont: PUBLIC PROC [context: Context] RETURNS [font: Font] ~ {
data: Data ← NARROW[context.data];
font ← data.currentFont;
};
black: PUBLIC Color ← MakeGray[0];
white: PUBLIC Color ← MakeGray[1];
Card: PROC [real: REAL] RETURNS [card: CARDINAL] ~ {
int: INT ← Real.RoundLI[real*LAST[CARDINAL]];
card ← MAX[MIN[int, LAST[CARDINAL]], 0];
};
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;
};
MakeGray: PUBLIC PROC [intensity: REAL] RETURNS [Color] ~ {
RETURN [NEW[ImagerBasic.CIEColorRep ← [x: Card[0.3101], y: Card[0.3163], Y: Card[intensity]]]];
};
GetColor: PUBLIC PROC [context: Context] RETURNS [color: Color] ~ {
data: Data ← NARROW[context.data];
color ← data.currentColor;
};
SetColor: PUBLIC PROC [context: Context, color: Color] ~ {
data: Data ← NARROW[context.data];
IF color = data.currentColor THEN RETURN;
data.sampledColor ← NIL;
data.currentColor ← color;
SELECT color FROM
black => {
DoToner: PROC [toner: Toner] = {data.pdState.SetColorInk[toner]};
data.pdState.DoForEachToner[DoToner];
};
white => {
DoToner: PROC [toner: Toner] = {data.pdState.SetColorClear[toner]};
data.pdState.DoForEachToner[DoToner];
};
ENDCASE => {
WITH color SELECT FROM
constantColor: ImagerBasic.CIEColor => {
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];
};
sampledColor: ImagerBasic.SampledColor => {
data.sampledColor ← sampledColor;
};
ENDCASE => SetColor[context, black];
};
};
MaskStroke: PUBLIC PROC [context: Context, path: Path, width: REAL, strokeEnds: StrokeEnds] ~ {
Not implemented yet
};
ApplyMask: PROC [data: Data, mask: Mask] ~ {
clipper: Mask ← data.currentClipper;
IF data.sampledColor = NIL THEN {
DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = {
Run: PROC [s, fMin: INTEGER, fSize: CARDINAL] = {
SendRun[s, fMin, fSize];
};
mask.MapClippedRuns[clipper, Run];
};
data.pdState.MaskRunGroup[DeliverRuns];
stats.clippedChars ← stats.clippedChars + 1;
}
ELSE TRUSTED {
transform: Transformation ← ImagerTransform.Concat[data.sampledColor.m, data.transformation];
linePointer: LONG POINTERNARROW[data.lineBuffer.refRep, REF ImagerMasks.MaskRep.bitmap].pointer;
Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]};
SampledColorRun: PROC [s, fMin: INTEGER, fSize: CARDINAL] = TRUSTED {
data.lineBuffer.sMin ← s;
data.lineBuffer.fMin ← fMin;
data.lineBuffer.sSize ← 1;
data.lineBuffer.fSize ← fSize;
ImagerHalftone.Halftone[data.lineBuffer, ImagerMasks.Box[sMin: s, sSize: 1, fMin: fMin, fSize: fSize], data.sampledColor.pa, transform, data.deviceBrick];
data.pdState.ColorSamples[black, s, fMin, 1, fSize, Line, opaque];
};
mask.MapClippedRuns[clipper, SampledColorRun];
};
};
MaskFill: PUBLIC PROC [context: Context, path: Path] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromPath[data, path];
ApplyMask[data, mask];
};
MaskPixel: PUBLIC PROC [context: Context, pixelArray: ImagerBasic.PixelArray] ~ {
Not implemented yet
};
MoveTo: PUBLIC PROC [context: Context, p: IntPair] ~ {
Not implemented yet
};
DrawTo: PUBLIC PROC [context: Context, p: IntPair] ~ {
Not implemented yet
};
MaskThinStroke: PUBLIC PROC [context: Context, path: Path] ~ {
Not implemented yet
};
MaskIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, [area.x, area.y, area.w, area.h]];
ApplyMask[data, mask];
};
SetCP: PUBLIC PROC [context: Context, cp: Pair] ~ {
data: Data ← NARROW[context.data];
data.currentPosition ← ImagerTransform.Transform[cp, data.transformation];
};
GetCP: PUBLIC PROC [context: Context] RETURNS [cp: Pair] ~ {
data: Data ← NARROW[context.data];
cp ← ImagerTransform.InverseTransform[data.currentPosition, data.transformation];
};
SetIntCP: PUBLIC PROC [context: Context, cp: IntPair] ~ {
SetCP[context, [cp.x, cp.y]];
};
GetIntCP: PUBLIC PROC [context: Context] RETURNS [cp: IntPair] ~ {
realCP: Pair ← GetCP[context];
cp.x ← Real.RoundLI[realCP.x];
cp.y ← Real.RoundLI[realCP.y];
};
rasterToRunGroupStorageRatio: INT ← 1;
CharRepresentation: TYPE = {null, runGroup, raster};
CharLoadInfo: TYPE = RECORD [loadRef: PDFileWriter.LoadReference, representation: CharRepresentation, sWidth, fWidth: Scaled.Value, 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 [s, fMin: INTEGER, fSize: CARDINAL] = {
SendRun[s-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 ImagerMasks.MaskRep.bitmap => {
loadReference ← pdState.LoadContiguousSampleArray[sSize: mask.sSize, fSize: mask.fSize, bitsPtr: b.pointer];
};
ENDCASE => ERROR;
stats.loadRasters ← stats.loadRasters + 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[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
Run: PROC [s, fMin: INTEGER, fSize: NAT] ~ {
run[s: s, fMin: fMin, fSize: fSize];
nRuns ← nRuns + 1;
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
nRuns ← 0;
ans.mask ← ImagerMasks.Create[Runs];
ans.nRuns ← nRuns;
};
MaskChars: PROC [context: Context, map: PROC[PROC[CHAR]]] ~ {
data: Data ← NARROW[context.data];
fontCache: ImagerFontCache.FontCache ← data.fontCache;
font: FONT ← data.currentFont;
transform: Transformation ← ImagerTransform.Concat[font.actualTransformation, data.transformation];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f, font.graphicsKey];
clipper: Mask ← data.currentClipper;
sCurrent: Scaled.Value ← Scaled.FromReal[data.currentPosition.x];
fCurrent: Scaled.Value ← Scaled.FromReal[data.currentPosition.y];
Load: PROC [char: CHAR] = {
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], data.transformation];
loadInfo.loadRef ← dummyLoadReference;
loadInfo.mask ← maskN.mask;
loadInfo.representation ← SELECT TRUE FROM
maskN.mask.SSize = 0 OR maskN.mask.FSize = 0 => null,
maskN.mask.FSize > 32*Environment.bitsPerWord => runGroup,
runGroupSize*rasterToRunGroupStorageRatio < rasterSize => runGroup,
ENDCASE => raster;
loadInfo.loadRepSize ← SELECT loadInfo.representation FROM
runGroup => runGroupSize,
raster => rasterSize,
null => 0,
ENDCASE => ERROR;
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
fontCache.LoadCharData[fontCode, char, loadInfo];
};
DoChar: PROC [char: CHAR] = {
sCP: INTEGER ← sCurrent.Round;
fCP: INTEGER ← fCurrent.Round;
loadInfo: REF CharLoadInfo ← LoadInfo[];
LoadInfo: PROC RETURNS [cli: REF CharLoadInfo] ~ INLINE {
cli ← NARROW[
fontCache.GetCharData[fontCode, char
! ImagerFontCache.CacheMiss => {Load[char]; RETRY}
]
];
};
mask: Mask ← loadInfo.mask;
bbox: Mask ← ImagerMasks.InlineBox[sMin: mask.SMin + sCP, fMin: mask.FMin + fCP, sSize: mask.SSize, fSize: mask.FSize];
visibility: Visibility ← bbox.IsVisible[clipper];
IF data.sampledColor # NIL AND visibility = visible THEN visibility ← partlyVisible;
IF loadInfo.loadRef = dummyLoadReference AND visibility = visible THEN {
IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + 5 THEN {
loadInfo.loadRef ← SELECT loadInfo.representation FROM
raster => LoadBitmap[data.pdState, loadInfo.mask],
runGroup => LoadRunGroup[data.pdState, loadInfo.mask],
ENDCASE => ERROR;
}
ELSE visibility ← partlyVisible;
};
SELECT visibility FROM
visible => {
SELECT loadInfo.representation FROM
raster => {
IF loadInfo.loadRef = dummyLoadReference THEN ERROR;
data.pdState.MaskSamplesRef[loadInfo.loadRef, bbox.SMin, bbox.FMin];
stats.rasterChars ← stats.rasterChars + 1;
};
runGroup => {
IF loadInfo.loadRef = dummyLoadReference THEN ERROR;
data.pdState.MaskRunGroupRef[loadInfo.loadRef, bbox.SMin, bbox.FMin];
stats.runGroupChars ← stats.runGroupChars + 1;
};
null => NULL;
ENDCASE => ERROR;
};
partlyVisible => {
ApplyMask[data, mask.Shift[sCP, fCP]];
stats.clippedChars ← stats.clippedChars + 1;
};
invisible => {stats.culledChars ← stats.culledChars + 1};
ENDCASE => ERROR;
sCurrent ← sCurrent.PLUS[loadInfo.sWidth];
fCurrent ← fCurrent.PLUS[loadInfo.fWidth];
};
map[DoChar];
data.currentPosition ← [x: sCurrent.Float, y: fCurrent.Float];
};
MaskChar: PUBLIC PROC [context: Context, char: CHAR] ~ {
MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]};
MaskChars[context, MapOneChar];
};
MaskCharacters: PUBLIC PROC [
context: Context,
characters: REF, -- may be a Rope.ROPE or a REF TEXT, for non-cedar probably a LONG STRING
start: INT ← 0,
length: INTLAST[INT]
] ~ {
WITH characters SELECT FROM
rope: ROPE => {
Map: PROC [Char: PROC[CHAR]] = {
Action: PROC [c: CHAR] RETURNS [quit: BOOLFALSE] = {Char[c]};
[] ← rope.Map[start: start, len: length, action: Action];
};
MaskChars[context, Map]
};
text: REF TEXT => {
size: INT ← text.length;
rem: INT ← RopeInline.NonNeg[size - RopeInline.NonNeg[start]];
Map: PROC [Char: PROC[CHAR]] = {
FOR i: INT IN [start..start+length) DO
Char[text[i]];
ENDLOOP;
};
IF length > rem THEN length ← rem;
MaskChars[context, Map];
};
ENDCASE => IF characters # NIL THEN ERROR;
};
END.