MODULE Gfx;
IMPORT
Math, GfxMatrix, GfxImages, GfxPaths, GfxFonts;
CONST
Version* = "Gfx 2.0/eos 25.05.2000";
Record* = 0; Fill* = 1; Clip* = 2; Stroke* = 3; EvenOdd* = 4;
InPath* = 5; InSubpath* = 6;
MaxDashPatSize* = 8;
NoJoin* = 0; MiterJoin* = 1; BevelJoin* = 2; RoundJoin* = 3;
NoCap* = 0; ButtCap* = 1; SquareCap* = 2; RoundCap* = 3;
fillColPat* = 0; strokeColPat* = 1; lineWidth* = 2; dashPat* = 3; capStyle* = 4; joinStyle* = 5; styleLimit* = 6;
flatness* = 7; font* = 8; ctm* = 9; clip* = 10;
strokeAttr* = {strokeColPat..styleLimit};
attr* = {fillColPat..font}; all* = attr + {ctm, clip};
TYPE
Context* = POINTER TO ContextDesc;
Color* = RECORD
r*, g*, b*, a*: INTEGER;
END;
Pattern* = POINTER TO PatternDesc;
PatternDesc* = RECORD
img*: GfxImages.Image;
px*, py*: REAL;
END;
JoinStyle* = SHORTINT;
CapStyle* = SHORTINT;
ClipArea* = POINTER TO ClipAreaDesc;
ClipAreaDesc* = RECORD END;
Methods* = POINTER TO MethodBlock;
MethodBlock* = RECORD
reset*: PROCEDURE (ctxt: Context);
resetCTM*: PROCEDURE (ctxt: Context);
setCTM*: PROCEDURE (ctxt: Context; VAR mat: GfxMatrix.Matrix);
translate*: PROCEDURE (ctxt: Context; dx, dy: REAL);
scale*: PROCEDURE (ctxt: Context; sx, sy: REAL);
rotate*: PROCEDURE (ctxt: Context; sin, cos: REAL);
concat*: PROCEDURE (ctxt: Context; VAR mat: GfxMatrix.Matrix);
resetClip*: PROCEDURE (ctxt: Context);
getClipRect*: PROCEDURE (ctxt: Context; VAR llx, lly, urx, ury: REAL);
getClip*: PROCEDURE (ctxt: Context): ClipArea;
setClip*: PROCEDURE (ctxt: Context; clip: ClipArea);
setStrokeColor*: PROCEDURE (ctxt: Context; color: Color);
setStrokePattern*: PROCEDURE (ctxt: Context; pat: Pattern);
setFillColor*: PROCEDURE (ctxt: Context; color: Color);
setFillPattern*: PROCEDURE (ctxt: Context; pat: Pattern);
setLineWidth*: PROCEDURE (ctxt: Context; width: REAL);
setDashPattern*: PROCEDURE (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
setCapStyle*: PROCEDURE (ctxt: Context; style: CapStyle);
setJoinStyle*: PROCEDURE (ctxt: Context; style: JoinStyle);
setStyleLimit*: PROCEDURE (ctxt: Context; limit: REAL);
setFlatness*: PROCEDURE (ctxt: Context; flatness: REAL);
setFont*: PROCEDURE (ctxt: Context; font: GfxFonts.Font);
getWidth*: PROCEDURE (ctxt: Context; VAR str: ARRAY OF CHAR; VAR dx, dy: REAL);
begin*: PROCEDURE (ctxt: Context; mode: SET);
end*: PROCEDURE (ctxt: Context);
enter*: PROCEDURE (ctxt: Context; x, y, dx, dy: REAL);
exit*: PROCEDURE (ctxt: Context; dx, dy: REAL);
close*: PROCEDURE (ctxt: Context);
line*: PROCEDURE (ctxt: Context; x, y: REAL);
arc*: PROCEDURE (ctxt: Context; x, y, x0, y0, x1, y1, x2, y2: REAL);
bezier*: PROCEDURE (ctxt: Context; x, y, x1, y1, x2, y2: REAL);
show*: PROCEDURE (ctxt: Context; x, y: REAL; VAR str: ARRAY OF CHAR);
flatten*: PROCEDURE (ctxt: Context);
outline*: PROCEDURE (ctxt: Context);
render*: PROCEDURE (ctxt: Context; mode: SET);
rect*: PROCEDURE (ctxt: Context; x0, y0, x1, y1: REAL);
ellipse*: PROCEDURE (ctxt: Context; x, y, rx, ry: REAL);
image*: PROCEDURE (ctxt: Context; x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
newPattern*: PROCEDURE (ctxt: Context; img: GfxImages.Image; px, py: REAL): Pattern;
END;
ContextDesc* = RECORD
do*: Methods;
mode*: SET;
path*: GfxPaths.Path;
cpx*, cpy*: REAL;
ctm*: GfxMatrix.Matrix;
cam*: GfxMatrix.Matrix;
strokeCol*, fillCol*: Color;
strokePat*, fillPat*: Pattern;
lineWidth*: REAL;
dashPatOn*, dashPatOff*: ARRAY MaxDashPatSize OF REAL;
dashPatLen*: LONGINT;
dashPhase*: REAL;
dashPeriod*: REAL;
capStyle*: CapStyle;
joinStyle*: JoinStyle;
styleLimit*: REAL;
flatness*: REAL;
font*: GfxFonts.Font;
dashPath: GfxPaths.Path;
tmpPath: GfxPaths.Path;
END;
State* = RECORD
saved: SET;
strokeCol, fillCol: Color; strokePat, fillPat: Pattern;
lineWidth: REAL;
dashPatOn, dashPatOff: ARRAY MaxDashPatSize OF REAL;
dashPatLen: LONGINT; dashPhase: REAL;
capStyle: CapStyle; joinStyle: JoinStyle; styleLimit: REAL;
flatness: REAL;
font: GfxFonts.Font;
ctm: GfxMatrix.Matrix;
clip: ClipArea;
END;
PathData = RECORD (GfxPaths.EnumData)
path: GfxPaths.Path;
END;
VAR
Black*, White*, Red*, Green*, Blue*, Cyan*, Magenta*, Yellow*, LGrey*, MGrey*, DGrey*: Color;
DefaultCap*: CapStyle;
DefaultJoin*: JoinStyle;
PROCEDURE Reset* (ctxt: Context);
BEGIN
ctxt.do.reset(ctxt)
END Reset;
PROCEDURE Init* (ctxt: Context);
BEGIN
ctxt.ctm := GfxMatrix.Identity; ctxt.cam := ctxt.ctm;
ctxt.strokeCol := Black; ctxt.strokePat := NIL;
ctxt.fillCol := Black; ctxt.fillPat := NIL;
ctxt.lineWidth := 1;
ctxt.dashPatLen := 0; ctxt.dashPhase := 0; ctxt.dashPeriod := 0;
ctxt.capStyle := DefaultCap; ctxt.joinStyle := DefaultJoin; ctxt.styleLimit := 5;
ctxt.mode := {};
ctxt.path := NIL;
ctxt.cpx := 0; ctxt.cpy := 0;
ctxt.flatness := 0.5;
ctxt.font := GfxFonts.Default;
NEW(ctxt.tmpPath); NEW(ctxt.dashPath)
END Init;
PROCEDURE Save* (ctxt: Context; elems: SET; VAR state: State);
VAR i: LONGINT;
BEGIN
state.saved := elems;
state.strokeCol := ctxt.strokeCol; state.strokePat := ctxt.strokePat;
state.fillCol := ctxt.fillCol; state.fillPat := ctxt.fillPat;
state.lineWidth := ctxt.lineWidth;
IF dashPat IN elems THEN
state.dashPatLen := ctxt.dashPatLen; state.dashPhase := ctxt.dashPhase;
i := 0;
WHILE i < ctxt.dashPatLen DO
state.dashPatOn[i] := ctxt.dashPatOn[i]; state.dashPatOff[i] := ctxt.dashPatOff[i]; INC(i)
END
END;
state.capStyle := ctxt.capStyle; state.joinStyle := ctxt.joinStyle; state.styleLimit := ctxt.styleLimit;
state.flatness := ctxt.flatness;
state.font := ctxt.font;
IF ctm IN elems THEN
state.ctm := ctxt.ctm
END;
IF clip IN elems THEN
state.clip := ctxt.do.getClip(ctxt)
END
END Save;
PROCEDURE Restore* (ctxt: Context; state: State);
VAR do: Methods;
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
do := ctxt.do;
IF strokeColPat IN state.saved THEN
do.setStrokeColor(ctxt, state.strokeCol);
do.setStrokePattern(ctxt, state.strokePat)
END;
IF fillColPat IN state.saved THEN
do.setFillColor(ctxt, state.fillCol);
do.setFillPattern(ctxt, state.fillPat)
END;
IF lineWidth IN state.saved THEN
do.setLineWidth(ctxt, state.lineWidth)
END;
IF dashPat IN state.saved THEN
do.setDashPattern(ctxt, state.dashPatOn, state.dashPatOff, state.dashPatLen, state.dashPhase)
END;
IF capStyle IN state.saved THEN
do.setCapStyle(ctxt, state.capStyle)
END;
IF joinStyle IN state.saved THEN
do.setJoinStyle(ctxt, state.joinStyle)
END;
IF styleLimit IN state.saved THEN
do.setStyleLimit(ctxt, state.styleLimit)
END;
IF flatness IN state.saved THEN
do.setFlatness(ctxt, state.flatness)
END;
IF font IN state.saved THEN
do.setFont(ctxt, state.font)
END;
IF ctm IN state.saved THEN
do.setCTM(ctxt, state.ctm)
END;
IF clip IN state.saved THEN
do.setClip(ctxt, state.clip)
END
END Restore;
PROCEDURE ResetCTM* (ctxt: Context);
BEGIN
ctxt.do.resetCTM(ctxt)
END ResetCTM;
PROCEDURE SetCTM* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
BEGIN
ctxt.do.setCTM(ctxt, mat)
END SetCTM;
PROCEDURE Translate* (ctxt: Context; dx, dy: REAL);
BEGIN
ctxt.do.translate(ctxt, dx, dy)
END Translate;
PROCEDURE Scale* (ctxt: Context; sx, sy: REAL);
BEGIN
ctxt.do.scale(ctxt, sx, sy)
END Scale;
PROCEDURE ScaleAt* (ctxt: Context; sx, sy, x, y: REAL);
BEGIN
ctxt.do.translate(ctxt, x, y);
ctxt.do.scale(ctxt, sx, sy);
ctxt.do.translate(ctxt, -x, -y)
END ScaleAt;
PROCEDURE Rotate* (ctxt: Context; sin, cos: REAL);
BEGIN
ctxt.do.rotate(ctxt, sin, cos)
END Rotate;
PROCEDURE RotateAt* (ctxt: Context; sin, cos, x, y: REAL);
BEGIN
ctxt.do.translate(ctxt, x, y);
ctxt.do.rotate(ctxt, sin, cos);
ctxt.do.translate(ctxt, -x, -y)
END RotateAt;
PROCEDURE Concat* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
BEGIN
ctxt.do.concat(ctxt, mat)
END Concat;
PROCEDURE ResetClip* (ctxt: Context);
BEGIN
ctxt.do.resetClip(ctxt)
END ResetClip;
PROCEDURE GetClipRect* (ctxt: Context; VAR llx, lly, urx, ury: REAL);
BEGIN
ctxt.do.getClipRect(ctxt, llx, lly, urx, ury)
END GetClipRect;
PROCEDURE GetClip* (ctxt: Context): ClipArea;
BEGIN
RETURN ctxt.do.getClip(ctxt)
END GetClip;
PROCEDURE SetClip* (ctxt: Context; clip: ClipArea);
BEGIN
ctxt.do.setClip(ctxt, clip)
END SetClip;
PROCEDURE SetStrokeColor* (ctxt: Context; color: Color);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.setStrokeColor(ctxt, color)
END SetStrokeColor;
PROCEDURE SetStrokePattern* (ctxt: Context; pat: Pattern);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.setStrokePattern(ctxt, pat)
END SetStrokePattern;
PROCEDURE SetFillColor* (ctxt: Context; color: Color);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.setFillColor(ctxt, color)
END SetFillColor;
PROCEDURE SetFillPattern* (ctxt: Context; pat: Pattern);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.setFillPattern(ctxt, pat)
END SetFillPattern;
PROCEDURE SetLineWidth* (ctxt: Context; width: REAL);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT(width >= 0.0, 101);
ctxt.do.setLineWidth(ctxt, width)
END SetLineWidth;
PROCEDURE SetDashPattern* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT((len <= LEN(on)) & (len <= LEN(off)), 101);
ctxt.do.setDashPattern(ctxt, on, off, len, phase)
END SetDashPattern;
PROCEDURE SetDashArray* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT);
BEGIN
ctxt.dashPatLen := len;
ctxt.dashPeriod := 0;
IF len > 0 THEN
REPEAT
DEC(len);
ctxt.dashPatOn[len] := on[len]; ctxt.dashPatOff[len] := off[len];
ctxt.dashPeriod := ctxt.dashPeriod + on[len] + off[len]
UNTIL len = 0
END;
ASSERT((ctxt.dashPatLen = 0) OR (ctxt.dashPeriod # 0), 120)
END SetDashArray;
PROCEDURE SetCapStyle* (ctxt: Context; style: CapStyle);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT((NoCap <= style) & (style <= RoundCap), 101);
ctxt.do.setCapStyle(ctxt, style)
END SetCapStyle;
PROCEDURE SetJoinStyle* (ctxt: Context; style: JoinStyle);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT((NoJoin <= style) & (style <= RoundJoin), 101);
ctxt.do.setJoinStyle(ctxt, style)
END SetJoinStyle;
PROCEDURE SetStyleLimit* (ctxt: Context; limit: REAL);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.setStyleLimit(ctxt, limit)
END SetStyleLimit;
PROCEDURE SetFlatness* (ctxt: Context; flatness: REAL);
BEGIN
ctxt.do.setFlatness(ctxt, flatness)
END SetFlatness;
PROCEDURE SetFont* (ctxt: Context; font: GfxFonts.Font);
BEGIN
ASSERT(font # NIL, 100);
ctxt.do.setFont(ctxt, font)
END SetFont;
PROCEDURE SetFontName* (ctxt: Context; fontname: ARRAY OF CHAR; size: INTEGER);
VAR font: GfxFonts.Font;
BEGIN
font := GfxFonts.OpenSize(fontname, size);
IF font = NIL THEN font := GfxFonts.Default END;
ctxt.do.setFont(ctxt, font)
END SetFontName;
PROCEDURE GetStringWidth* (ctxt: Context; str: ARRAY OF CHAR; VAR dx, dy: REAL);
BEGIN
ctxt.do.getWidth(ctxt, str, dx, dy)
END GetStringWidth;
PROCEDURE Begin* (ctxt: Context; mode: SET);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, mode);
INCL(ctxt.mode, InPath)
END Begin;
PROCEDURE End* (ctxt: Context);
BEGIN
ASSERT(InPath IN ctxt.mode, 100);
IF InSubpath IN ctxt.mode THEN ctxt.do.exit(ctxt, 0, 0) END;
ctxt.do.end(ctxt);
EXCL(ctxt.mode, InPath)
END End;
PROCEDURE MoveTo* (ctxt: Context; x, y: REAL);
BEGIN
ASSERT(InPath IN ctxt.mode, 100);
IF InSubpath IN ctxt.mode THEN ctxt.do.exit(ctxt, 0, 0) END;
ctxt.do.enter(ctxt, x, y, 0, 0);
INCL(ctxt.mode, InSubpath)
END MoveTo;
PROCEDURE Enter* (ctxt: Context; x, y, dx, dy: REAL);
BEGIN
ASSERT(InPath IN ctxt.mode, 100);
IF InSubpath IN ctxt.mode THEN ctxt.do.exit(ctxt, 0, 0) END;
ctxt.do.enter(ctxt, x, y, dx, dy);
INCL(ctxt.mode, InSubpath)
END Enter;
PROCEDURE Exit* (ctxt: Context; dx, dy: REAL);
BEGIN
ASSERT(InSubpath IN ctxt.mode, 100);
ctxt.do.exit(ctxt, dx, dy);
EXCL(ctxt.mode, InSubpath)
END Exit;
PROCEDURE Close* (ctxt: Context);
BEGIN
ASSERT(InSubpath IN ctxt.mode, 100);
ctxt.do.close(ctxt);
EXCL(ctxt.mode, InSubpath)
END Close;
PROCEDURE LineTo* (ctxt: Context; x, y: REAL);
BEGIN
ASSERT(InSubpath IN ctxt.mode, 100);
ctxt.do.line(ctxt, x, y)
END LineTo;
PROCEDURE ArcTo* (ctxt: Context; x, y, x0, y0, x1, y1, x2, y2: REAL);
BEGIN
ASSERT(InSubpath IN ctxt.mode, 100);
ctxt.do.arc(ctxt, x, y, x0, y0, x1, y1, x2, y2)
END ArcTo;
PROCEDURE BezierTo* (ctxt: Context; x, y, x1, y1, x2, y2: REAL);
BEGIN
ASSERT(InSubpath IN ctxt.mode, 100);
ctxt.do.bezier(ctxt, x, y, x1, y1, x2, y2)
END BezierTo;
PROCEDURE ShowAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
BEGIN
ASSERT(InPath IN ctxt.mode, 100);
IF InSubpath IN ctxt.mode THEN ctxt.do.exit(ctxt, 0, 0) END;
ctxt.do.show(ctxt, x, y, str)
END ShowAt;
PROCEDURE Show* (ctxt: Context; str: ARRAY OF CHAR);
BEGIN
ASSERT(InPath IN ctxt.mode, 100);
IF InSubpath IN ctxt.mode THEN ctxt.do.exit(ctxt, 0, 0) END;
ctxt.do.show(ctxt, ctxt.cpx, ctxt.cpy, str)
END Show;
PROCEDURE Flatten* (ctxt: Context);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.flatten(ctxt)
END Flatten;
PROCEDURE EnumPathElem (VAR data: GfxPaths.EnumData);
BEGIN
WITH data: PathData DO
CASE data.elem OF
| GfxPaths.Enter: GfxPaths.AddEnter(data.path, data.x, data.y, data.dx, data.dy)
| GfxPaths.Line: GfxPaths.AddLine(data.path, data.x, data.y)
| GfxPaths.Exit: GfxPaths.AddExit(data.path, data.dx, data.dy)
END
END
END EnumPathElem;
PROCEDURE GetFlattenedPath* (ctxt: Context; path: GfxPaths.Path);
VAR data: PathData;
BEGIN
ASSERT(ctxt.path # path, 100);
GfxPaths.Clear(path);
data.path := path;
GfxPaths.EnumFlattened(ctxt.path, ctxt.flatness, EnumPathElem, data)
END GetFlattenedPath;
PROCEDURE EnterCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
BEGIN
IF ctxt.capStyle = ButtCap THEN
GfxPaths.AddEnter(path, x, y, dy, -dx);
GfxPaths.AddLine(path, x + dy, y - dx)
ELSIF ctxt.capStyle = SquareCap THEN
GfxPaths.AddEnter(path, x - dx, y - dy, dy, -dx);
GfxPaths.AddLine(path, x - dx + dy, y - dy - dx);
GfxPaths.AddLine(path, x + dy, y - dx)
ELSIF ctxt.capStyle = RoundCap THEN
GfxPaths.AddEnter(path, x - dx, y - dy, dy, -dx);
GfxPaths.AddArc(path, x + dy, y - dx, x, y, x - dx, y - dy, x + dy, y - dx)
ELSE
GfxPaths.AddEnter(path, x + dy, y - dx, 0, 0)
END
END EnterCapStyle;
PROCEDURE AddCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
BEGIN
IF ctxt.capStyle = ButtCap THEN
GfxPaths.AddLine(path, x + dy, y - dx)
ELSIF ctxt.capStyle = SquareCap THEN
GfxPaths.AddLine(path, x - dx - dy, y - dy + dx);
GfxPaths.AddLine(path, x - dx + dy, y - dy - dx);
GfxPaths.AddLine(path, x + dy, y - dx)
ELSIF ctxt.capStyle = RoundCap THEN
GfxPaths.AddArc(path, x + dy, y - dx, x, y, x - dy, y + dx, x - dx, y - dy)
ELSE
GfxPaths.AddExit(path, 0, 0);
GfxPaths.AddEnter(path, x + dy, y - dx, 0, 0)
END
END AddCapStyle;
PROCEDURE ExitCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
BEGIN
IF ctxt.capStyle = ButtCap THEN
GfxPaths.AddLine(path, x, y);
GfxPaths.AddExit(path, dy, -dx)
ELSIF ctxt.capStyle = SquareCap THEN
GfxPaths.AddLine(path, x - dx - dy, y - dy + dx);
GfxPaths.AddLine(path, x - dx, y - dy);
GfxPaths.AddExit(path, dy, -dx)
ELSIF ctxt.capStyle = RoundCap THEN
GfxPaths.AddArc(path, x - dx, y - dy, x, y, x - dy, y + dx, x - dx, y - dy);
GfxPaths.AddExit(path, dy, -dx)
ELSE
GfxPaths.AddExit(path, 0, 0)
END
END ExitCapStyle;
PROCEDURE ExceedsLimit* (ctxt: Context; hx, hy: REAL): BOOLEAN;
VAR limit: REAL;
BEGIN
GfxMatrix.ApplyToDist(ctxt.cam, 0.5*ctxt.lineWidth * ctxt.styleLimit, limit);
RETURN hx * hx + hy * hy > limit * limit
END ExceedsLimit;
PROCEDURE EnterJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
VAR ix, iy, t: REAL;
BEGIN
IF (ctxt.joinStyle = BevelJoin) OR (ctxt.joinStyle = MiterJoin) & ExceedsLimit(ctxt, hx, hy) THEN
GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
GfxPaths.AddEnter(path, ix, iy, -hy, hx);
GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = MiterJoin THEN
GfxPaths.AddEnter(path, x + hx, y + hy, idx, idy);
GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = RoundJoin THEN
t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
GfxPaths.AddEnter(path, x + t * hx, y + t * hy, -hy, hx);
GfxPaths.AddArc(path, x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
ELSE
GfxPaths.AddEnter(path, x + ody, y - odx, 0, 0)
END
END EnterJoinStyle;
PROCEDURE AddJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
BEGIN
IF (ctxt.joinStyle = BevelJoin) OR (ctxt.joinStyle = MiterJoin) & ExceedsLimit(ctxt, hx, hy) THEN
GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = MiterJoin THEN
GfxPaths.AddLine(path, x + hx, y + hy);
GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = RoundJoin THEN
GfxPaths.AddArc(path, x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
ELSE
GfxPaths.AddExit(path, 0, 0);
GfxPaths.AddEnter(path, x + ody, y - odx, 0, 0)
END
END AddJoinStyle;
PROCEDURE ExitJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
VAR ix, iy, t: REAL;
BEGIN
IF (ctxt.joinStyle = BevelJoin) OR (ctxt.joinStyle = MiterJoin) & ExceedsLimit(ctxt, hx, hy) THEN
GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
GfxPaths.AddLine(path, ix, iy);
GfxPaths.AddExit(path, -hy, hx)
ELSIF ctxt.joinStyle = MiterJoin THEN
GfxPaths.AddLine(path, x + hx, y + hy);
GfxPaths.AddExit(path, odx, ody)
ELSIF ctxt.joinStyle = RoundJoin THEN
t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
GfxPaths.AddArc(path, x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx);
GfxPaths.AddExit(path, -hy, hx)
ELSE
GfxPaths.AddExit(path, 0, 0)
END
END ExitJoinStyle;
PROCEDURE Outline* (ctxt: Context);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.outline(ctxt)
END Outline;
PROCEDURE GetNormVector* (x, y, len: REAL; VAR nx, ny: REAL);
VAR t: REAL;
BEGIN
t := len/Math.sqrt(x * x + y * y);
nx := t * x; ny := t * y
END GetNormVector;
PROCEDURE GetHalfAxisVector* (idx, idy, odx, ody: REAL; VAR hx, hy: REAL);
VAR cprod, t: REAL;
BEGIN
cprod := idx * ody - idy * odx;
IF ABS(cprod) < 1.0E-3 THEN
hx := 0; hy := 0
ELSE
t := ((idy - ody) * ody + (idx - odx) * odx)/cprod;
IF cprod > 0 THEN
hx := idy - t * idx; hy := -(idx + t * idy)
ELSE
hx := t * idx - idy; hy := idx + t * idy
END
END
END GetHalfAxisVector;
PROCEDURE AddEnterJoinStyle (ctxt: Context; x, y, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
VAR ix, iy, t: REAL;
BEGIN
IF (ctxt.joinStyle = BevelJoin) OR (ctxt.joinStyle = MiterJoin) & ExceedsLimit(ctxt, hx, hy) THEN
GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
GfxPaths.AddLine(path, ix, iy); GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = MiterJoin THEN
GfxPaths.AddLine(path, x + hx, y + hy); GfxPaths.AddLine(path, x + ody, y - odx)
ELSIF ctxt.joinStyle = RoundJoin THEN
t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
GfxPaths.AddLine(path, x + t * hx, y + t * hy);
GfxPaths.AddArc(path, x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
ELSE
GfxPaths.AddLine(path, x + ody, y - odx)
END
END AddEnterJoinStyle;
PROCEDURE AddExitJoinStyle (ctxt: Context; x, y, idx, idy, hx, hy: REAL; path: GfxPaths.Path);
VAR ix, iy, t: REAL;
BEGIN
IF (ctxt.joinStyle = BevelJoin) OR (ctxt.joinStyle = MiterJoin) & ExceedsLimit(ctxt, hx, hy) THEN
GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
GfxPaths.AddLine(path, ix, iy)
ELSIF ctxt.joinStyle = MiterJoin THEN
GfxPaths.AddLine(path, x + hx, y + hy)
ELSIF ctxt.joinStyle = RoundJoin THEN
t := Math.sqrt((idx * idx + idy * idy)/(hx * hx + hy * hy));
GfxPaths.AddArc(path, x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx)
END;
GfxPaths.AddLine(path, x - hx, y - hy)
END AddExitJoinStyle;
PROCEDURE GetPolyOutline (ctxt: Context; VAR x, y: ARRAY OF REAL; n: LONGINT; dxi, dyi, dxo, dyo: REAL; dst: GfxPaths.Path);
VAR closed: BOOLEAN; width, odx, ody, idx, idy, hx, hy: REAL; i, j: LONGINT;
BEGIN
closed := (x[n] = x[0]) & (y[n] = y[0]);
GfxMatrix.ApplyToDist(ctxt.cam, 0.5*ctxt.lineWidth, width);
GetNormVector(x[1] - x[0], y[1] - y[0], width, odx, ody);
IF (dxi = 0) & (dyi = 0) THEN
EnterCapStyle(ctxt, x[0], y[0], odx, ody, dst)
ELSE
GetNormVector(dxi, dyi, width, idx, idy);
GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
IF (hx = 0) & (hy = 0) THEN
IF closed THEN
GfxPaths.AddEnter(dst, x[0] + ody, y[0] - odx, dxi, dyi)
ELSE
GfxPaths.AddEnter(dst, x[0] - ody, y[0] + odx, ody, -odx);
GfxPaths.AddLine(dst, x[0] + ody, y[0] - odx)
END
ELSIF idx * ody > idy * odx THEN
IF closed THEN
EnterJoinStyle(ctxt, x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
ELSE
GfxPaths.AddEnter(dst, x[0] - hx, y[0] - hy, ody, -odx);
AddEnterJoinStyle(ctxt, x[0], y[0], hx, hy, odx, ody, dst)
END
ELSE
IF closed THEN
GfxPaths.AddEnter(dst, x[0] - hx, y[0] - hy, dxi, dyi)
ELSE
GfxPaths.AddEnter(dst, x[0] + hx, y[0] + hy, -hx, -hy);
GfxPaths.AddLine(dst, x[0] - hx, y[0] - hy)
END
END
END;
i := 1; j := 2;
WHILE j <= n DO
idx := odx; idy := ody;
GetNormVector(x[j] - x[i], y[j] - y[i], width, odx, ody);
GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
IF (hx = 0) & (hy = 0) THEN
GfxPaths.AddLine(dst, x[i] + idy, y[i] - idx)
ELSIF idx * ody > idy * odx THEN
GfxPaths.AddLine(dst, x[i] + idy, y[i] - idx);
AddJoinStyle(ctxt, x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
ELSE
GfxPaths.AddLine(dst, x[i] - hx, y[i] - hy)
END;
i := j; INC(j)
END;
idx := odx; idy := ody;
IF (dxo = 0) & (dyo = 0) THEN
GfxPaths.AddLine(dst, x[n] + ody, y[n] - odx);
AddCapStyle(ctxt, x[n], y[n], -odx, -ody, dst)
ELSE
GfxPaths.AddLine(dst, x[n] + idy, y[n] - idx);
GetNormVector(dxo, dyo, width, odx, ody);
GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
IF (hx = 0) & (hy = 0) THEN
IF closed THEN
GfxPaths.AddExit(dst, odx, ody);
GfxPaths.AddEnter(dst, x[n] - idy, y[n] + idx, -dxo, -dyo)
ELSE
GfxPaths.AddLine(dst, x[n] - idy, y[n] + idx)
END
ELSIF idx * ody > idy * odx THEN
IF closed THEN
ExitJoinStyle(ctxt, x[n], y[n], idx, idy, hx, hy, odx, ody, dst);
GfxPaths.AddEnter(dst, x[n] - hx, y[n] - hy, -dxo, -dyo)
ELSE
AddExitJoinStyle(ctxt, x[n], y[n], idx, idy, hx, hy, dst)
END
ELSE
GfxPaths.AddLine(dst, x[n] - hx, y[n] - hy);
IF closed THEN
GfxPaths.AddExit(dst, dxo, dyo);
EnterJoinStyle(ctxt, x[n], y[n], -odx, -ody, -hx, -hy, -idx, -idy, dst)
ELSE
AddEnterJoinStyle(ctxt, x[n], y[n], -hx, -hy, -idx, -idy, dst)
END
END
END;
odx := -idx; ody := -idy;
i := n-1; j := n-2;
WHILE j >= 0 DO
idx := odx; idy := ody;
GetNormVector(x[j] - x[i], y[j] - y[i], width, odx, ody);
GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
IF (hx = 0) & (hy = 0) THEN
GfxPaths.AddLine(dst, x[i] + idy, y[i] - idx)
ELSIF idx * ody > idy * odx THEN
GfxPaths.AddLine(dst, x[i] + idy, y[i] - idx);
AddJoinStyle(ctxt, x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
ELSE
GfxPaths.AddLine(dst, x[i] - hx, y[i] - hy)
END;
i := j; DEC(j)
END;
IF (dxi = 0) & (dyi = 0) THEN
GfxPaths.AddLine(dst, x[0] + ody, y[0] - odx);
ExitCapStyle(ctxt, x[0], y[0], -odx, -ody, dst)
ELSE
idx := odx; idy := ody;
GetNormVector(-dxi, -dyi, width, odx, ody);
GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
GfxPaths.AddLine(dst, x[0] + idy, y[0] - idx);
IF (hx = 0) & (hy = 0) THEN
IF closed THEN
GfxPaths.AddExit(dst, -dxi, -dyi)
ELSE
GfxPaths.AddExit(dst, -idy, idx)
END
ELSIF idx * ody > idy * odx THEN
IF closed THEN
ExitJoinStyle(ctxt, x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
ELSE
AddExitJoinStyle(ctxt, x[0], y[0], idx, idy, hx, hy, dst);
GfxPaths.AddExit(dst, -idx, -idy)
END
ELSE
GfxPaths.AddLine(dst, x[0] - hx, y[0] - hy);
IF closed THEN
GfxPaths.AddExit(dst, -dxi, -dyi)
ELSE
GfxPaths.AddExit(dst, hx, hy)
END
END
END
END GetPolyOutline;
PROCEDURE GetStrokeOutline (ctxt: Context; VAR scan: GfxPaths.Scanner; dst: GfxPaths.Path);
CONST last = 127;
VAR x, y: ARRAY last+1 OF REAL; dxi, dyi, dxo, dyo: REAL; n: LONGINT;
BEGIN
ASSERT(scan.elem = GfxPaths.Enter);
x[0] := scan.x; y[0] := scan.y; dxi := scan.dx; dyi := scan.dy;
GfxPaths.Scan(scan); n := 0;
WHILE scan.elem = GfxPaths.Line DO
IF n < last THEN
INC(n); x[n] := scan.x; y[n] := scan.y
ELSE
dxo := scan.x - x[n]; dyo := scan.y - y[n];
GetPolyOutline(ctxt, x, y, n, dxi, dyi, dxo, dyo, dst);
dxi := x[n] - x[n-1]; dyi := y[n] - y[n-1];
x[0] := x[n]; y[0] := y[n];
x[1] := scan.x; y[1] := scan.y;
n := 1
END;
GfxPaths.Scan(scan)
END;
IF n > 0 THEN
GetPolyOutline(ctxt, x, y, n, dxi, dyi, scan.dx, scan.dy, dst)
END;
GfxPaths.Scan(scan)
END GetStrokeOutline;
PROCEDURE GetDashOffsets* (ctxt: Context; offset: REAL; VAR beg, end, next: REAL; VAR idx: LONGINT);
VAR phase, period, len: REAL;
BEGIN
idx := 0;
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPhase, phase);
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPeriod, period);
beg := ENTIER((phase + offset)/period) * period - phase;
LOOP
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPatOn[idx], len);
end := beg + len;
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPatOff[idx], len);
next := end + len;
idx := (idx+1) MOD ctxt.dashPatLen;
IF next > offset THEN EXIT END;
beg := next
END
END GetDashOffsets;
PROCEDURE GetDashOutline (ctxt: Context; VAR scan: GfxPaths.Scanner; dst: GfxPaths.Path);
VAR
width, cx, cy, dx, dy, beg, end, next, offset, len, cos, sin, wdx, wdy, endOff, dash, nx, ny: REAL;
index: LONGINT; dscan: GfxPaths.Scanner;
BEGIN
GfxMatrix.ApplyToDist(ctxt.cam, 0.5*ctxt.lineWidth, width);
ASSERT(scan.elem = GfxPaths.Enter);
cx := scan.x; cy := scan.y; dx := scan.dx; dy := scan.dy;
GfxPaths.Scan(scan);
GetDashOffsets(ctxt, 0, beg, end, next, index);
IF 0 < end THEN
IF width = 0 THEN
GfxPaths.AddEnter(dst, cx, cy, dx, dy)
ELSE
GfxPaths.Clear(ctxt.dashPath);
GfxPaths.AddEnter(ctxt.dashPath, cx, cy, dx, dy)
END
END;
offset := 0;
WHILE scan.elem = GfxPaths.Line DO
dx := scan.x - cx; dy := scan.y - cy;
len := Math.sqrt(dx * dx + dy * dy);
cos := dx/len; sin := dy/len;
endOff := offset + len;
IF offset < end THEN
IF end <= endOff THEN
len := end - offset;
IF width = 0 THEN
GfxPaths.AddLine(dst, cx + len * cos, cy + len * sin);
GfxPaths.AddExit(dst, 0, 0)
ELSE
GfxPaths.AddLine(ctxt.dashPath, cx + len * cos, cy + len * sin);
GfxPaths.AddExit(ctxt.dashPath, 0, 0);
GfxPaths.Open(dscan, ctxt.dashPath, 0);
GetStrokeOutline(ctxt, dscan, dst)
END
ELSIF width = 0 THEN
GfxPaths.AddLine(dst, scan.x, scan.y)
ELSE
GfxPaths.AddLine(ctxt.dashPath, scan.x, scan.y)
END
END;
IF next < endOff THEN
wdx := width * cos; wdy := width * sin;
beg := offset;
REPEAT
len := next - beg;
cx := cx + len * cos; cy := cy + len * sin;
beg := next;
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPatOn[index], dash);
end := beg + dash;
GfxMatrix.ApplyToDist(ctxt.cam, ctxt.dashPatOff[index], dash);
next := end + dash;
index := (index+1) MOD ctxt.dashPatLen;
IF end <= endOff THEN
len := end - beg;
nx := cx + len * cos; ny := cy + len * sin;
IF width = 0 THEN
GfxPaths.AddEnter(dst, cx, cy, 0, 0);
GfxPaths.AddLine(dst, nx, ny);
GfxPaths.AddExit(dst, 0, 0)
ELSE
EnterCapStyle(ctxt, cx, cy, wdx, wdy, dst);
GfxPaths.AddLine(dst, nx + wdy, ny - wdx);
AddCapStyle(ctxt, nx, ny, -wdx, -wdy, dst);
GfxPaths.AddLine(dst, cx - wdy, cy + wdx);
ExitCapStyle(ctxt, cx, cy, wdx, wdy, dst)
END
END
UNTIL next >= endOff;
IF endOff < end THEN
IF width = 0 THEN
GfxPaths.AddEnter(dst, cx, cy, 0, 0);
GfxPaths.AddLine(dst, scan.x, scan.y)
ELSE
GfxPaths.Clear(ctxt.dashPath);
GfxPaths.AddEnter(ctxt.dashPath, cx, cy, 0, 0);
GfxPaths.AddLine(ctxt.dashPath, scan.x, scan.y)
END
END
END;
cx := scan.x; cy := scan.y; offset := endOff;
GfxPaths.Scan(scan)
END;
ASSERT(scan.elem = GfxPaths.Exit);
IF offset < end THEN
IF width = 0 THEN
GfxPaths.AddExit(dst, scan.dx, scan.dy)
ELSE
GfxPaths.AddExit(ctxt.dashPath, scan.dx, scan.dy);
GfxPaths.Open(dscan, ctxt.dashPath, 0);
GetStrokeOutline(ctxt, dscan, dst)
END
END;
GfxPaths.Scan(scan)
END GetDashOutline;
PROCEDURE GetOutline* (ctxt: Context; dst: GfxPaths.Path);
VAR scan: GfxPaths.Scanner;
BEGIN
ASSERT(dst # ctxt.path, 100);
ctxt.do.flatten(ctxt);
GfxPaths.Clear(dst);
GfxPaths.Open(scan, ctxt.path, 0);
WHILE scan.elem = GfxPaths.Enter DO
IF ctxt.dashPatLen > 0 THEN
GetDashOutline(ctxt, scan, dst)
ELSE
GetStrokeOutline(ctxt, scan, dst)
END
END
END GetOutline;
PROCEDURE Render* (ctxt: Context; mode: SET);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
EXCL(mode, Record);
IF mode # {} THEN
ctxt.do.render(ctxt, mode)
END
END Render;
PROCEDURE DrawPath* (ctxt: Context; path: GfxPaths.Path; mode: SET);
VAR scan: GfxPaths.Scanner;
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
IF path = ctxt.path THEN
Render(ctxt, mode)
ELSE
ctxt.do.begin(ctxt, mode);
GfxPaths.Open(scan, path, 0);
WHILE scan.elem # GfxPaths.Stop DO
CASE scan.elem OF
| GfxPaths.Enter: ctxt.do.enter(ctxt, scan.x, scan.y, scan.dx, scan.dy)
| GfxPaths.Line: ctxt.do.line(ctxt, scan.x, scan.y)
| GfxPaths.Arc: ctxt.do.arc(ctxt, scan.x, scan.y, scan.x0, scan.y0, scan.x1, scan.y1, scan.x2, scan.y2)
| GfxPaths.Bezier: ctxt.do.bezier(ctxt, scan.x, scan.y, scan.x1, scan.y1, scan.x2, scan.y2)
| GfxPaths.Exit: ctxt.do.exit(ctxt, scan.dx, scan.dy)
END
END;
ctxt.do.end(ctxt)
END
END DrawPath;
PROCEDURE DrawLine* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
BEGIN
IF (x0=x1)&(y0=y1) THEN RETURN END;
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT(mode * {Fill, Clip, EvenOdd} = {}, 101);
ctxt.do.begin(ctxt, mode);
ctxt.do.enter(ctxt, x0, y0, 0, 0);
ctxt.do.line(ctxt, x1, y1);
ctxt.do.exit(ctxt, 0, 0);
ctxt.do.end(ctxt)
END DrawLine;
PROCEDURE DrawArc* (ctxt: Context; x, y, r, start, end: REAL; mode: SET);
VAR x1, y1, x2, y2: REAL;
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ASSERT(mode * {Fill, Clip, EvenOdd} = {}, 101);
IF r > 0 THEN x1 := x + r; y1 := y; x2 := x; y2 := y + r
ELSIF r < 0 THEN r := -r; x1 := x; y1 := y + r; x2 := x + r; y2 := y
ELSE RETURN
END;
ctxt.do.begin(ctxt, mode);
ctxt.do.enter(ctxt, x + r * Math.cos(start), y + r * Math.sin(start), 0, 0);
ctxt.do.arc(ctxt, x + r * Math.cos(end), y + r * Math.sin(end), x, y, x1, y1, x2, y2);
ctxt.do.end(ctxt)
END DrawArc;
PROCEDURE DrawRect* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, mode);
ctxt.do.rect(ctxt, x0, y0, x1, y1);
ctxt.do.end(ctxt)
END DrawRect;
PROCEDURE DrawCircle* (ctxt: Context; x, y, r: REAL; mode: SET);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, mode);
ctxt.do.ellipse(ctxt, x, y, r, ABS(r));
ctxt.do.end(ctxt)
END DrawCircle;
PROCEDURE DrawEllipse* (ctxt: Context; x, y, rx, ry: REAL; mode: SET);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, mode);
ctxt.do.ellipse(ctxt, x, y, rx, ry);
ctxt.do.end(ctxt)
END DrawEllipse;
PROCEDURE DrawStringAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, {Fill});
ctxt.do.show(ctxt, x, y, str);
ctxt.do.end(ctxt)
END DrawStringAt;
PROCEDURE DrawString* (ctxt: Context; str: ARRAY OF CHAR);
BEGIN
ASSERT(~(InPath IN ctxt.mode), 100);
ctxt.do.begin(ctxt, {Fill});
ctxt.do.show(ctxt, ctxt.cpx, ctxt.cpy, str);
ctxt.do.end(ctxt)
END DrawString;
PROCEDURE DrawImageAt* (ctxt: Context; x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
BEGIN
ctxt.do.image(ctxt, x, y, img, filter)
END DrawImageAt;
PROCEDURE NewPattern* (ctxt: Context; img: GfxImages.Image; px, py: REAL): Pattern;
BEGIN
RETURN ctxt.do.newPattern(ctxt, img, px, py)
END NewPattern;
PROCEDURE DefResetContext* (ctxt: Context);
BEGIN
Init(ctxt);
ctxt.do.resetClip(ctxt);
ctxt.do.resetCTM(ctxt)
END DefResetContext;
PROCEDURE DefSetCTM* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
BEGIN
ctxt.ctm := mat
END DefSetCTM;
PROCEDURE DefTranslate* (ctxt: Context; dx, dy: REAL);
BEGIN
GfxMatrix.Translate(ctxt.ctm, dx, dy, ctxt.ctm)
END DefTranslate;
PROCEDURE DefScale* (ctxt: Context; sx, sy: REAL);
BEGIN
GfxMatrix.Scale(ctxt.ctm, sx, sy, ctxt.ctm)
END DefScale;
PROCEDURE DefRotate* (ctxt: Context; sin, cos: REAL);
BEGIN
GfxMatrix.Rotate(ctxt.ctm, sin, cos, ctxt.ctm)
END DefRotate;
PROCEDURE DefConcat* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
BEGIN
GfxMatrix.Concat(mat, ctxt.ctm, ctxt.ctm)
END DefConcat;
PROCEDURE DefSetStrokeColor* (ctxt: Context; color: Color);
BEGIN
ctxt.strokeCol := color
END DefSetStrokeColor;
PROCEDURE DefSetStrokePattern* (ctxt: Context; pat: Pattern);
BEGIN
ctxt.strokePat := pat
END DefSetStrokePattern;
PROCEDURE DefSetFillColor* (ctxt: Context; color: Color);
BEGIN
ctxt.fillCol := color
END DefSetFillColor;
PROCEDURE DefSetFillPattern* (ctxt: Context; pat: Pattern);
BEGIN
ctxt.fillPat := pat
END DefSetFillPattern;
PROCEDURE DefSetLineWidth* (ctxt: Context; width: REAL);
BEGIN
ctxt.lineWidth := width
END DefSetLineWidth;
PROCEDURE DefSetDashPattern* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
BEGIN
SetDashArray(ctxt, on, off, len);
ctxt.dashPhase := phase
END DefSetDashPattern;
PROCEDURE DefSetCapStyle* (ctxt: Context; style: CapStyle);
BEGIN
ctxt.capStyle := style
END DefSetCapStyle;
PROCEDURE DefSetJoinStyle* (ctxt: Context; style: JoinStyle);
BEGIN
ctxt.joinStyle := style
END DefSetJoinStyle;
PROCEDURE DefSetStyleLimit* (ctxt: Context; limit: REAL);
BEGIN
ctxt.styleLimit := limit
END DefSetStyleLimit;
PROCEDURE DefSetFlatness* (ctxt: Context; flatness: REAL);
BEGIN
ctxt.flatness := flatness
END DefSetFlatness;
PROCEDURE DefSetFont* (ctxt: Context; font: GfxFonts.Font);
BEGIN
ctxt.font := font
END DefSetFont;
PROCEDURE DefGetStringWidth* (ctxt: Context; VAR str: ARRAY OF CHAR; VAR dx, dy: REAL);
BEGIN
GfxFonts.GetStringWidth(ctxt.font, str, dx, dy)
END DefGetStringWidth;
PROCEDURE DefFlatten* (ctxt: Context);
BEGIN
GetFlattenedPath(ctxt, ctxt.tmpPath);
GfxPaths.Copy(ctxt.tmpPath, ctxt.path);
GfxPaths.Clear(ctxt.tmpPath)
END DefFlatten;
PROCEDURE DefOutline* (ctxt: Context);
BEGIN
GetOutline(ctxt, ctxt.tmpPath);
GfxPaths.Copy(ctxt.tmpPath, ctxt.path);
GfxPaths.Clear(ctxt.tmpPath)
END DefOutline;
PROCEDURE DefRect* (ctxt: Context; x0, y0, x1, y1: REAL);
BEGIN
ctxt.do.enter(ctxt, x0, y0, 0, y0 - y1);
ctxt.do.line(ctxt, x1, y0); ctxt.do.line(ctxt, x1, y1); ctxt.do.line(ctxt, x0, y1); ctxt.do.line(ctxt, x0, y0);
ctxt.do.exit(ctxt, x1 - x0, 0)
END DefRect;
PROCEDURE DefEllipse* (ctxt: Context; x, y, rx, ry: REAL);
VAR xr: REAL;
BEGIN
xr := x + rx;
IF xr # x THEN
ctxt.do.enter(ctxt, xr, y, 0, ry);
ctxt.do.arc(ctxt, xr, y, x, y, xr, y, x, y + ry);
ctxt.do.exit(ctxt, 0, ry)
END
END DefEllipse;
PROCEDURE DefNewPattern* (ctxt: Context; img: GfxImages.Image; px, py: REAL): Pattern;
VAR pat: Pattern;
BEGIN
NEW(pat); pat.img := img; pat.px := px; pat.py := py;
RETURN pat
END DefNewPattern;
PROCEDURE InitColors;
PROCEDURE init (VAR col: Color; r, g, b: INTEGER);
BEGIN
col.r := r; col.g := g; col.b := b; col.a := 255
END init;
BEGIN
init(Black, 0, 0, 0); init(White, 255, 255, 255); init(Red, 255, 0, 0); init(Green, 0, 255, 0); init(Blue, 0, 0, 255);
init(Cyan, 0, 255, 255); init(Magenta, 255, 0, 255); init(Yellow, 255, 255, 0);
init(LGrey, 192, 192, 192); init(MGrey, 160, 160, 160); init(DGrey, 128, 128, 128)
END InitColors;
BEGIN
InitColors;
DefaultCap := ButtCap; DefaultJoin := MiterJoin
END Gfx.