PDGraphicsImpl.mesa
Michael Plass, May 12, 1983 10:45 am
DIRECTORY
Environment,
PDGraphics,
IPImagerBasic,
IPScan,
PDFileWriter,
Real,
RealFns,
Rope,
XFontCache,
PressFontReader
;
PDGraphicsImpl:
CEDAR
PROGRAM
IMPORTS IPScan, PDFileWriter, Real, RealFns, Rope, XFontCache, PressFontReader
EXPORTS PDGraphics
= BEGIN
tryLeftoverMode: BOOLEAN ← TRUE;
tryBandSize: NAT ← 16;
Pair: TYPE = IPImagerBasic.Pair;
InkWell: TYPE = ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1];
Toner: TYPE = PDFileWriter.Toner;
Context: TYPE = PDGraphics.Context;
Transform: TYPE = PDGraphics.Transform;
PathItem: TYPE = PDGraphics.PathItem;
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;
};
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];
MakeGray:
PROC [context: Context, 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 ← context.pdState.LoadContiguousColorTile[phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: bitsPtr];
context.grayTileRef[toner][index] ← loadReference;
};
SetGray:
PUBLIC
PROC [context: Context, gray:
REAL] = {
index: [0..64) ← MAX[MIN[Real.RoundLI[gray*63], 63], 0];
SetValueForToner:
PROC [toner: Toner] = {
IF context.grayTileRef[toner][index] = dummyLoadReference THEN MakeGray[context, toner, index];
context.pdState.SetColorTile[toner, context.grayTileRef[toner][index]]
};
context.pdState.DoForEachToner[SetValueForToner];
};
ToRange: PROC [v: REAL] RETURNS [REAL] = {RETURN[MAX[MIN[v, 1.0], 0.0]]};
HSVToRGB:
PROC [h, s, v:
REAL]
RETURNS [r, g, b:
REAL] = {
hue: REAL ← ToRange[h];
saturation: REAL ← ToRange[s];
value: REAL ← ToRange[v];
ihue: INTEGER;
fhue,m,n,k: REAL;
hue ← hue*6;
ihue ← Real.FixI[hue]; --integer hue
fhue ← hue-ihue; --fractional hue
IF ihue=6 THEN ihue ← 0;
m ← value*(1-saturation);
n ← value*(1-(saturation*fhue));
k ← value*(1-(saturation*(1-fhue)));
SELECT ihue
FROM
0 => RETURN[value,k,m];
1 => RETURN[n,value,m];
2 => RETURN[m,value,k];
3 => RETURN[m,n,value];
4 => RETURN[k,m,value];
5 => RETURN[value,m,n];
ENDCASE => RETURN[0,0,0];
};
SetColor:
PUBLIC
PROC [context: Context, hue, saturation, value:
REAL] = {
Won't work for 4-color printers.
r, g, b: REAL;
SetValueForToner:
PROC [toner: Toner] = {
i:
REAL ←
SELECT toner
FROM
black => value,
cyan => r,
magenta => g,
yellow => b,
ENDCASE => ERROR;
index: [0..64) ← MAX[MIN[Real.RoundLI[i*63], 63], 0];
IF context.grayTileRef[toner][index] = dummyLoadReference THEN MakeGray[context, toner, index];
context.pdState.SetColorTile[toner, context.grayTileRef[toner][index]]
};
[r, g, b] ← HSVToRGB[hue, saturation, value];
context.pdState.DoForEachToner[SetValueForToner];
};
SetBlack:
PUBLIC
PROC [context: Context] = {
DoToner: PROC [toner: Toner] = {context.pdState.SetColorInk[toner]};
context.pdState.DoForEachToner[DoToner];
};
SetWhite:
PUBLIC
PROC [context: Context] = {
DoToner: PROC [toner: Toner] = {context.pdState.SetColorClear[toner]};
context.pdState.DoForEachToner[DoToner];
};
SetFunnyGray:
PUBLIC
PROC [context: Context, gray:
REAL, toner: Toner] = {
sSize: NAT ~ 4;
fSize: NAT ~ 9;
fSizeRoundedUpToWord: NAT ~ 16;
phase: NAT ~ 3;
bits: ARRAY [0..sSize) OF PACKED ARRAY [0..fSizeRoundedUpToWord) OF [0..1];
nLevels: NAT ~ sSize * fSize;
brick:
ARRAY [0..sSize)
OF
PACKED
ARRAY [0..fSizeRoundedUpToWord)
OF [0..nLevels] ~ [
[ 08, 11, 21, 24, 28, 33, 26, 22, 10 ,0,0,0,0,0,0,0],
[ 12, 17, 25, 34, 03, 05, 30, 27, 23 ,0,0,0,0,0,0,0],
[ 29, 35, 31, 15, 01, 02, 07, 36, 32 ,0,0,0,0,0,0,0],
[ 20, 18, 16, 14, 09, 04, 06, 13, 19 ,0,0,0,0,0,0,0]
];
level: [0..nLevels] ← MAX[MIN[Real.RoundLI[gray*nLevels], nLevels], 0];
bitsPtr: LONG POINTER;
loadReference: PDFileWriter.LoadReference;
FOR s:
NAT
IN [0..sSize)
DO
FOR f:
NAT
IN [0..fSizeRoundedUpToWord)
DO
bits[s][f] ← IF level >= brick[s][f] THEN 0 ELSE 1;
ENDLOOP;
ENDLOOP;
TRUSTED {bitsPtr ← @bits};
loadReference ← context.pdState.LoadContiguousColorTile[phase: phase, sMin: 0, fMin: 0, sSize: sSize, fSize: fSize, bitsPtr: bitsPtr];
context.pdState.SetColorTile[toner, loadReference];
};
NewHornetContext:
PUBLIC
PROC [fileName: Rope.
ROPE]
RETURNS [context: Context] = {
GenPageBounds:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
move[[0,0]];
line[[0, 3264]];
line[[4224, 3264]];
line[[4224, 0]];
};
context ← NEW[PDGraphics.ContextRec];
context.transform.a ← context.transform.d ← 0;
context.transform.b ← context.transform.c ← 384/72.0;
context.pdState ← PDFileWriter.Create[fileName: fileName, deviceCode: hornet, sResolution: 384, fResolution: 384, imageSSize: 3264, imageFSize: 4224, nColors: 1, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode];
context.devicePath ← IPScan.Allocate[];
context.devicePath.PushPath[GenPageBounds];
FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO context.grayTileRef[toner][i] ← dummyLoadReference ENDLOOP ENDLOOP;
context.fontCache ← XFontCache.Create[];
};
NewPuffinContext:
PUBLIC
PROC [fileName: Rope.
ROPE]
RETURNS [context: Context] = {
GenPageBounds:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
move[[0,0]];
line[[0, 4224]];
line[[3264, 4224]];
line[[3264, 0]];
};
context ← NEW[PDGraphics.ContextRec];
context.transform.d ← -(context.transform.a ← 384/72.0);
context.transform.f ← 4224;
context.transform.b ← context.transform.c ← 0;
context.pdState ← PDFileWriter.Create[fileName: fileName, deviceCode: puffin, sResolution: 384, fResolution: 384, imageSSize: 4224, imageFSize: 3264, nColors: 3, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode];
context.devicePath ← IPScan.Allocate[];
context.devicePath.PushPath[GenPageBounds];
FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO context.grayTileRef[toner][i] ← dummyLoadReference ENDLOOP ENDLOOP;
context.fontCache ← XFontCache.Create[];
};
TransformPoint:
PUBLIC
PROC [context: Context, p: Pair]
RETURNS [device: Pair] = {
device.x ← context.transform.a * p.x + context.transform.c * p.y + context.transform.e;
device.y ← context.transform.b * p.x + context.transform.d * p.y + context.transform.f;
};
TransformVector:
PUBLIC
PROC [context: Context, v: Pair]
RETURNS [device: Pair] = {
device.x ← context.transform.a * v.x + context.transform.c * v.y;
device.y ← context.transform.b * v.x + context.transform.d * v.y;
};
Concat:
PUBLIC
PROC [context: Context, transform: Transform] = {
new: Transform;
new.a ← transform.a * context.transform.a + transform.b * context.transform.c;
new.b ← transform.a * context.transform.b + transform.b * context.transform.d;
new.c ← transform.c * context.transform.a + transform.d * context.transform.c;
new.d ← transform.c * context.transform.b + transform.d * context.transform.d;
new.e ← transform.e * context.transform.a + transform.f * context.transform.c + context.transform.e;
new.f ← transform.e * context.transform.b + transform.f * context.transform.d + context.transform.f;
context.transform ← new;
};
Translate: PUBLIC PROC [context: Context, v: Pair] = {Concat[context, [e: v.x, f: v.y]]};
Scale: PUBLIC PROC [context: Context, s: REAL] = {Concat[context, [a: s, d: s]]};
Rotate: PUBLIC PROC [context: Context, degrees: REAL] = {s: REAL ~ RealFns.SinDeg[degrees]; c: REAL ~ RealFns.CosDeg[degrees]; Concat[context, [c, s, -s, c, 0, 0]]};
MoveTo: PUBLIC PROC [context: Context, x, y: REAL] = {context.path ← CONS[[move, TransformPoint[context, [x, y]]], context.path]};
LineTo: PUBLIC PROC [context: Context, x, y: REAL] = {context.path ← CONS[[knot, TransformPoint[context, [x, y]]], context.path]};
CurveTo:
PUBLIC
PROC [context: Context, x1, y1, x2, y2, x3, y3:
REAL] = {
context.path ← CONS[[control, TransformPoint[context, [x1, y1]]], context.path];
context.path ← CONS[[control, TransformPoint[context, [x2, y2]]], context.path];
context.path ← CONS[[knot, TransformPoint[context, [x3, y3]]], context.path]
};
Reverse:
PROC [list:
LIST
OF PathItem]
RETURNS [
LIST
OF PathItem] ~ {
new: LIST OF PathItem ← NIL;
WHILE list #
NIL
DO
current: LIST OF PathItem ← list;
list ← list.rest;
current.rest ← new;
new ← current;
ENDLOOP;
RETURN [new]
};
DrawArea:
PUBLIC
PROC [context: Context] = {
DeliverRuns:
PROC [SendRun:
PROC [sMin, fMin, fSize:
CARDINAL]] = {
BoxFromScanConverter:
PROC [x, y, w, h:
INTEGER] = {
FOR s:
INTEGER
IN [y..y+h)
DO
SendRun[sMin: s, fMin: x, fSize: w];
ENDLOOP;
};
context.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: 0, ymax: context.pdState.GetBounds.sMax];
};
ClipArea[context, FALSE];
context.pdState.MaskRunGroup[DeliverRuns];
context.devicePath.PopPath;
};
ClipArea:
PUBLIC
PROC [context: Context, exclude:
BOOLEAN] = {
path: LIST OF PathItem ← Reverse[context.path];
GenPath:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
WHILE path #
NIL
DO
SELECT path.first.pathItemType
FROM
move => move[path.first.pair];
knot => line[path.first.pair];
control => {
curve[path.first.pair, path.rest.first.pair, path.rest.rest.first.pair];
path ← path.rest.rest
};
ENDCASE => ERROR;
path ← path.rest;
ENDLOOP;
};
context.path ← NIL;
context.devicePath.PushPath[GenPath, exclude];
context.clippers ← context.clippers+1;
};
PopClipper:
PUBLIC
PROC [context: Context] = {
context.devicePath.PopPath;
context.clippers ← context.clippers-1;
};
CharRepresentation: TYPE = {runGroup, raster};
CharLoadInfo: TYPE = RECORD [loadRef: PDFileWriter.LoadReference, sMin, fMin, sMax, fMax: INTEGER, representation: CharRepresentation];
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;
};
Visibility: TYPE = {all, part, none};
Floor: PROC[r: REAL] RETURNS [i: INT] = {i ← Real.RoundLI[r]; WHILE i>r DO i←i-1 ENDLOOP};
Ceiling: PROC[r: REAL] RETURNS [i: INT] = {i ← Real.RoundLI[r]; WHILE i<r DO i←i+1 ENDLOOP};
BoxVisibility:
PROC [context: Context, sMin, sSize, fMin, fSize:
REAL]
RETURNS [Visibility] = {
pageSmax, pageFmax: CARDINAL;
[pageSmax, pageFmax] ← context.pdState.GetBounds;
IF sMin >= pageSmax OR sMin + sSize <= 0 OR fMin >= pageFmax OR fMin + fSize <= 0 THEN RETURN[none];
IF context.clippers = 0
THEN
RETURN [
IF sMin >= 0 AND sMin + sSize <= pageSmax AND fMin >= 0 AND fMin + fSize <= pageFmax THEN all
ELSE part
]
ELSE {
v: Visibility ← all;
xMin: INTEGER ← Floor[fMin];
xMax: INTEGER ← Ceiling[fMin+fSize];
yMin: INTEGER ← Floor[sMin];
yMax: INTEGER ← Ceiling[sMin+sSize];
curY: INTEGER ← yMin;
GenPath:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
move[[xMin, yMin]];
line[[xMax, yMin]];
line[[xMax, yMax]];
line[[xMin, yMax]];
};
CheckVisible:
PROC [x, y, w, h:
INTEGER] = {
IF x # xMin OR x + w # xMax THEN v ← part;
IF curY # y THEN v ← part;
curY ← curY + h;
};
context.devicePath.PushPath[GenPath];
context.devicePath.ScanConvert[proc: CheckVisible, ymin: yMin, ymax: yMax];
context.devicePath.PopPath;
IF curY = yMin THEN v ← none
ELSE IF curY # yMax THEN v ← part;
RETURN[v];
};
};
rasterToRunGroupStorageRatio: INT ← 1;
DrawChars:
PUBLIC
PROC [context: Context, font: PressFontReader.Font, map:
PROC[
PROC[
CHAR]]] = {
fontCache: XFontCache.FontCache ~ context.fontCache;
devicePath: IPScan.DevicePath ← NIL;
a: REAL ~ context.transform.a;
b: REAL ~ context.transform.b;
c: REAL ~ context.transform.c;
d: REAL ~ context.transform.d;
Loophole: PROC [font: PressFontReader.Font] RETURNS [REF] ~ TRUSTED {RETURN[LOOPHOLE[font]]};
f: REF ~ Loophole[font]; -- because font^ is READONLY
fontCode: XFontCache.FontCode ~ XFontCache.GetFontCode[a, b, c, d, f];
Load:
PROC [char:
CHAR] = {
XForm:
PROC [x, y:
REAL]
RETURNS [p: Pair] = {
p.x ← a * x + c * y;
p.y ← b * x + d * y;
};
GenPath:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
moveTo: PROCEDURE [x, y: REAL] = {move[XForm[x, y]]};
lineTo: PROCEDURE [x, y: REAL] = {line[XForm[x, y]]};
curveTo: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] = {curve[XForm[x1, y1], XForm[x2, y2], XForm[x3, y3]]};
drawArea: PROCEDURE = {};
TRUSTED{font.GetCharOutline[char, moveTo, lineTo, curveTo, drawArea]};
};
FindBB:
PROC [x, y, w, h:
INTEGER] = {
IF loadInfo.sMin > y THEN loadInfo.sMin ← y;
IF loadInfo.fMin > x THEN loadInfo.fMin ← x;
IF loadInfo.sMax < y+h THEN loadInfo.sMax ← y+h;
IF loadInfo.fMax < x+w THEN loadInfo.fMax ← x+w;
nRuns ← nRuns + 1;
};
DeliverRuns:
PROC [SendRun:
PROC [sMin, fMin, fSize:
CARDINAL]] = {
BoxFromScanConverter:
PROC [x, y, w, h:
INTEGER] = {
f: CARDINAL ~ x-loadInfo.fMin;
FOR s:
INTEGER
IN [y-loadInfo.sMin..y+h-loadInfo.sMin)
DO
SendRun[sMin: s, fMin: f, fSize: w];
ENDLOOP;
};
devicePath.ScanConvert[BoxFromScanConverter, loadInfo.sMin, loadInfo.sMax, TRUE];
};
loadInfo: REF CharLoadInfo ← NEW[CharLoadInfo];
nRuns: INT ← 0;
IF devicePath = NIL THEN devicePath ← IPScan.Allocate[];
devicePath.PushPath[GenPath];
loadInfo.sMin ← loadInfo.fMin ← LAST[INTEGER];
loadInfo.sMax ← loadInfo.sMax ← FIRST[INTEGER];
devicePath.ScanConvert[proc: FindBB, parityFill: TRUE];
IF loadInfo.sMin>loadInfo.sMax THEN loadInfo.sMin ← loadInfo.sMax ← loadInfo.fMin ← loadInfo.fMax ← 0;
IF loadInfo.fMax-loadInfo.fMin > 32*Environment.bitsPerWord
OR (nRuns * 2 + 2)*rasterToRunGroupStorageRatio <
INT[loadInfo.sMax-loadInfo.sMin] *
INT[(loadInfo.fMax-loadInfo.fMin+15)/16] + 2
THEN {
loadInfo.loadRef ← context.pdState.LoadRunGroup[DeliverRuns];
loadInfo.representation ← runGroup;
stats.loadRunGroups ← stats.loadRunGroups + 1;
}
ELSE {
lineBuf: PACKED ARRAY [0..32*Environment.bitsPerWord) OF [0..1] ← ALL[0];
curY: INT ← loadInfo.sMin;
DeliverLines:
PROC [
SendScanLine:
PROC [p:
LONG
POINTER]] = {
BoxFromScanConverter:
PROC [x, y, w, h:
INTEGER] = {
f: CARDINAL ~ x-loadInfo.fMin;
IF h # 1 THEN ERROR;
WHILE curY < y
DO
TRUSTED {SendScanLine[@lineBuf]};
lineBuf ← ALL[0];
curY ← curY + 1;
ENDLOOP;
FOR f: [0..32*Environment.bitsPerWord)
IN [x-loadInfo.fMin..x-loadInfo.fMin+w)
DO
lineBuf[f] ← 1;
ENDLOOP;
};
devicePath.ScanConvert[BoxFromScanConverter, loadInfo.sMin, loadInfo.sMax, TRUE];
WHILE curY < loadInfo.sMax
DO
TRUSTED {SendScanLine[@lineBuf]};
lineBuf ← ALL[0];
curY ← curY + 1;
ENDLOOP;
};
loadInfo.loadRef ← context.pdState.LoadSampleArray[loadInfo.sMax-loadInfo.sMin, loadInfo.fMax-loadInfo.fMin, DeliverLines];
loadInfo.representation ← raster;
stats.loadRasters ← stats.loadRasters + 1;
};
devicePath.PopPath;
fontCache.LoadCharData[fontCode, char, loadInfo];
};
DoChar:
PROC [char:
CHAR] = {
e: REAL ~ context.transform.e;
f: REAL ~ context.transform.f;
XForm:
PROC [x, y:
REAL]
RETURNS [p: Pair] = {
p.x ← a * x + c * y + e;
p.y ← b * x + d * y + f;
};
GenPath:
PROC [move:
PROC[Pair], line:
PROC[Pair], curve:
PROC[Pair, Pair, Pair]] = {
moveTo: PROCEDURE [x, y: REAL] = {move[XForm[x, y]]};
lineTo: PROCEDURE [x, y: REAL] = {line[XForm[x, y]]};
curveTo: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] = {curve[XForm[x1, y1], XForm[x2, y2], XForm[x3, y3]]};
drawArea: PROCEDURE = {};
TRUSTED{font.GetCharOutline[char, moveTo, lineTo, curveTo, drawArea]};
};
charInfo: PressFontReader.CharInfo ← Info[];
Info: PROC RETURNS [PressFontReader.CharInfo] = TRUSTED {RETURN[font.GetCharInfo[char]]};
visibility: Visibility;
loadInfo: REF CharLoadInfo;
loadInfo ← NARROW[fontCache.GetCharData[fontCode, char ! XFontCache.CacheMiss => {Load[char]; RETRY}]];
visibility ← BoxVisibility[context, context.transform.f+loadInfo.sMin, loadInfo.sMax-loadInfo.sMin, context.transform.e+loadInfo.fMin, loadInfo.fMax-loadInfo.fMin];
SELECT visibility FROM
all => {
sMin: CARDINAL ~ Real.RoundLI[context.transform.f] + loadInfo.sMin;
fMin: CARDINAL ~ Real.RoundLI[context.transform.e] + loadInfo.fMin;
IF loadInfo.sMin < loadInfo.sMax
AND loadInfo.fMin < loadInfo.fMax
THEN
SELECT loadInfo.representation
FROM
raster => {
context.pdState.MaskSamplesRef[loadInfo.loadRef, sMin, fMin];
stats.rasterChars ← stats.rasterChars + 1;
};
runGroup => {
context.pdState.MaskRunGroupRef[loadInfo.loadRef, sMin, fMin];
stats.runGroupChars ← stats.runGroupChars + 1;
};
ENDCASE => ERROR;
};
part => {
smin: REAL ~ MIN[MAX[context.transform.f + loadInfo.sMin, 0], LAST[NAT]];
smax: REAL ~ MIN[MAX[context.transform.f + loadInfo.sMax, 0], LAST[NAT]];
DeliverRuns:
PROC [SendRun:
PROC [sMin, fMin, fSize:
CARDINAL]] = {
BoxFromScanConverter:
PROC [x, y, w, h:
INTEGER] = {
f: CARDINAL ~ x;
FOR s:
INTEGER
IN [y..y+h)
DO
SendRun[sMin: s, fMin: f, fSize: w];
ENDLOOP;
};
context.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: Real.RoundLI[smin], ymax: Real.RoundLI[smax], parityFill: TRUE];
};
context.devicePath.PushPath[GenPath];
context.pdState.MaskRunGroup[DeliverRuns];
context.devicePath.PopPath;
stats.clippedChars ← stats.clippedChars + 1;
};
none => {stats.culledChars ← stats.culledChars + 1};
ENDCASE => ERROR;
Translate[context, [charInfo.widthX, charInfo.widthY]];
};
map[DoChar];
};
DrawRope:
PUBLIC
PROC [context: Context, font: PressFontReader.Font, rope: Rope.
ROPE] = {
Map:
PROC [Char:
PROC[
CHAR]] = {
Action: PROC [c: CHAR] RETURNS [quit: BOOL ← FALSE] = {Char[c]};
[] ← rope.Map[action: Action];
};
DrawChars[context, font, Map];
};
DrawChar:
PUBLIC PROC [context: Context, font: PressFontReader.Font, char:
CHAR] = {
MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]};
DrawChars[context, font, MapOneChar];
};
END.
This code is buggy:
avoidTrapezoids: BOOLEAN ← TRUE;
DrawConvexQuadrilateral: PUBLIC PROC [context: Context, p1, p2, p3, p4: Pair] = {
RunGroupQuadrilateral: PROC [p1, p2, p3, p4: Pair] = {
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
move[p1];
line[p2];
line[p3];
line[p4];
};
DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
FOR s: INTEGER IN [y..y+h) DO
SendRun[sMin: s, fMin: x, fSize: w];
ENDLOOP;
};
context.devicePath.ScanConvert[BoxFromScanConverter];
};
context.devicePath.PushPath[GenPath];
context.pdState.MaskRunGroup[DeliverRuns];
context.devicePath.PopPath;
};
Intersect: PROC [m1, m2: Pair, p: Pair] RETURNS [q: Pair] = {
deltay: REAL ~ m2.y - m1.y;
deltax: REAL ~ m2.x - m1.x;
q ← p;
IF deltay # 0 THEN q.x ← m1.x + deltax*(p.y - m1.y)/deltay;
};
DrawTrap: PROC [fmin1, fmin2, fmax1, fmax2: Pair] = {
smin: INT ~ Real.RoundLI[fmin1.y];
fminAtsmin: INT ~ Real.RoundLI[fmin1.x];
fmaxAtsmin: INT ~ Real.RoundLI[fmin2.x];
smax: INT ~ Real.RoundLI[fmax1.y];
fminAtsmax: INT ~ Real.RoundLI[fmax1.x];
fmaxAtsmax: INT ~ Real.RoundLI[fmax2.x];
IF smin >= smax THEN RETURN;
IF fminAtsmin = fmaxAtsmin AND fminAtsmax = fmaxAtsmax THEN RETURN;
IF fminAtsmin = fminAtsmax AND fmaxAtsmin = fmaxAtsmax THEN {
context.pdState.MaskRectangle[sMin: smin, fMin: fminAtsmin, sSize: smax - smin, fSize: fmaxAtsmin - fminAtsmin];
}
ELSE IF avoidTrapezoids THEN RunGroupQuadrilateral[fmin1, fmin2, fmax2, fmax1]
ELSE context.pdState.MaskTrapezoid[sMin: smin, sSize: smax-smin, fMin: fminAtsmin, fSize: fmaxAtsmin-fminAtsmin, fMinLast: fminAtsmax, fSizeLast: fmaxAtsmax-fminAtsmax];
};
p1 ← TransformPoint[context, p1];
p2 ← TransformPoint[context, p2];
p3 ← TransformPoint[context, p3];
p4 ← TransformPoint[context, p4];
IF p1.y > p2.y THEN {t: Pair ← p1; p1 ← p2; p2 ← t};
IF p1.y > p3.y THEN {t: Pair ← p1; p1 ← p3; p3 ← t};
IF p1.y > p4.y THEN {t: Pair ← p1; p1 ← p4; p4 ← t};
IF p2.y > p3.y THEN {t: Pair ← p2; p2 ← p3; p3 ← t};
IF p2.y > p4.y THEN {t: Pair ← p2; p2 ← p4; p4 ← t};
IF p3.y > p4.y THEN {t: Pair ← p3; p3 ← p4; p4 ← t};
IF context.clippers > 0
OR MIN[p1.x, p2.x, p3.x, p4.x] < 0
OR MAX[p1.x, p2.x, p3.x, p4.x] > context.pdState.GetBounds.fMax
OR MIN[p1.y, p2.y, p3.y, p4.y] < 0
OR MAX[p1.y, p2.y, p3.y, p4.y] > context.pdState.GetBounds.sMax THEN {
RunGroupQuadrilateral[p1, p2, p4, p3];
}
ELSE {
q2: Pair ← Intersect[p1, p3, p2];
q3: Pair ← Intersect[p2, p4, p3];
IF q2.x > p2.x THEN {t: Pair ← q2; q2 ← p2; p2 ← t};
IF q3.x > p3.x THEN {t: Pair ← q3; q3 ← p3; p3 ← t};
DrawTrap[p1, p1, q2, p2];
DrawTrap[q2, p2, q3, p3];
DrawTrap[q3, p3, p4, p4];
};
};