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: BOOLEANTRUE;
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: REALSELECT 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: BOOLFALSE] = {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: BOOLEANTRUE;
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];
};
};