Explorar o código

removed because no any activity & oberon port of the AGG 2d graphics in progress

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@8471 8c9fc860-2736-0410-a75d-ab315db34111
eth.metacore %!s(int64=6) %!d(string=hai) anos
pai
achega
5ba4938154
Modificáronse 9 ficheiros con 0 adicións e 8186 borrados
  1. 0 1595
      source/AGfx.Mod
  2. BIN=BIN
      source/AGfx.Tool
  3. 0 209
      source/AGfxBuffer.Mod
  4. 0 1124
      source/AGfxFonts.Mod
  5. 0 794
      source/AGfxImages.Mod
  6. 0 345
      source/AGfxMatrix.Mod
  7. 0 1404
      source/AGfxPaths.Mod
  8. 0 1359
      source/AGfxRaster.Mod
  9. 0 1356
      source/AGfxRegions.Mod

+ 0 - 1595
source/AGfx.Mod

@@ -1,1595 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE Gfx; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "High-level, device independent 2D-graphics"; *)
-
-	(*
-		11.2.98 - changed behaviour of GetOutline if current line width is zero: calculates dashed path instead of outline (eos)
-		11.2.98 - eliminated offset parameter from subpath begin since it can be simulated by modifying the dash phase
-		16.2.98 - DrawPath now accepts current path
-		17.2.98 - ...but only if Record is not included in mode
-		17.2.98 - added RenderPath
-		19.2.98 - simplified cap and join styles to procedures
-		19.2.98 - eliminated clip path (was not correct, anyway), introduced GetClipRect instead
-		6.3.98 - fixed bug in GetDashOutline (started last dash twice even if fully drawn)
-		18.9.98 - several changes: renaming, added stroke pattern and rect/ellipse methods, text is now part of path model,
-			standard colors
-		9.12.98 - adaptation to new GfxMaps
-		10.3.99 - separate dash pattern into on/off arrays
-		13.5.99 - remodeled cap and join styles
-		13.5.99 - eliminated 'erase'
-		2.6.99 - bugfix in GetStrokeOutline and GetDashOutline (forgot to consume GfxPaths.Exit after subpath)
-		8.6.99 - made GetPolyOutline automatically close open subpaths
-		25.8.99 - use GfxImages instead of GfxMaps
-		6.10.99 - save and restore graphics state, MoveTo and Close
-		13.02.2000 - added ClipArea, replaced SaveClip/RestoreClip by GetClip/SetClip, fine-grained SaveState semantics
-		26.02.2000 - allow font change within path
-		27.02.2000 - added DrawArc
-		29.03.2000 - made default flatness 1/2
-		25.05.2000 - no longer imports Texts and Oberon
-		12.06.2001 - made tmpPath and dashPath ctxt fields to allow concurrency
-	*)
-
-	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;	(** drawing mode elements **)
-		InPath* = 5; InSubpath* = 6;	(** context state **)
-		MaxDashPatSize* = 8;	(** maximal number of dash entries **)
-		NoJoin* = 0; MiterJoin* = 1; BevelJoin* = 2; RoundJoin* = 3;	(** join styles **)
-		NoCap* = 0; ButtCap* = 1; SquareCap* = 2; RoundCap* = 3;	(** cap styles **)
-
-		(** state elements **)
-		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
-
-		(** color type **)
-		Color* = RECORD
-			r*, g*, b*, a*: INTEGER;
-		END;
-
-		(** fill patterns **)
-		Pattern* = POINTER TO PatternDesc;
-		PatternDesc* = RECORD
-			img*: GfxImages.Image;	(** replicated image map **)
-			px*, py*: REAL;	(** pinpoint coordinates **)
-		END;
-
-		(** line join and cap styles **)
-		JoinStyle* = SHORTINT;
-		CapStyle* = SHORTINT;
-
-		(** abstract clip areas **)
-		ClipArea* = POINTER TO ClipAreaDesc;
-		ClipAreaDesc* = RECORD END;
-
-		(** graphics context **)
-		Context* = OBJECT { ABSTRACT }
-		VAR
-			mode*: SET;	(** current drawing mode **)
-			path*: GfxPaths.Path;	(** current path in device coordinates (updated only if mode contains the 'Record' flag) **)
-			cpx*, cpy*: REAL;	(** current point in user coordinates **)
-			ctm*: GfxMatrix.Matrix;	(** current transformation matrix **)
-			cam*: GfxMatrix.Matrix;	(** current attribute matrix (frozen ctm while inside path) **)
-			strokeCol*, fillCol*: Color;	(** current stroke and fill color **)
-			strokePat*, fillPat*: Pattern;	(** current stroke and fill pattern **)
-			lineWidth*: REAL;	(** current line width **)
-			dashPatOn*, dashPatOff*: ARRAY MaxDashPatSize OF REAL;	(** line dash array **)
-			dashPatLen*: LONGINT;	(** number of valid elements in dash arrays **)
-			dashPhase*: REAL;	(** offset for first dash **)
-			dashPeriod*: REAL;	(** sum of dash element lengths **)
-			capStyle*: CapStyle;	(** line cap style **)
-			joinStyle*: JoinStyle;	(** line join style **)
-			styleLimit*: REAL;	(** determines area that may be rendered to by styles **)
-			flatness*: REAL;	(** current flatness tolerance (in device coordinates) **)
-			font*: GfxFonts.Font;	(** current font **)
-			dashPath: GfxPaths.Path;	(* path for temporarily storing dashes *)
-			tmpPath: GfxPaths.Path;
-
-			(** initialize context values to defaults **)
-			PROCEDURE InitContext* ();
-			BEGIN
-				SELF.ctm := GfxMatrix.Identity; SELF.cam := SELF.ctm;
-				SELF.strokeCol := Black; SELF.strokePat := NIL;
-				SELF.fillCol := Black; SELF.fillPat := NIL;
-				SELF.lineWidth := 1;
-				SELF.dashPatLen := 0; SELF.dashPhase := 0; SELF.dashPeriod := 0;
-				SELF.capStyle := DefaultCap; SELF.joinStyle := DefaultJoin; SELF.styleLimit := 5;
-				SELF.mode := {};
-				SELF.path := NIL;
-				SELF.cpx := 0; SELF.cpy := 0;
-				SELF.flatness := 0.5;
-				SELF.font := GfxFonts.Default;
-				IF SELF.tmpPath = NIL THEN
-					NEW(SELF.tmpPath);
-				END;
-				IF SELF.dashPath = NIL THEN
-					NEW(SELF.dashPath)
-				END;
-			END InitContext;
-
-			(** reset context to default values **)
-			PROCEDURE Reset*();
-			BEGIN
-				SELF.InitContext();
-				SELF.ResetClip();
-				SELF.ResetCTM()
-			END Reset;
-
-		(**--- Coordinate System ---**)
-
-			(** reset current transformation matrix **)
-			PROCEDURE{ABSTRACT} ResetCTM*(); END ResetCTM;
-
-			(** set current transformation matrix **)
-			PROCEDURE SetCTM*(VAR mat: GfxMatrix.Matrix);
-			BEGIN
-				SELF.ctm := mat;
-			END SetCTM;
-
-			(** translate coordinate system **)
-			PROCEDURE Translate*(dx, dy: REAL);
-			BEGIN
-				GfxMatrix.Translate(SELF.ctm, dx, dy, SELF.ctm);
-			END Translate;
-
-			(** scale coordinate system at origin **)
-			PROCEDURE Scale*(sx, sy: REAL);
-			BEGIN
-				GfxMatrix.Scale(SELF.ctm, sx, sy, SELF.ctm);
-			END Scale;
-
-			(** scale coordinate system at specified point **)
-			PROCEDURE ScaleAt* (sx, sy, x, y: REAL);
-			BEGIN
-				SELF.Translate(x, y);
-				SELF.Scale(sx, sy);
-				SELF.Translate(-x, -y)
-			END ScaleAt;
-
-			(** rotate coordinate system at origin **)
-			PROCEDURE Rotate*(sin, cos: REAL);
-			BEGIN
-				GfxMatrix.Rotate(SELF.ctm, sin, cos, SELF.ctm);
-			END Rotate;
-
-			(** rotate coordinate system at specified point **)
-			PROCEDURE RotateAt* (sin, cos, x, y: REAL);
-			BEGIN
-				SELF.Translate(x, y);
-				SELF.Rotate(sin, cos);
-				SELF.Translate(-x, -y)
-			END RotateAt;
-
-			(** concat transformation matrix to CTM **)
-			PROCEDURE Concat*(VAR mat: GfxMatrix.Matrix);
-			BEGIN
-				GfxMatrix.Concat(mat, SELF.ctm, SELF.ctm);
-			END Concat;
-
-		(**--- Clipping ---**)
-
-			(** reset clip path **)
-			PROCEDURE{ABSTRACT} ResetClip*(); END ResetClip;
-
-			(** get bounding box of clipping path in user coordinates **)
-			PROCEDURE{ABSTRACT} GetClipRect*(VAR llx, lly, urx, ury: REAL); END GetClipRect;
-
-			(** get current clipping area **)
-			PROCEDURE{ABSTRACT} GetClip*(): ClipArea; END GetClip;
-
-			(** restore saved clipping path **)
-			PROCEDURE{ABSTRACT} SetClip*(clip: ClipArea); END SetClip;
-
-		(**--- Graphics State ---**)
-
-			(** set stroke color **)
-			PROCEDURE SetStrokeColor*(color: Color);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.strokeCol := color;
-			END SetStrokeColor;
-
-			(** set stroke pattern (NIL = solid) **)
-			PROCEDURE SetStrokePattern*(pat: Pattern);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.strokePat := pat
-			END SetStrokePattern;
-
-			(** set fill color **)
-			PROCEDURE SetFillColor*(color: Color);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.fillCol := color;
-			END SetFillColor;
-
-			(** set fill pattern (NIL = solid) **)
-			PROCEDURE SetFillPattern*(pat: Pattern);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.fillPat := pat
-			END SetFillPattern;
-
-			(** set line width **)
-			PROCEDURE SetLineWidth*(width: REAL);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				ASSERT(width >= 0.0, 101);
-				SELF.lineWidth := width
-			END SetLineWidth;
-
-			(** set dash pattern **)
-			PROCEDURE SetDashPattern*(VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				ASSERT((len <= LEN(on)) & (len <= LEN(off)), 101);
-				SELF.SetDashArray(on, off, len);
-				SELF.dashPhase := phase
-			END SetDashPattern;
-
-			(** copy values from parameter, and calculate dash period **)
-			PROCEDURE SetDashArray* (VAR on, off: ARRAY OF REAL; len: LONGINT);
-			BEGIN
-				SELF.dashPatLen := len;
-				SELF.dashPeriod := 0;
-				IF len > 0 THEN
-					REPEAT
-						DEC(len);
-						SELF.dashPatOn[len] := on[len]; SELF.dashPatOff[len] := off[len];
-						SELF.dashPeriod := SELF.dashPeriod + on[len] + off[len]
-					UNTIL len = 0
-				END;
-				ASSERT((SELF.dashPatLen = 0) OR (SELF.dashPeriod # 0), 120)
-			END SetDashArray;
-
-			(** set line cap style **)
-			PROCEDURE SetCapStyle*(style: CapStyle);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				ASSERT((NoCap <= style) & (style <= RoundCap), 101);
-				SELF.capStyle := style
-			END SetCapStyle;
-
-			(** set line join style **)
-			PROCEDURE SetJoinStyle*(style: JoinStyle);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				ASSERT((NoJoin <= style) & (style <= RoundJoin), 101);
-				SELF.joinStyle := style
-			END SetJoinStyle;
-
-			(** set style border factor **)
-			PROCEDURE SetStyleLimit*(limit: REAL);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.styleLimit := limit
-			END SetStyleLimit;
-
-			(** set flatness parameter **)
-			PROCEDURE SetFlatness*(flatness: REAL);
-			BEGIN
-				SELF.flatness := flatness
-			END SetFlatness;
-
-			(** set current font **)
-			PROCEDURE SetFont*(font: GfxFonts.Font);
-			BEGIN
-				ASSERT(font # NIL, 100);
-				SELF.font := font
-			END SetFont;
-
-			(** set current font using name and size **)
-			PROCEDURE SetFontName* (fontname: ARRAY OF CHAR; size: INTEGER);
-				VAR font: GfxFonts.Font;
-			BEGIN
-				font := GfxFonts.OpenSize(fontname, size);
-				IF font = NIL THEN font := GfxFonts.Default END;
-				SELF.SetFont(font)
-			END SetFontName;
-
-			(** calculate distance that current point would move if given string were rendered **)
-			PROCEDURE GetStringWidth*(VAR str: ARRAY OF CHAR; VAR dx, dy: REAL);
-			BEGIN
-				GfxFonts.GetStringWidth(SELF.font, str, dx, dy)
-			END GetStringWidth;
-
-		(**--- Current Path ---**)
-
-			(** start new path **)
-			PROCEDURE Begin* (mode: SET);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin(mode);
-				INCL(SELF.mode, InPath)
-			END Begin;
-
-			(** exit current subpath (if open) and end current path **)
-			PROCEDURE End* ();
-			BEGIN
-				ASSERT(InPath IN SELF.mode, 100);
-				IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
-				SELF.DoEnd();
-				EXCL(SELF.mode, InPath)
-			END End;
-
-			(** end current subpath (if open) and begin new subpath **)
-			PROCEDURE MoveTo* (x, y: REAL);
-			BEGIN
-				ASSERT(InPath IN SELF.mode, 100);
-				IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
-				SELF.DoEnter(x, y, 0, 0);
-				INCL(SELF.mode, InSubpath)
-			END MoveTo;
-
-			(** start subpath at inner point **)
-			PROCEDURE Enter* (x, y, dx, dy: REAL);
-			BEGIN
-				ASSERT(InPath IN SELF.mode, 100);
-				IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
-				SELF.DoEnter(x, y, dx, dy);
-				INCL(SELF.mode, InSubpath)
-			END Enter;
-
-			(** end subpath at inner point **)
-			PROCEDURE Exit* (dx, dy: REAL);
-			BEGIN
-				ASSERT(InSubpath IN SELF.mode, 100);
-				SELF.DoExit(dx, dy);
-				EXCL(SELF.mode, InSubpath)
-			END Exit;
-
-			(** close current subpath **)
-			PROCEDURE Close* ();
-			BEGIN
-				ASSERT(InSubpath IN SELF.mode, 100);
-				SELF.DoClose();
-				EXCL(SELF.mode, InSubpath)
-			END Close;
-
-			(** append line to current path **)
-			PROCEDURE LineTo* (x, y: REAL);
-			BEGIN
-				ASSERT(InSubpath IN SELF.mode, 100);
-				SELF.DoLine(x, y)
-			END LineTo;
-
-			(** append arc to current path **)
-			PROCEDURE ArcTo* (x, y, x0, y0, x1, y1, x2, y2: REAL);
-			BEGIN
-				ASSERT(InSubpath IN SELF.mode, 100);
-				SELF.DoArc(x, y, x0, y0, x1, y1, x2, y2)
-			END ArcTo;
-
-			(** append cubic bezier to current path **)
-			PROCEDURE BezierTo* (x, y, x1, y1, x2, y2: REAL);
-			BEGIN
-				ASSERT(InSubpath IN SELF.mode, 100);
-				SELF.DoBezier(x, y, x1, y1, x2, y2)
-			END BezierTo;
-	
-			(** append character outlines to current path at given point; advance current point to position after last character **)
-			PROCEDURE ShowAt* (x, y: REAL; str: ARRAY OF CHAR);
-			BEGIN
-				ASSERT(InPath IN SELF.mode, 100);
-				IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
-				SELF.DoShow(x, y, str)
-			END ShowAt;
-			
-			(** append character outlines to current path at current point; advance current point to position after last character **)
-			PROCEDURE Show* (str: ARRAY OF CHAR);
-			BEGIN
-				ASSERT(InPath IN SELF.mode, 100);
-				IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
-				SELF.DoShow(SELF.cpx, SELF.cpy, str)
-			END Show;
-
-		(**--- Path Flattening ---**)
-
-			(** replace arcs and beziers in current path by approximation using straight lines **)
-			PROCEDURE Flatten*();
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.GetFlattenedPath(SELF.tmpPath);
-				SELF.tmpPath.CopyTo(SELF.path);
-				SELF.tmpPath.Clear()
-			END Flatten;
-
-			(** store flattened current path in given path  **)
-			PROCEDURE GetFlattenedPath* (path: GfxPaths.Path);
-				VAR data: PathData;
-			BEGIN
-				ASSERT(SELF.path # path, 100);
-				path.Clear();
-				data.path := path;
-				SELF.path.EnumFlattened(SELF.flatness, EnumPathElem, data)
-			END GetFlattenedPath;
-
-		(**--- Cap Styles ---**)
-
-			PROCEDURE EnterCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
-			BEGIN
-				IF SELF.capStyle = ButtCap THEN
-					path.AddEnter(x, y, dy, -dx);
-					path.AddLine(x + dy, y - dx)
-				ELSIF SELF.capStyle = SquareCap THEN
-					path.AddEnter(x - dx, y - dy, dy, -dx);
-					path.AddLine(x - dx + dy, y - dy - dx);
-					path.AddLine(x + dy, y - dx)
-				ELSIF SELF.capStyle = RoundCap THEN
-					path.AddEnter(x - dx, y - dy, dy, -dx);
-					path.AddArc(x + dy, y - dx, x, y, x - dx, y - dy, x + dy, y - dx)
-				ELSE
-					path.AddEnter(x + dy, y - dx, 0, 0)
-				END
-			END EnterCapStyle;
-
-			PROCEDURE AddCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
-			BEGIN
-				IF SELF.capStyle = ButtCap THEN
-					path.AddLine(x + dy, y - dx)
-				ELSIF SELF.capStyle = SquareCap THEN
-					path.AddLine(x - dx - dy, y - dy + dx);
-					path.AddLine(x - dx + dy, y - dy - dx);
-					path.AddLine(x + dy, y - dx)
-				ELSIF SELF.capStyle = RoundCap THEN
-					path.AddArc(x + dy, y - dx, x, y, x - dy, y + dx, x - dx, y - dy)
-				ELSE
-					path.AddExit(0, 0);
-					path.AddEnter(x + dy, y - dx, 0, 0)
-				END
-			END AddCapStyle;
-
-			PROCEDURE ExitCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
-			BEGIN
-				IF SELF.capStyle = ButtCap THEN
-					path.AddLine(x, y);
-					path.AddExit(dy, -dx)
-				ELSIF SELF.capStyle = SquareCap THEN
-					path.AddLine(x - dx - dy, y - dy + dx);
-					path.AddLine(x - dx, y - dy);
-					path.AddExit(dy, -dx)
-				ELSIF SELF.capStyle = RoundCap THEN
-					path.AddArc(x - dx, y - dy, x, y, x - dy, y + dx, x - dx, y - dy);
-					path.AddExit(dy, -dx)
-				ELSE
-					path.AddExit(0, 0)
-				END
-			END ExitCapStyle;
-
-		(**--- Join Styles ---**)
-
-			(** return if half axis vector (in device coordinates) exceeds style limit **)
-			PROCEDURE ExceedsLimit* (hx, hy: REAL): BOOLEAN;
-				VAR limit: REAL;
-			BEGIN
-				GfxMatrix.ApplyToDist(SELF.cam, 0.5*SELF.lineWidth * SELF.styleLimit, limit);
-				RETURN hx * hx + hy * hy > limit * limit
-			END ExceedsLimit;
-
-			PROCEDURE EnterJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-				VAR ix, iy, t: REAL;
-			BEGIN
-				IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
-					GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
-					path.AddEnter(ix, iy, -hy, hx);
-					path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = MiterJoin THEN
-					path.AddEnter(x + hx, y + hy, idx, idy);
-					path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = RoundJoin THEN
-					t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
-					path.AddEnter(x + t * hx, y + t * hy, -hy, hx);
-					path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
-				ELSE
-					path.AddEnter(x + ody, y - odx, 0, 0)
-				END
-			END EnterJoinStyle;
-
-			PROCEDURE AddJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-			BEGIN
-				IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
-					path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = MiterJoin THEN
-					path.AddLine(x + hx, y + hy);
-					path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = RoundJoin THEN
-					path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
-				ELSE
-					path.AddExit(0, 0);
-					path.AddEnter(x + ody, y - odx, 0, 0)
-				END
-			END AddJoinStyle;
-
-			PROCEDURE ExitJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-				VAR ix, iy, t: REAL;
-			BEGIN
-				IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
-					GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
-					path.AddLine(ix, iy);
-					path.AddExit(-hy, hx)
-				ELSIF SELF.joinStyle = MiterJoin THEN
-					path.AddLine(x + hx, y + hy);
-					path.AddExit(odx, ody)
-				ELSIF SELF.joinStyle = RoundJoin THEN
-					t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
-					path.AddArc(x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx);
-					path.AddExit(-hy, hx)
-				ELSE
-					path.AddExit(0, 0)
-				END
-			END ExitJoinStyle;
-
-		(**--- Path Outline ---**)
-
-			(** replace current path by outline of area which would be drawn to if the path were stroked **)
-			PROCEDURE Outline*();
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.GetOutline(SELF.tmpPath);
-				SELF.tmpPath.CopyTo(SELF.path);
-				SELF.tmpPath.Clear()
-			END Outline;
-
-			PROCEDURE AddEnterJoinStyle (x, y, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-				VAR ix, iy, t: REAL;
-			BEGIN
-				IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
-					GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
-					path.AddLine(ix, iy); path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = MiterJoin THEN
-					path.AddLine(x + hx, y + hy); path.AddLine(x + ody, y - odx)
-				ELSIF SELF.joinStyle = RoundJoin THEN
-					t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
-					path.AddLine(x + t * hx, y + t * hy);
-					path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
-				ELSE
-					path.AddLine(x + ody, y - odx)
-				END
-			END AddEnterJoinStyle;
-
-			PROCEDURE AddExitJoinStyle (x, y, idx, idy, hx, hy: REAL; path: GfxPaths.Path);
-				VAR ix, iy, t: REAL;
-			BEGIN
-				IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
-					GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
-					path.AddLine(ix, iy)
-				ELSIF SELF.joinStyle = MiterJoin THEN
-					path.AddLine(x + hx, y + hy)
-				ELSIF SELF.joinStyle = RoundJoin THEN
-					t := Math.sqrt((idx * idx + idy * idy)/(hx * hx + hy * hy));
-					path.AddArc(x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx)
-				END;
-				path.AddLine(x - hx, y - hy)
-			END AddExitJoinStyle;
-
-			PROCEDURE GetPolyOutline (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(SELF.cam, 0.5*SELF.lineWidth, width);
-				GetNormVector(x[1] - x[0], y[1] - y[0], width, odx, ody);
-				IF (dxi = 0) & (dyi = 0) THEN
-					SELF.EnterCapStyle(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	(* collinear vectors *)
-						IF closed THEN
-							dst.AddEnter(x[0] + ody, y[0] - odx, dxi, dyi)
-						ELSE
-							dst.AddEnter(x[0] - ody, y[0] + odx, ody, -odx);
-							dst.AddLine(x[0] + ody, y[0] - odx)
-						END
-					ELSIF idx * ody > idy * odx THEN	(* starts with left turn *)
-						IF closed THEN
-							SELF.EnterJoinStyle(x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
-						ELSE
-							dst.AddEnter(x[0] - hx, y[0] - hy, ody, -odx);
-							SELF.AddEnterJoinStyle(x[0], y[0], hx, hy, odx, ody, dst)
-						END
-					ELSE
-						IF closed THEN
-							dst.AddEnter(x[0] - hx, y[0] - hy, dxi, dyi)
-						ELSE
-							dst.AddEnter(x[0] + hx, y[0] + hy, -hx, -hy);
-							dst.AddLine(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	(* collinear vectors *)
-						dst.AddLine(x[i] + idy, y[i] - idx)
-					ELSIF idx * ody > idy * odx THEN	(* left turn => outer join *)
-						dst.AddLine(x[i] + idy, y[i] - idx);
-						SELF.AddJoinStyle(x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
-					ELSE	(* right turn => inner join *)
-						dst.AddLine(x[i] - hx, y[i] - hy)
-					END;
-					i := j; INC(j)
-				END;
-
-				idx := odx; idy := ody;
-				IF (dxo = 0) & (dyo = 0) THEN
-					dst.AddLine(x[n] + ody, y[n] - odx);
-					SELF.AddCapStyle(x[n], y[n], -odx, -ody, dst)
-				ELSE
-					dst.AddLine(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	(* collinear vectors *)
-						IF closed THEN
-							dst.AddExit(odx, ody);
-							dst.AddEnter(x[n] - idy, y[n] + idx, -dxo, -dyo)
-						ELSE
-							dst.AddLine(x[n] - idy, y[n] + idx)
-						END
-					ELSIF idx * ody > idy * odx THEN	(* ends in left turn *)
-						IF closed THEN
-							SELF.ExitJoinStyle(x[n], y[n], idx, idy, hx, hy, odx, ody, dst);
-							dst.AddEnter(x[n] - hx, y[n] - hy, -dxo, -dyo)
-						ELSE
-							SELF.AddExitJoinStyle(x[n], y[n], idx, idy, hx, hy, dst)
-						END
-					ELSE
-						dst.AddLine(x[n] - hx, y[n] - hy);
-						IF closed THEN
-							dst.AddExit(dxo, dyo);
-							SELF.EnterJoinStyle(x[n], y[n], -odx, -ody, -hx, -hy, -idx, -idy, dst)
-						ELSE
-							SELF.AddEnterJoinStyle(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	(* collinear vectors *)
-						dst.AddLine(x[i] + idy, y[i] - idx)
-					ELSIF idx * ody > idy * odx THEN	(* left turn => outer join *)
-						dst.AddLine(x[i] + idy, y[i] - idx);
-						SELF.AddJoinStyle(x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
-					ELSE	(* right turn => inner join *)
-						dst.AddLine(x[i] - hx, y[i] - hy)
-					END;
-					i := j; DEC(j)
-				END;
-
-				IF (dxi = 0) & (dyi = 0) THEN
-					dst.AddLine(x[0] + ody, y[0] - odx);
-					SELF.ExitCapStyle(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);
-					dst.AddLine(x[0] + idy, y[0] - idx);
-					IF (hx = 0) & (hy = 0) THEN	(* collinear vectors *)
-						IF closed THEN
-							dst.AddExit(-dxi, -dyi)
-						ELSE
-							dst.AddExit(-idy, idx)
-						END
-					ELSIF idx * ody > idy * odx THEN	(* left turn *)
-						IF closed THEN
-							SELF.ExitJoinStyle(x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
-						ELSE
-							SELF.AddExitJoinStyle(x[0], y[0], idx, idy, hx, hy, dst);
-							dst.AddExit(-idx, -idy)
-						END
-					ELSE
-						dst.AddLine(x[0] - hx, y[0] - hy);
-						IF closed THEN
-							dst.AddExit(-dxi, -dyi)
-						ELSE
-							dst.AddExit(hx, hy)
-						END
-					END
-				END
-			END GetPolyOutline;
-
-			PROCEDURE GetStrokeOutline (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;
-				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];
-						SELF.GetPolyOutline(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;
-					scan.Scan()
-				END;
-				IF n > 0 THEN
-					SELF.GetPolyOutline(x, y, n, dxi, dyi, scan.dx, scan.dy, dst)
-				END;
-				scan.Scan()
-			END GetStrokeOutline;
-
-			(** get offset values and pattern index of visible and invisible dash part at start of subpath (in device space) **)
-			PROCEDURE GetDashOffsets* (offset: REAL; VAR beg, end, next: REAL; VAR idx: LONGINT);
-				VAR phase, period, len: REAL;
-			BEGIN
-				idx := 0;
-				GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPhase, phase);
-				GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPeriod, period);
-				beg := ENTIER((phase + offset)/period) * period - phase;	(* offset - period < beg <= offset *)
-				LOOP
-					GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOn[idx], len);
-					end := beg + len;
-					GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOff[idx], len);
-					next := end + len;
-					idx := (idx+1) MOD SELF.dashPatLen;
-					IF next > offset THEN EXIT END;
-					beg := next
-				END
-			END GetDashOffsets;
-
-			PROCEDURE GetDashOutline (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(SELF.cam, 0.5*SELF.lineWidth, width);
-				ASSERT(scan.elem = GfxPaths.Enter);
-				cx := scan.x; cy := scan.y; dx := scan.dx; dy := scan.dy;
-				scan.Scan();
-				SELF.GetDashOffsets(0, beg, end, next, index);
-				IF 0 < end THEN	(* starts within dash *)
-					IF width = 0 THEN
-						dst.AddEnter(cx, cy, dx, dy)
-					ELSE
-						SELF.dashPath.Clear();
-						SELF.dashPath.AddEnter(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	(* begin of line is within dash *)
-						IF end <= endOff THEN	(* end of current dash comes before end of line => finish current dash *)
-							len := end - offset;
-							IF width = 0 THEN
-								dst.AddLine(cx + len * cos, cy + len * sin);
-								dst.AddExit(0, 0)
-							ELSE
-								SELF.dashPath.AddLine(cx + len * cos, cy + len * sin);
-								SELF.dashPath.AddExit(0, 0);
-								dscan.Open(SELF.dashPath, 0);
-								SELF.GetStrokeOutline(dscan, dst)
-							END
-						ELSIF width = 0 THEN	(* continue current dash to end of line *)
-							dst.AddLine(scan.x, scan.y)
-						ELSE
-							SELF.dashPath.AddLine(scan.x, scan.y)
-						END
-					END;
-					IF next < endOff THEN	(* next dash starts before end of line => draw complete dashes *)
-						wdx := width * cos; wdy := width * sin;
-						beg := offset;
-						REPEAT
-							len := next - beg;
-							cx := cx + len * cos; cy := cy + len * sin;
-							beg := next;
-							GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOn[index], dash);
-							end := beg + dash;
-							GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOff[index], dash);
-							next := end + dash;
-							index := (index+1) MOD SELF.dashPatLen;
-							IF end <= endOff THEN	(* next dash can be fully drawn *)
-								len := end - beg;
-								nx := cx + len * cos; ny := cy + len * sin;
-								IF width = 0 THEN
-									dst.AddEnter(cx, cy, 0, 0);
-									dst.AddLine(nx, ny);
-									dst.AddExit(0, 0)
-								ELSE
-									SELF.EnterCapStyle(cx, cy, wdx, wdy, dst);
-									dst.AddLine(nx + wdy, ny - wdx);
-									SELF.AddCapStyle(nx, ny, -wdx, -wdy, dst);
-									dst.AddLine(cx - wdy, cy + wdx);
-									SELF.ExitCapStyle(cx, cy, wdx, wdy, dst)
-								END
-							END
-						UNTIL next >= endOff;
-						IF endOff < end THEN	(* next dash not complete => hasn't been started yet *)
-							IF width = 0 THEN
-								dst.AddEnter(cx, cy, 0, 0);
-								dst.AddLine(scan.x, scan.y)
-							ELSE
-								SELF.dashPath.Clear();
-								SELF.dashPath.AddEnter(cx, cy, 0, 0);
-								SELF.dashPath.AddLine(scan.x, scan.y)
-							END
-						END
-					END;
-					cx := scan.x; cy := scan.y; offset := endOff;
-					scan.Scan()
-				END;
-				ASSERT(scan.elem = GfxPaths.Exit);
-				IF offset < end THEN	(* currently within dash => end properly *)
-					IF width = 0 THEN
-						dst.AddExit(scan.dx, scan.dy)
-					ELSE
-						SELF.dashPath.AddExit(scan.dx, scan.dy);
-						dscan.Open(SELF.dashPath, 0);
-						SELF.GetStrokeOutline(dscan, dst)
-					END
-				END;
-				scan.Scan()
-			END GetDashOutline;
-
-			(** store outline/dashes of current path in specified path **)
-			PROCEDURE GetOutline* (dst: GfxPaths.Path);
-				VAR scan: GfxPaths.Scanner;
-			BEGIN
-				ASSERT(dst # SELF.path, 100);
-				SELF.Flatten();
-				dst.Clear();
-				scan.Open(SELF.path, 0);
-				WHILE scan.elem = GfxPaths.Enter DO
-					IF SELF.dashPatLen > 0 THEN
-						SELF.GetDashOutline(scan, dst)
-					ELSE
-						SELF.GetStrokeOutline(scan, dst)
-					END
-				END
-			END GetOutline;
-
-		(**--- Drawing Operations ---**)
-
-			(** draw current path in requested mode **)
-			PROCEDURE Render* (mode: SET);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				EXCL(mode, Record);
-				IF mode # {} THEN
-					SELF.DoRender(mode)
-				END
-			END Render;
-
-			(** draw given path in requested mode **)
-			PROCEDURE DrawPath* (path: GfxPaths.Path; mode: SET);
-				VAR scan: GfxPaths.Scanner;
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				IF path = SELF.path THEN
-					EXCL(mode, Record);
-					IF mode # {} THEN
-						SELF.DoRender(mode)
-					END;
-				ELSE
-					SELF.DoBegin(mode);
-					scan.Open(path, 0);
-					WHILE scan.elem # GfxPaths.Stop DO
-						CASE scan.elem OF
-						| GfxPaths.Enter: SELF.DoEnter(scan.x, scan.y, scan.dx, scan.dy)
-						| GfxPaths.Line: SELF.DoLine(scan.x, scan.y);
-						| GfxPaths.Arc: SELF.DoArc(scan.x, scan.y, scan.x0, scan.y0, scan.x1, scan.y1, scan.x2, scan.y2)
-						| GfxPaths.Bezier: SELF.DoBezier(scan.x, scan.y, scan.x1, scan.y1, scan.x2, scan.y2)
-						| GfxPaths.Exit: SELF.DoExit(scan.dx, scan.dy)
-						END;
-						scan.Scan();
-					END;
-					SELF.DoEnd()
-				END
-			END DrawPath;
-
-			(** draw line in requested mode **)
-			PROCEDURE DrawLine* (x0, y0, x1, y1: REAL; mode: SET);
-			BEGIN
-				IF (x0=x1)&(y0=y1) THEN RETURN END; (*optimization PH 2012*)
-				ASSERT(~(InPath IN SELF.mode), 100);
-				ASSERT(mode * {Fill, Clip, EvenOdd} = {}, 101);
-				SELF.DoBegin(mode);
-				SELF.DoEnter(x0, y0, 0, 0);
-				SELF.DoLine(x1, y1);
-				SELF.DoExit(0, 0);
-				SELF.DoEnd()
-			END DrawLine;
-
-			(** draw arc in requested mode (start and end angle in radians; negative radius for clockwise arc) **)
-			PROCEDURE DrawArc* (x, y, r, start, end: REAL; mode: SET);
-				VAR x1, y1, x2, y2: REAL;
-			BEGIN
-				ASSERT(~(InPath IN SELF.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;
-				SELF.DoBegin(mode);
-				SELF.DoEnter(x + r * Math.cos(start), y + r * Math.sin(start), 0, 0);
-				SELF.DoArc(x + r * Math.cos(end), y + r * Math.sin(end), x, y, x1, y1, x2, y2);
-				SELF.DoEnd()
-			END DrawArc;
-
-			(** draw rectangle in requested mode **)
-			PROCEDURE DrawRect* (x0, y0, x1, y1: REAL; mode: SET);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin(mode);
-				SELF.DoRect(x0, y0, x1, y1);
-				SELF.DoEnd()
-			END DrawRect;
-
-			(** draw circle in requested mode (clockwise if r > 0, counterclockwise if r < 0) **)
-			PROCEDURE DrawCircle* (x, y, r: REAL; mode: SET);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin(mode);
-				SELF.DoEllipse(x, y, r, ABS(r));
-				SELF.DoEnd()
-			END DrawCircle;
-
-			(** draw ellipse in requested mode (clockwise if rx*ry > 0, counterclockwise if rx*ry < 0) **)
-			PROCEDURE DrawEllipse* (x, y, rx, ry: REAL; mode: SET);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin(mode);
-				SELF.DoEllipse(x, y, rx, ry);
-				SELF.DoEnd()
-			END DrawEllipse;
-
-			(** draw string at given coordinates and move current point to string end **)
-			PROCEDURE DrawStringAt* (x, y: REAL; str: ARRAY OF CHAR);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin({Fill});
-				SELF.DoShow(x, y, str);
-				SELF.DoEnd()
-			END DrawStringAt;
-
-			(** draw string at current point and move current point to string end **)
-			PROCEDURE DrawString* (str: ARRAY OF CHAR);
-			BEGIN
-				ASSERT(~(InPath IN SELF.mode), 100);
-				SELF.DoBegin({Fill});
-				SELF.DoShow(SELF.cpx, SELF.cpy, str);
-				SELF.DoEnd()
-			END DrawString;
-
-		(** images and patterns **)
-
-			(** draw image at given point **)
-			PROCEDURE DrawImageAt* (x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
-			BEGIN
-				SELF.DrawImage(x, y, img, filter)
-			END DrawImageAt;
-
-			PROCEDURE{ABSTRACT} DrawImage*(x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter); END DrawImage;
-
-			PROCEDURE NewPattern*(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 NewPattern;
-
-		(**--- Implementation ---**)
-			(** draw current path in requested mode **)
-			PROCEDURE{ABSTRACT} DoRender*(mode: SET); END DoRender;
-
-			(** start new path **)
-			PROCEDURE{ABSTRACT} DoBegin*(mode: SET); END DoBegin;
-
-			(** exit current subpath (if open) and end current path **)
-			PROCEDURE{ABSTRACT} DoEnd*(); END DoEnd;
-
-			(** start subpath at inner point **)
-			PROCEDURE{ABSTRACT} DoEnter*(x, y, dx, dy: REAL); END DoEnter;
-
-			(** end subpath at inner point **)
-			PROCEDURE{ABSTRACT} DoExit*(dx, dy: REAL); END DoExit;
-
-			(** close current subpath **)
-			PROCEDURE{ABSTRACT} DoClose*(); END DoClose;
-
-			(** append line to current path **)
-			PROCEDURE{ABSTRACT} DoLine*(x, y: REAL); END DoLine;
-
-			(** append arc to current path **)
-			PROCEDURE{ABSTRACT} DoArc*(x, y, x0, y0, x1, y1, x2, y2: REAL); END DoArc;
-
-			(** append cubic bezier to current path **)
-			PROCEDURE{ABSTRACT} DoBezier*(x, y, x1, y1, x2, y2: REAL); END DoBezier;
-
-			(** append character outlines to current path at current point; advance current point to position after last character **)
-			PROCEDURE{ABSTRACT} DoShow*(x, y: REAL; VAR str: ARRAY OF CHAR); END DoShow;
-
-			(** painting operators (potential for optimization) **)
-			PROCEDURE{ABSTRACT} DoRect*(x0, y0, x1, y1: REAL); (* default implementation *)
-			BEGIN
-				SELF.DoEnter(x0, y0, 0, y0 - y1);
-				SELF.DoLine(x1, y0); SELF.DoLine(x1, y1); SELF.DoLine(x0, y1); SELF.DoLine(x0, y0);
-				SELF.DoExit(x1 - x0, 0)
-			END DoRect;
-
-			PROCEDURE{ABSTRACT} DoEllipse*(x, y, rx, ry: REAL); (* default implementation *)
-				VAR xr: REAL;
-			BEGIN
-				xr := x + rx;
-				IF xr # x THEN
-					SELF.DoEnter(xr, y, 0, ry);
-					SELF.DoArc(xr, y, x, y, xr, y, x, y + ry);
-					SELF.DoExit(0, ry)
-				END
-			END DoEllipse;
-
-		END Context;
-
-		(** graphics state **)
-		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;	(** standard colors **)
-		DefaultCap*: CapStyle;	(** default line cap style (initially butt caps) **)
-		DefaultJoin*: JoinStyle;	(** default line join style (initially miter joins) **)
-
-
-	(**--- Contexts ---**)
-
-	(** reset context to default values **)
-	PROCEDURE Reset* (ctxt: Context);
-	BEGIN
-		ctxt.Reset()
-	END Reset;
-
-	(** initialize context values to defaults **)
-	PROCEDURE Init* (ctxt: Context);
-	BEGIN
-		ctxt.InitContext();
-	END Init;
-
-	(** save and restore graphics state **)
-	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.GetClip()
-		END
-	END Save;
-
-	PROCEDURE Restore* (ctxt: Context; state: State);
-	BEGIN
-		ASSERT(~(InPath IN ctxt.mode), 100);
-		IF strokeColPat IN state.saved THEN
-			ctxt.SetStrokeColor(state.strokeCol);
-			ctxt.SetStrokePattern(state.strokePat)
-		END;
-		IF fillColPat IN state.saved THEN
-			ctxt.SetFillColor(state.fillCol);
-			ctxt.SetFillPattern(state.fillPat)
-		END;
-		IF lineWidth IN state.saved THEN
-			ctxt.SetLineWidth(state.lineWidth)
-		END;
-		IF dashPat IN state.saved THEN
-			ctxt.SetDashPattern(state.dashPatOn, state.dashPatOff, state.dashPatLen, state.dashPhase)
-		END;
-		IF capStyle IN state.saved THEN
-			ctxt.SetCapStyle(state.capStyle)
-		END;
-		IF joinStyle IN state.saved THEN
-			ctxt.SetJoinStyle(state.joinStyle)
-		END;
-		IF styleLimit IN state.saved THEN
-			ctxt.SetStyleLimit(state.styleLimit)
-		END;
-		IF flatness IN state.saved THEN
-			ctxt.SetFlatness(state.flatness)
-		END;
-		IF font IN state.saved THEN
-			ctxt.SetFont(state.font)
-		END;
-		IF ctm IN state.saved THEN
-			ctxt.SetCTM(state.ctm)
-		END;
-		IF clip IN state.saved THEN
-			ctxt.SetClip(state.clip)
-		END
-	END Restore;
-
-	(**--- Coordinate System ---**)
-
-	(** reset current transformation matrix **)
-	PROCEDURE ResetCTM* (ctxt: Context);
-	BEGIN
-		ctxt.ResetCTM()
-	END ResetCTM;
-
-	(** set current transformation matrix **)
-	PROCEDURE SetCTM* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
-	BEGIN
-		ctxt.SetCTM(mat)
-	END SetCTM;
-
-	(** translate coordinate system **)
-	PROCEDURE Translate* (ctxt: Context; dx, dy: REAL);
-	BEGIN
-		ctxt.Translate(dx, dy)
-	END Translate;
-
-	(** scale coordinate system at origin **)
-	PROCEDURE Scale* (ctxt: Context; sx, sy: REAL);
-	BEGIN
-		ctxt.Scale(sx, sy)
-	END Scale;
-
-	(** scale coordinate system at specified point **)
-	PROCEDURE ScaleAt* (ctxt: Context; sx, sy, x, y: REAL);
-	BEGIN
-		ctxt.ScaleAt(sx, sy, x, y);
-	END ScaleAt;
-
-	(** rotate coordinate system at origin **)
-	PROCEDURE Rotate* (ctxt: Context; sin, cos: REAL);
-	BEGIN
-		ctxt.Rotate(sin, cos)
-	END Rotate;
-
-	(** rotate coordinate system at specified point **)
-	PROCEDURE RotateAt* (ctxt: Context; sin, cos, x, y: REAL);
-	BEGIN
-		ctxt.RotateAt(sin, cos, x, y);
-	END RotateAt;
-
-	(** concat transformation matrix to CTM **)
-	PROCEDURE Concat* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
-	BEGIN
-		ctxt.Concat(mat)
-	END Concat;
-
-
-	(**--- Clipping ---**)
-
-	(** reset clip path **)
-	PROCEDURE ResetClip* (ctxt: Context);
-	BEGIN
-		ctxt.ResetClip()
-	END ResetClip;
-
-	(** get bounding box of clipping path in user coordinates **)
-	PROCEDURE GetClipRect* (ctxt: Context; VAR llx, lly, urx, ury: REAL);
-	BEGIN
-		ctxt.GetClipRect(llx, lly, urx, ury)
-	END GetClipRect;
-
-	(** get current clipping area **)
-	PROCEDURE GetClip* (ctxt: Context): ClipArea;
-	BEGIN
-		RETURN ctxt.GetClip()
-	END GetClip;
-
-	(** restore saved clipping path **)
-	PROCEDURE SetClip* (ctxt: Context; clip: ClipArea);
-	BEGIN
-		ctxt.SetClip(clip)
-	END SetClip;
-
-
-	(**--- Graphics State ---**)
-
-	(** set stroke color **)
-	PROCEDURE SetStrokeColor* (ctxt: Context; color: Color);
-	BEGIN
-		ctxt.SetStrokeColor(color)
-	END SetStrokeColor;
-
-	(** set stroke pattern (NIL = solid) **)
-	PROCEDURE SetStrokePattern* (ctxt: Context; pat: Pattern);
-	BEGIN
-		ctxt.SetStrokePattern(pat)
-	END SetStrokePattern;
-
-	(** set fill color **)
-	PROCEDURE SetFillColor* (ctxt: Context; color: Color);
-	BEGIN
-		ctxt.SetFillColor(color)
-	END SetFillColor;
-
-	(** set fill pattern (NIL = solid) **)
-	PROCEDURE SetFillPattern* (ctxt: Context; pat: Pattern);
-	BEGIN
-		ctxt.SetFillPattern(pat)
-	END SetFillPattern;
-
-	(** set line width **)
-	PROCEDURE SetLineWidth* (ctxt: Context; width: REAL);
-	BEGIN
-		ctxt.SetLineWidth(width)
-	END SetLineWidth;
-
-	(** set dash pattern **)
-	PROCEDURE SetDashPattern* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
-	BEGIN
-		ctxt.SetDashPattern(on, off, len, phase)
-	END SetDashPattern;
-
-	(** copy values from parameter, and calculate dash period **)
-	PROCEDURE SetDashArray* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT);
-	BEGIN
-		ctxt.SetDashArray(on, off, len);
-	END SetDashArray;
-
-	(** set line cap style **)
-	PROCEDURE SetCapStyle* (ctxt: Context; style: CapStyle);
-	BEGIN
-		ctxt.SetCapStyle(style)
-	END SetCapStyle;
-
-	(** set line join style **)
-	PROCEDURE SetJoinStyle* (ctxt: Context; style: JoinStyle);
-	BEGIN
-		ctxt.SetJoinStyle(style)
-	END SetJoinStyle;
-
-	(** set style border factor **)
-	PROCEDURE SetStyleLimit* (ctxt: Context; limit: REAL);
-	BEGIN
-		ctxt.SetStyleLimit(limit)
-	END SetStyleLimit;
-
-	(** set flatness parameter **)
-	PROCEDURE SetFlatness* (ctxt: Context; flatness: REAL);
-	BEGIN
-		ctxt.SetFlatness(flatness)
-	END SetFlatness;
-
-	(** set current font **)
-	PROCEDURE SetFont* (ctxt: Context; font: GfxFonts.Font);
-	BEGIN
-		ctxt.SetFont(font)
-	END SetFont;
-
-	(** set current font using name and size **)
-	PROCEDURE SetFontName* (ctxt: Context; fontname: ARRAY OF CHAR; size: INTEGER);
-	BEGIN
-		ctxt.SetFontName(fontname, size);
-	END SetFontName;
-
-	(** calculate distance that current point would move if given string were rendered **)
-	PROCEDURE GetStringWidth* (ctxt: Context; str: ARRAY OF CHAR; VAR dx, dy: REAL);
-	BEGIN
-		ctxt.GetStringWidth(str, dx, dy)
-	END GetStringWidth;
-
-
-	(**--- Current Path ---**)
-
-	(** start new path **)
-	PROCEDURE Begin* (ctxt: Context; mode: SET);
-	BEGIN
-		ctxt.Begin(mode);
-	END Begin;
-
-	(** exit current subpath (if open) and end current path **)
-	PROCEDURE End* (ctxt: Context);
-	BEGIN
-		ctxt.End();
-	END End;
-
-	(** end current subpath (if open) and begin new subpath **)
-	PROCEDURE MoveTo* (ctxt: Context; x, y: REAL);
-	BEGIN
-		ctxt.MoveTo(x, y);
-	END MoveTo;
-
-	(** start subpath at inner point **)
-	PROCEDURE Enter* (ctxt: Context; x, y, dx, dy: REAL);
-	BEGIN
-		ctxt.Enter(x, y, dx, dy);
-	END Enter;
-
-	(** end subpath at inner point **)
-	PROCEDURE Exit* (ctxt: Context; dx, dy: REAL);
-	BEGIN
-		ctxt.Exit(dx, dy);
-	END Exit;
-
-	(** close current subpath **)
-	PROCEDURE Close* (ctxt: Context);
-	BEGIN
-		ctxt.Close();
-	END Close;
-
-	(** append line to current path **)
-	PROCEDURE LineTo* (ctxt: Context; x, y: REAL);
-	BEGIN
-		ctxt.LineTo(x, y);
-	END LineTo;
-
-	(** append arc to current path **)
-	PROCEDURE ArcTo* (ctxt: Context; x, y, x0, y0, x1, y1, x2, y2: REAL);
-	BEGIN
-		ctxt.ArcTo(x, y, x0, y0, x1, y1, x2, y2);
-	END ArcTo;
-
-	(** append cubic bezier to current path **)
-	PROCEDURE BezierTo* (ctxt: Context; x, y, x1, y1, x2, y2: REAL);
-	BEGIN
-		ctxt.BezierTo(x, y, x1, y1, x2, y2);
-	END BezierTo;
-
-	(** append character outlines to current path at given point; advance current point to position after last character **)
-	PROCEDURE ShowAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
-	BEGIN
-		ctxt.ShowAt(x, y, str);
-	END ShowAt;
-
-	(** append character outlines to current path at current point; advance current point to position after last character **)
-	PROCEDURE Show* (ctxt: Context; str: ARRAY OF CHAR);
-	BEGIN
-		ctxt.Show(str);
-	END Show;
-
-
-	(**--- Path Flattening ---**)
-
-	(** replace arcs and beziers in current path by approximation using straight lines **)
-	PROCEDURE Flatten* (ctxt: Context);
-	BEGIN
-		ctxt.Flatten()
-	END Flatten;
-
-	PROCEDURE EnumPathElem (VAR data: GfxPaths.EnumData);
-	BEGIN
-		WITH data: PathData DO
-			CASE data.elem OF
-			| GfxPaths.Enter: data.path.AddEnter(data.x, data.y, data.dx, data.dy)
-			| GfxPaths.Line: data.path.AddLine(data.x, data.y)
-			| GfxPaths.Exit: data.path.AddExit(data.dx, data.dy)
-			END
-		END
-	END EnumPathElem;
-
-	(** store flattened current path in given path  **)
-	PROCEDURE GetFlattenedPath* (ctxt: Context; path: GfxPaths.Path);
-	BEGIN
-		ctxt.GetFlattenedPath(path);
-	END GetFlattenedPath;
-
-
-	(**--- Cap Styles ---**)
-
-	PROCEDURE EnterCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.EnterCapStyle(x, y, dx, dy, path);
-	END EnterCapStyle;
-
-	PROCEDURE AddCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.AddCapStyle(x, y, dx, dy, path);
-	END AddCapStyle;
-
-	PROCEDURE ExitCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.ExitCapStyle(x, y, dx, dy, path);
-	END ExitCapStyle;
-
-
-	(**--- Join Styles ---**)
-
-	(** return if half axis vector (in device coordinates) exceeds style limit **)
-	PROCEDURE ExceedsLimit* (ctxt: Context; hx, hy: REAL): BOOLEAN;
-	BEGIN
-		RETURN ctxt.ExceedsLimit(hx, hy);
-	END ExceedsLimit;
-
-	PROCEDURE EnterJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.EnterJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
-	END EnterJoinStyle;
-
-	PROCEDURE AddJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.AddJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
-	END AddJoinStyle;
-
-	PROCEDURE ExitJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
-	BEGIN
-		ctxt.ExitJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
-	END ExitJoinStyle;
-
-
-	(**--- Path Outline ---**)
-
-	(** replace current path by outline of area which would be drawn to if the path were stroked **)
-	PROCEDURE Outline* (ctxt: Context);
-	BEGIN
-		ctxt.Outline()
-	END Outline;
-
-	(** return vector scaled to given length **)
-	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;
-
-	(** return vector to outer corner of two joining vectors whose lengths correspond to line width **)
-	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	(* intersect outer border lines to find half axis vector *)
-			t := ((idy - ody) * ody + (idx - odx) * odx)/cprod;
-			IF cprod > 0 THEN	(* left turn *)
-				hx := idy - t * idx; hy := -(idx + t * idy)
-			ELSE	(* right turn *)
-				hx := t * idx - idy; hy := idx + t * idy
-			END
-		END
-	END GetHalfAxisVector;
-
-	(** get offset values and pattern index of visible and invisible dash part at start of subpath (in device space) **)
-	PROCEDURE GetDashOffsets* (ctxt: Context; offset: REAL; VAR beg, end, next: REAL; VAR idx: LONGINT);
-	BEGIN
-		ctxt.GetDashOffsets(offset, beg, end, next, idx);
-	END GetDashOffsets;
-
-	(** store outline/dashes of current path in specified path **)
-	PROCEDURE GetOutline* (ctxt: Context; dst: GfxPaths.Path);
-	BEGIN
-		ctxt.GetOutline(dst);
-	END GetOutline;
-
-
-	(**--- Drawing Operations ---**)
-
-	(** draw current path in requested mode **)
-	PROCEDURE Render* (ctxt: Context; mode: SET);
-	BEGIN
-		ASSERT(~(InPath IN ctxt.mode), 100);
-		EXCL(mode, Record);
-		IF mode # {} THEN
-			ctxt.DoRender(mode)
-		END
-	END Render;
-
-	(** draw given path in requested mode **)
-	PROCEDURE DrawPath* (ctxt: Context; path: GfxPaths.Path; mode: SET);
-	BEGIN
-		ctxt.DrawPath(path, mode);
-	END DrawPath;
-
-	(** draw line in requested mode **)
-	PROCEDURE DrawLine* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
-	BEGIN
-		ctxt.DrawLine(x0, y0, x1, y1, mode);
-	END DrawLine;
-
-	(** draw arc in requested mode (start and end angle in radians; negative radius for clockwise arc) **)
-	PROCEDURE DrawArc* (ctxt: Context; x, y, r, start, end: REAL; mode: SET);
-	BEGIN
-		ctxt.DrawArc(x, y, r, start, end, mode);
-	END DrawArc;
-
-	(** draw rectangle in requested mode **)
-	PROCEDURE DrawRect* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
-	BEGIN
-		ctxt.DrawRect(x0, y0, x1, y1, mode);
-	END DrawRect;
-
-	(** draw circle in requested mode (clockwise if r > 0, counterclockwise if r < 0) **)
-	PROCEDURE DrawCircle* (ctxt: Context; x, y, r: REAL; mode: SET);
-	BEGIN
-		ctxt.DrawCircle(x, y, r, mode);
-	END DrawCircle;
-
-	(** draw ellipse in requested mode (clockwise if rx*ry > 0, counterclockwise if rx*ry < 0) **)
-	PROCEDURE DrawEllipse* (ctxt: Context; x, y, rx, ry: REAL; mode: SET);
-	BEGIN
-		ctxt.DrawEllipse(x, y, rx, ry, mode);
-	END DrawEllipse;
-
-	(** draw string at given coordinates and move current point to string end **)
-	PROCEDURE DrawStringAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
-	BEGIN
-		ctxt.DrawStringAt(x, y, str);
-	END DrawStringAt;
-
-	(** draw string at current point and move current point to string end **)
-	PROCEDURE DrawString* (ctxt: Context; str: ARRAY OF CHAR);
-	BEGIN
-		ctxt.DrawString(str);
-	END DrawString;
-
-
-	(**--- Images and Patterns ---**)
-
-	(** draw image at given point **)
-	PROCEDURE DrawImageAt* (ctxt: Context; x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
-	BEGIN
-		ctxt.DrawImage(x, y, img, filter)
-	END DrawImageAt;
-
-	(** return new pattern **)
-	PROCEDURE NewPattern* (ctxt: Context; img: GfxImages.Image; px, py: REAL): Pattern;
-	BEGIN
-		RETURN ctxt.NewPattern(img, px, py)
-	END NewPattern;
-
-	(*--- Initialization of Standard Colors ---*)
-
-	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.

BIN=BIN
source/AGfx.Tool


+ 0 - 209
source/AGfxBuffer.Mod

@@ -1,209 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxBuffer; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Raster contexts rendering into background buffers"; *)
-
-	(*
-		10.12.98 - first version; derived from GfxDev
-		25.8.99 - replaced GfxMaps with Images/GfxImages
-		10.8.99 - scratched SetPoint, added Close method
-		13.02.2000 - new get/set clip methods
-		10.05.2000 - Rect now ignores empty rectangles
-	*)
-
-
-	IMPORT
-		Images := Raster, GfxMatrix, GfxImages, GfxRegions, Gfx, GfxRaster;
-
-
-	TYPE
-		Context* = OBJECT (GfxRaster.Context)
-		VAR
-			orgX*, orgY*: REAL;	(** origin of default coordinate system **)
-			scale*: REAL;	(** default scale factor **)
-			bgCol*: Gfx.Color;	(** background color for erasing **)
-			img*: Images.Image;	(** target buffer **)
-			pix: Images.Pixel;
-			compOp:SHORTINT; (* composition operation for raster device *)
-
-			(** initialize buffered context **)
-			PROCEDURE InitBuffer* (img: Images.Image);
-			BEGIN
-				SELF.InitRaster();
-				SELF.compOp := 1; (* Copy Op is default *)
-				SELF.img := img;
-				SELF.SetCoordinates(0, 0, 1);
-				SELF.SetBGColor(Gfx.White);
-				SELF.Reset();
-			END InitBuffer;
-
-
-			PROCEDURE{FINAL} SetColPat* (col: Gfx.Color; pat: Gfx.Pattern);
-			BEGIN
-				SetColPat^(col, pat);
-				Images.SetRGBA(SELF.pix, col.r, col.g, col.b, col.a)
-			END SetColPat;
-
-			(** set default coordinate origin and scale factor **)
-			PROCEDURE{FINAL} SetCoordinates* (x, y, scale: REAL);
-			BEGIN
-				SELF.orgX := x; SELF.orgY := y; SELF.scale := scale
-			END SetCoordinates;
-
-			(** set background color **)
-			PROCEDURE{FINAL} SetBGColor* (col: Gfx.Color);
-			BEGIN
-				SELF.bgCol := col
-			END SetBGColor;
-
-			(** set composition operation as defined in Raster **)
-			PROCEDURE{FINAL} SetCompOp* (op:SHORTINT);
-			BEGIN
-				SELF.compOp := op
-			END SetCompOp;
-
-		(**--- Coordinate System ---**)
-
-			(** current transformation matrix **)
-			PROCEDURE{FINAL} ResetCTM* ();
-			BEGIN
-				GfxMatrix.Translate(GfxMatrix.Identity, SELF.orgX, SELF.orgY, SELF.ctm);
-				GfxMatrix.Scale(SELF.ctm, SELF.scale, SELF.scale, SELF.ctm)
-			END ResetCTM;
-
-		(**--- Clipping ---**)
-
-			PROCEDURE{FINAL} ResetClip* ();
-			BEGIN
-				SELF.ResetClip^();
-				SELF.clipReg.SetToRect(0, 0, SHORT(SELF.img.width), SHORT(SELF.img.height))
-			END ResetClip;
-
-		(**--- Raster ---**)
-
-			PROCEDURE{FINAL} DrawImage* (x, y: REAL; img: Images.Image; VAR filter: GfxImages.Filter);
-				VAR m: GfxMatrix.Matrix; dx, dy, llx, lly, urx, ury: INTEGER; col: Images.Pixel;
-			BEGIN
-				GfxMatrix.Translate(SELF.ctm, x, y, m);
-				dx := SHORT(ENTIER(m[2, 0] + 0.5));
-				dy := SHORT(ENTIER(m[2, 1] + 0.5));
-				col := filter.col;
-				Images.SetModeColor(filter, SELF.fillCol.r, SELF.fillCol.g, SELF.fillCol.b);
-				IF (filter.hshift # GfxImages.hshift) & (dx + 0.1 < m[2, 0]) & (m[2, 0] < dx + 0.9) OR
-					(filter.vshift # GfxImages.vshift) & (dy + 0.1 < m[2, 1]) & (m[2, 1] < dy + 0.9) OR
-					GfxMatrix.Scaled(m) OR
-					GfxMatrix.Rotated(m)
-				THEN
-					GfxImages.Transform(img, SELF.img, m, filter)
-				ELSE
-					llx := 0; lly := 0; urx := SHORT(img.width); ury := SHORT(img.height);
-					GfxRegions.ClipRect(llx, lly, urx, ury, SELF.clipReg.llx - dx, SELF.clipReg.lly - dy, SELF.clipReg.urx - dx, SELF.clipReg.ury - dy);
-					IF SELF.clipReg.llx >  dx THEN dx := SELF.clipReg.llx END;
-					IF SELF.clipReg.lly > dy THEN dy := SELF.clipReg.lly END;
-					IF dx + urx > SELF.img.width THEN urx := SHORT(SELF.img.width - dx) END;
-					IF dy + ury > SELF.img.height THEN ury := SHORT(SELF.img.height - dy) END;
-					IF dx < 0 THEN llx := -dx; dx := 0 END;
-					IF dy < 0 THEN lly := -dy; dy := 0 END;
-					IF (llx < urx) & (lly < ury) THEN
-						IF ~GfxRegions.RectEmpty(llx, lly, urx, ury) THEN
-							Images.Copy(img, SELF.img, llx, lly, urx, ury, dx, dy, filter)
-						END
-					END
-				END;
-				Images.SetModeColor(filter, ORD(col[Images.r]), ORD(col[Images.g]), ORD(col[Images.b]))
-			END DrawImage;
-
-			PROCEDURE{FINAL} dot*(x, y: LONGINT);	(** current dot procedure **)
-				VAR px, py: LONGINT; mode: Images.Mode;
-			BEGIN
-				IF (SELF.clipState = GfxRaster.In) OR
-					(SELF.clipState = GfxRaster.InOut) & SELF.clipReg.RectInside(SHORT(x), SHORT(y), SHORT(x+1), SHORT(y+1))
-				THEN
-					IF SELF.pat = NIL THEN
-						Images.InitMode(mode, SELF.compOp);
-						Images.Put(SELF.img, SHORT(x), SHORT(y), SELF.pix, mode)
-					ELSE
-						px := (x - ENTIER(SELF.orgX + SELF.pat.px + 0.5)) MOD SELF.pat.img.width;
-						py := (y - ENTIER(SELF.orgY + SELF.pat.py + 0.5)) MOD SELF.pat.img.height;
-						Images.InitModeColor(mode, Images.srcOverDst, SELF.col.r, SELF.col.g, SELF.col.b);
-						Images.Copy(SELF.pat.img, SELF.img, px, py, px+1, py+1, SHORT(x), SHORT(y), mode)
-					END
-				END
-			END dot;
-
-			PROCEDURE{FINAL} rect* (llx, lly, urx, ury: LONGINT);	(** current rect procedure **)
-				VAR data: RegData; mode: Images.Mode;
-			BEGIN
-				IF (SELF.clipState # GfxRaster.Out) & (llx < urx) & (lly < ury) THEN
-					IF SELF.pat = NIL THEN
-						IF SELF.clipState = GfxRaster.In THEN
-							Images.InitMode(mode, SELF.compOp);
-							Images.Fill(SELF.img, SHORT(llx), SHORT(lly), SHORT(urx), SHORT(ury), SELF.pix, mode)
-						ELSE
-							data.bc := SELF;
-							SELF.clipReg.Enumerate(SHORT(llx), SHORT(lly), SHORT(urx), SHORT(ury), Color, data)
-						END
-					ELSE
-						data.bc := SELF;
-						data.dx := SHORT(ENTIER(SELF.orgX + SELF.pat.px + 0.5));
-						data.dy := SHORT(ENTIER(SELF.orgY + SELF.pat.py + 0.5));
-						Images.InitModeColor(data.mode, Images.srcOverDst, SELF.col.r, SELF.col.g, SELF.col.b);
-						SELF.clipReg.Enumerate(SHORT(llx), SHORT(lly), SHORT(urx), SHORT(ury), Tile, data)
-					END
-				END
-			END rect;
-
-		END Context;
-
-		RegData = RECORD (GfxRegions.EnumData)
-			dx, dy: INTEGER;
-			bc: Context;
-			mode: Images.Mode;
-		END;
-
-	(*--- Rendering ---*)
-
-	PROCEDURE Color (llx, lly, urx, ury: INTEGER; VAR data: GfxRegions.EnumData);
-		VAR bc: Context; mode: Images.Mode;
-	BEGIN
-		bc := data(RegData).bc;
-		Images.InitMode(mode, bc.compOp);
-		Images.Fill(bc.img, llx, lly, urx, ury, bc.pix, mode)
-	END Color;
-
-	PROCEDURE Tile (llx, lly, urx, ury: INTEGER; VAR data: GfxRegions.EnumData);
-		VAR bc: Context;
-	BEGIN
-		WITH data: RegData DO
-			bc := data.bc;
-			Images.FillPattern(bc.pat.img, bc.img, llx, lly, urx, ury, data.dx, data.dy, data.mode)
-		END
-	END Tile;
-
-
-	(** set default coordinate origin and scale factor **)
-	PROCEDURE SetCoordinates* (bc: Context; x, y, scale: REAL);
-	BEGIN
-		bc.SetCoordinates(x, y, scale);
-	END SetCoordinates;
-
-	(** set background color **)
-	PROCEDURE SetBGColor* (bc: Context; col: Gfx.Color);
-	BEGIN
-		bc.SetBGColor(col);
-	END SetBGColor;
-
-	(** set composition operation as defined in Raster **)
-	PROCEDURE SetCompOp* (bc: Context; op:SHORTINT);
-	BEGIN
-		bc.SetCompOp(op);
-	END SetCompOp;
-
-	(** initialize buffered context **)
-	PROCEDURE Init* (bc: Context; img: Images.Image);
-	BEGIN
-		bc.InitBuffer( img );
-	END Init;
-
-END GfxBuffer.

+ 0 - 1124
source/AGfxFonts.Mod

@@ -1,1124 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxFonts; (** non-portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Gfx font engine"; *)
-
-	(*
-		8.1.98 - GetInstance now transforms an existing bounding box correctly
-		13.1.98 - bug fix in OpenRaster, font.ymax was not computed correctly for scaled raster fonts in OpenRaster
-		13.1.98 - improved GetStringWidth; uses outline metrics now if images would be taken from outline, too
-		9.2.98 - adapted SplineToBezier to new behaviour of GfxPaths.EnumSpline (no Enter/Exit)
-		18.9.98 - major cleanup of both interface and implementation (support for other font formats, switch to GfxMaps,
-			aging of cached chars, use GfxFonts0 to enumerate available raster fonts)
-		9.12.98 - raster files; adaptation to new GfxMaps
-		16.2.99 - bugfix in InitDefault (m[1, 1] wasn't set), tnx to pjm
-		12.3.99 - bugfix in WFGetWidth (dy = dx)
-		20.4.99 - put point size in separate field instead of merging it with font matrix
-		8.6.99 - don't set niceMaps for large raster fonts if outline exists
-		16.7.99 - try to open raster font before calling extensions
-		25.8.99 - use Images and GfxImages instead of GfxMaps, PictImages instead of GfxPictures
-		28.10.99 - WarpMap doesn't try to create empty images anymore
-		21.02.2000 - introduced registered font families to improve opening foreign font file formats
-		14.03.2000 - improved wildcard handling for registered font families (key without value is command)
-		30.03.2000 - accept "M.P=*" as wildcard
-		24.05.2000 - integrated GfxFonts0, no longer imports Fonts, for the moment no registered extensions
-	*)
-
-	IMPORT
-		SYSTEM, KernelLog, Commands, Files, Configuration, Math, Raster, GfxMatrix,
-		GfxImages, GfxPaths, GfxRegions;
-
-
-	CONST
-		FontNameLen* = 64;
-		MaxCachedChars = 512;	(* maximal number of cached characters *)
-		MetaFontTag = 01F7H; OldMetaFontTag = 701H - 1000H;	(* = F701H *)
-		MaxBezierPoints = 3*GfxPaths.MaxSplinePoints + 1;
-		DPI = 91.44;
-		FontId = 0DBX;
-
-
-	TYPE
-		FontName* = ARRAY FontNameLen OF CHAR;
-
-		(* Metafont outlines **)
-		Outline = POINTER TO OutlineDesc;
-		OutlineDesc = RECORD
-			width: ARRAY 256 OF REAL;	(* width including left and right side bearings (0 if character undefined) *)
-			len: ARRAY 256 OF SHORTINT;	(* number of subpaths of each character *)
-			path: GfxPaths.Path;	(* path containing character outlines *)
-			pos: ARRAY 256 OF INTEGER;	(* positions of characters within path *)
-			xmin, ymin, xmax, ymax: REAL;	(* union of character bounding boxes *)
-		END;
-
-		(* cached characters *)
-		Char = POINTER TO CharDesc;
-		CharDesc = RECORD
-			x, y, dx, dy: REAL;	(* metrics *)
-			map: Raster.Image;	(* pixels *)
-			used: INTEGER;	(* number of accesses to this character *)
-		END;
-
-		(* raster file *)
-		RasterChar = POINTER TO RasterCharDesc;
-		RasterCharDesc = RECORD
-			dx, x, y, w, h: INTEGER;
-			adr: ADDRESS;
-		END;
-		RasterFile = POINTER TO RasterFileDesc;
-		RasterFileDesc = RECORD
-			xmin, ymin, xmax, ymax: INTEGER;
-			char: ARRAY 256 OF RasterChar;
-			mem: POINTER TO ARRAY OF CHAR;
-		END;
-
-		(** font structure **)
-		Font* = POINTER TO FontDesc;
-		Methods* = POINTER TO MethodDesc;
-		FontDesc* = RECORD
-			class*: Methods;
-			name*: FontName;	(** font name **)
-			ptsize*: INTEGER;	(** point size **)
-			mat*, wmat: GfxMatrix.Matrix;	(** font matrix **)
-			xmin*, ymin*, xmax*, ymax*: INTEGER;	(** union of character bounding boxes **)
-			niceMaps*: BOOLEAN;	(** true if returned bitmaps look better than the filled outlines **)
-			outline: Outline;	(* outline, if available *)
-			prev, next: Font;	(* previous and next font in font cache *)
-			char: ARRAY 256 OF Char;	(* cached characters *)
-			rfile: RasterFile;	(* link to raster file *)
-		END;
-
-		MethodDesc* = RECORD
-			derive*: PROCEDURE (font: Font; ptsize: INTEGER; VAR mat: GfxMatrix.Matrix): Font;
-			getwidth*: PROCEDURE (font: Font; ch: CHAR; VAR dx, dy: REAL);
-			getmap*: PROCEDURE (font: Font; ch: CHAR; VAR x, y, dx, dy: REAL; VAR map: Raster.Image);
-			getoutline*: PROCEDURE (font: Font; ch: CHAR; x, y: REAL; path: GfxPaths.Path);
-		END;
-
-		PathEnumData = RECORD (GfxPaths.EnumData)
-			xc, yc: ARRAY MaxBezierPoints OF REAL;	(* control points for spline-Bezier conversion *)
-			n: INTEGER;	(* number of control points *)
-			lx, ly: REAL;	(* current point for converting path to region *)
-			px, py: INTEGER;	(* current region point coordinates *)
-			region: GfxRegions.Region;
-		END;
-
-		RegEnumData = RECORD (GfxRegions.EnumData)
-			map: Raster.Image;
-			dx, dy: INTEGER;
-		END;
-
-
-	VAR
-		Default*: Font;	(** system default font **)
-		OpenProc*: PROCEDURE (VAR family, style: ARRAY OF CHAR; ptsize: INTEGER; VAR mat: GfxMatrix.Matrix): Font;
-		FClass, OFClass, WFClass, OWFClass, OClass: Methods;	(* builtin font classes *)
-		Cache: Font;	(* sentinel for list of cached fonts *) (* RACE ?*)
-		Chars: LONGINT;	(* current number of cached characters *)
-	(*	TmpPath: GfxPaths.Path;
-		TmpRegion: GfxRegions.Region; *)
-
-	(*--- File Directory ---*)
-
-	PROCEDURE Append(VAR to(** in/out *): ARRAY OF CHAR; this: ARRAY OF CHAR);
-		VAR i, j, l: LONGINT;
-	BEGIN
-		i := 0;
-		WHILE to[i] # 0X DO
-			INC(i)
-		END;
-		l := LEN(to)-1; j := 0;
-		WHILE (i < l) & (this[j] # 0X) DO
-			to[i] := this[j]; INC(i); INC(j)
-		END;
-		to[i] := 0X
-	END Append;
-
-	PROCEDURE AppendCh(VAR to(** in/out *): ARRAY OF CHAR; this: CHAR);
-		VAR i: LONGINT;
-	BEGIN
-		i := 0;
-		WHILE to[i] # 0X DO
-			INC(i)
-		END;
-		IF i < (LEN(to)-1) THEN
-			to[i] := this; to[i+1] := 0X
-		END
-	END AppendCh;
-
-	PROCEDURE IntToStr(val: LONGINT; VAR str: ARRAY OF CHAR);
-		VAR
-			i, j: LONGINT;
-			digits: ARRAY 16 OF LONGINT;
-	BEGIN
-		IF val = MIN(LONGINT) THEN
-			COPY("-2147483648", str);
-			RETURN
-		END;
-		IF val < 0 THEN
-			val := -val; str[0] := "-"; j := 1
-		ELSE
-			j := 0
-		END;
-		i := 0;
-		REPEAT
-			digits[i] := val MOD 10; INC(i); val := val DIV 10
-		UNTIL val = 0;
-		DEC(i);
-		WHILE i >= 0 DO
-			str[j] := CHR(digits[i]+ORD("0")); INC(j); DEC(i)
-		END;
-		str[j] := 0X
-	END IntToStr;
-
-	PROCEDURE Find (VAR family, style: ARRAY OF CHAR; sppm: INTEGER; VAR fname: ARRAY OF CHAR; VAR fppm: INTEGER);
-		VAR
-			enum: Files.Enumerator; i, time, date, size: LONGINT; error, ppm: INTEGER;
-			s: ARRAY 4 OF CHAR; pattern: ARRAY 64 OF CHAR; name: Files.FileName; flags: SET;
-	BEGIN
-		fname[0] := 0X; fppm := 0;
-		error := MAX(INTEGER);
-		COPY(family, pattern); AppendCh(pattern, "*");
-		IF style = "Bold" THEN AppendCh(pattern, "b")
-		ELSIF style = "Italic" THEN AppendCh(pattern, "i")
-		ELSIF style = "Medium" THEN AppendCh(pattern, "m")
-		ELSIF style = "BoldItalic" THEN AppendCh(pattern, "j")
-		END;
-		Append(pattern, ".*.Fnt");
-		NEW(enum); enum.Open(pattern, {});
-		WHILE (error # 0) & enum.GetEntry(name, flags, time, date, size) DO
-			i := 0; ppm := 0;
-			WHILE (name[i] # 0X) & (name[i] # ".") & (name[i] < "0") OR ("9" < name[i]) DO INC(i) END;
-			WHILE ("0" <= name[i]) & (name[i] <= "9") DO
-				ppm := 10*ppm + ORD(name[i]) - ORD("0");
-				INC(i)
-			END;
-			IF ppm = 0 THEN ppm := 10 END;
-			IF (style = "") & (name[i] = ".") OR (CAP(style[0]) = CAP(name[i])) THEN
-				WHILE (name[i] # 0X) & (name[i] # ".") DO INC(i) END;
-				IF name[i] = "." THEN INC(i) END;
-				s[0] := name[i]; s[1] := name[i+1]; s[2] := name[i+2]; s[3] := 0X;
-				IF s = "Scn" THEN
-				ELSIF s = "Pr2" THEN ppm := SHORT(200 * LONG(ppm) DIV 91)
-				ELSIF s = "Pr3" THEN ppm := SHORT(300 * LONG(ppm) DIV 91)
-				ELSIF s = "Pr6" THEN ppm := SHORT(600 * LONG(ppm) DIV 91)
-				ELSE ppm := MIN(INTEGER)
-				END;
-				IF ABS(sppm - ppm) < error THEN
-					error := ABS(sppm - ppm); COPY(name, fname); fppm := ppm
-				END
-			END
-		END;
-		enum.Close
-	END Find;
-
-
-	(*--- Outlines ---*)
-
-	(* append path element to Bezier control points *)
-	PROCEDURE AddSplineElem (VAR data: GfxPaths.EnumData);
-		CONST
-			sqrt3 = 1.7320508; t = 4/3*(sqrt3 - 1);
-		VAR
-			rx, ry, trx, try: REAL;
-	BEGIN
-		WITH data: PathEnumData DO
-			CASE data.elem OF
-			| GfxPaths.Line:	(* spline is line *)
-				data.xc[data.n] := data.x; data.yc[data.n] := data.y; INC(data.n)
-			| GfxPaths.Arc:	(* spline is full circle *)
-				rx := data.x - data.x0; ry := data.y - data.y0;
-				trx := t * rx; try := t * ry;
-				data.xc[data.n] := data.x0 + rx - try; data.yc[data.n] := data.y0 + ry + trx; INC(data.n);
-				data.xc[data.n] := data.x0 - ry + trx; data.yc[data.n] := data.y0 + rx + try; INC(data.n);
-				data.xc[data.n] := data.x0 - ry; data.yc[data.n] := data.y0 + rx; INC(data.n);
-				data.xc[data.n] := data.x0 - ry - trx; data.yc[data.n] := data.y0 + rx + try; INC(data.n);
-				data.xc[data.n] := data.x0 - rx - try; data.yc[data.n] := data.y0 - ry + trx; INC(data.n);
-				data.xc[data.n] := data.x0 - rx; data.yc[data.n] := data.y0 - ry; INC(data.n);
-				data.xc[data.n] := data.x0 - rx + try; data.yc[data.n] := data.y0 - ry - trx; INC(data.n);
-				data.xc[data.n] := data.x0 + ry - trx; data.yc[data.n] := data.y0 - rx - try; INC(data.n);
-				data.xc[data.n] := data.x0 + ry; data.yc[data.n] := data.y0 - rx; INC(data.n);
-				data.xc[data.n] := data.x0 + ry + trx; data.yc[data.n] := data.y0 - rx + try; INC(data.n);
-				data.xc[data.n] := data.x0 + rx + try; data.yc[data.n] := data.y0 + ry - trx; INC(data.n);
-				data.xc[data.n] := data.x0 + rx; data.yc[data.n] := data.y0 + ry; INC(data.n)
-			| GfxPaths.Bezier:
-				data.xc[data.n] := data.x1; data.yc[data.n] := data.y1; INC(data.n);
-				data.xc[data.n] := data.x2; data.yc[data.n] := data.y2; INC(data.n);
-				data.xc[data.n] := data.x; data.yc[data.n] := data.y; INC(data.n)
-			END
-		END
-	END AddSplineElem;
-
-	(* convert natural spline to Bezier control points *)
-	PROCEDURE SplineToBezier (VAR x, y: ARRAY OF REAL; VAR n: LONGINT; closed: BOOLEAN);
-		VAR data: PathEnumData;
-	BEGIN
-		data.n := 1; data.x := x[0]; data.y := y[0];
-		GfxPaths.EnumSpline(x, y, SHORT(n), closed, AddSplineElem, data);
-		n := 1;
-		WHILE n < data.n DO
-			x[n] := data.xc[n]; y[n] := data.yc[n]; INC(n)
-		END
-	END SplineToBezier;
-
-	(* convert Bezier2 to Bezier *)
-	PROCEDURE Bezier2ToBezier (VAR x, y: ARRAY OF REAL; VAR n: LONGINT);
-		VAR nout, m: LONGINT;
-	BEGIN
-		IF ODD(n) THEN
-			nout := (n - 1) DIV 2 * 3 + 1;
-			m := nout
-		ELSE	(* ends with line *)
-			nout := (n - 2) DIV 2 * 3 + 2;
-			m := nout-1;
-			x[m] := x[n-1]; y[m] := y[n-1]
-		END;
-		WHILE m > 0 DO
-			DEC(m); DEC(n);
-			x[m] := x[n]; y[m] := y[n];
-			DEC(m); DEC(n);
-			x[m] := (1/3)*(2*x[n] + x[m+1]); y[m] := (1/3)*(2*y[n] + y[m+1]);
-			DEC(m);
-			x[m] := (1/3)*(2*x[n] + x[n-1]); y[m] := (1/3)*(2*y[n] + y[n-1])
-		END;
-		n := nout
-	END Bezier2ToBezier;
-
-	(* load character outlines *)
-		PROCEDURE LoadOutline (outline: Outline; VAR r: Files.Reader);
-		CONST
-			polygon = 0; bezier = 1; spline = 2; bezier2 = 3;
-			maxNofContours = 128;
-
-		VAR
-			minY, maxY, base, i, y, ntypes, nchars, x, left, ncontours, n, m, cont, k: LONGINT; scale: REAL; ch: CHAR;
-			type, pred, succ, last: ARRAY maxNofContours OF LONGINT; str: ARRAY 32 OF CHAR; kind: ARRAY 5 OF INTEGER;
-			closed: BOOLEAN; px, py: POINTER TO ARRAY maxNofContours, MaxBezierPoints OF REAL;
-			done: ARRAY maxNofContours OF BOOLEAN;
-
-		PROCEDURE coincident (px, py, qx, qy: REAL; dist: LONGINT): BOOLEAN;
-		BEGIN
-			RETURN (ABS(px - qx) <= dist) & (ABS(py - qy) <= dist)
-		END coincident;
-
-	BEGIN
-		minY := MAX(LONGINT); maxY := MIN(LONGINT); base := minY;
-		FOR i := 1 TO 5 DO
-			r.RawNum(y);
-			IF y > maxY THEN maxY := y END;
-			IF y < minY THEN base := minY; minY := y
-			ELSIF y < base THEN base := y
-			END
-		END;
-		scale := 1/(maxY - minY);
-
-		NEW(outline.path);
-		outline.path.Clear();
-		outline.xmin := MAX(REAL); outline.ymin := MAX(REAL);
-		outline.xmax := MIN(REAL); outline.ymax := MIN(REAL);
-		NEW(px); NEW(py);
-		ntypes := 1;
-		r.RawNum(nchars);
-		WHILE nchars > 0 DO
-			DEC(nchars);
-			r.Char(ch); r.RawNum(x); left := x;
-			r.RawNum(x);
-			IF x >= left THEN
-				outline.width[ORD(ch)] := scale * SHORT(x - left)
-			ELSE
-				outline.width[ORD(ch)] := scale * SHORT(left - x);
-				left := x
-			END;
-
-			(* read contour curves *)
-			r.RawNum(ncontours);
-			n := 0;
-			WHILE n < ncontours DO
-				r.RawNum(type[n]);
-				IF type[n] = ntypes THEN
-					r.RawString(str);
-					ASSERT(str = "Graphic");
-					r.RawString(str);
-					IF str = "PolygonDesc" THEN kind[type[n]] := polygon
-					ELSIF str = "BezierDesc" THEN kind[type[n]] := bezier
-					ELSIF str = "SplineDesc" THEN kind[type[n]] := spline
-					ELSIF str = "Bezier2Desc" THEN kind[type[n]] := bezier2
-					ELSE HALT(101)
-					END;
-					INC(ntypes)
-				END;
-				r.RawBool(closed);
-				IF closed THEN pred[n] := n; succ[n] := n
-				ELSE pred[n] := -1; succ[n] := -1
-				END;
-				r.RawNum(m);
-				DEC(m);
-				FOR i := 0 TO m DO
-					r.RawNum(x); r.RawNum(y);
-					px[n, i] := x - left; py[n, i] := y - base
-				END;
-				IF m < 1 THEN
-					DEC(ncontours)
-				ELSE
-					IF closed THEN
-						INC(m); px[n, m] := px[n, 0]; py[n, m] := py[n, 0]
-					END;
-					IF kind[type[n]] = spline THEN
-						INC(m);
-						SplineToBezier(px[n], py[n], m, closed);
-						DEC(m)
-					ELSIF kind[type[n]] = bezier2 THEN
-						INC(m);
-						Bezier2ToBezier(px[n], py[n], m);
-						DEC(m)
-					END;
-					FOR i := 0 TO m DO
-						IF px[n, i] < outline.xmin THEN outline.xmin := px[n, i] END;
-						IF px[n, i] > outline.xmax THEN outline.xmax := px[n, i] END;
-						IF py[n, i] < outline.ymin THEN outline.ymin := py[n, i] END;
-						IF py[n, i] > outline.ymax THEN outline.ymax := py[n, i] END
-					END;
-					last[n] := m;
-					INC(n)
-				END
-			END;
-			outline.len[ORD(ch)] := SHORT(SHORT(ncontours));
-
-			(* find connected curves *)
-			FOR i := 0 TO 3 DO
-				n := 0;
-				WHILE n < outline.len[ORD(ch)] DO
-					m := n + 1;
-					WHILE (pred[n] < 0) & (m < outline.len[ORD(ch)]) DO
-						IF (succ[m] < 0) & coincident(px[n, 0], py[n, 0], px[m, last[m]], py[m, last[m]], i) THEN
-							px[m, last[m]] := px[n, 0]; py[m, last[m]] := py[n, 0];
-							pred[n] := m; succ[m] := n
-						END;
-						INC(m)
-					END;
-					m := n + 1;
-					WHILE (succ[n] < 0) & (m < outline.len[ORD(ch)]) DO
-						IF (pred[m] < 0) & coincident(px[n, last[n]], py[n, last[n]], px[m, 0], py[m, 0], i) THEN
-							px[n, last[n]] := px[m, 0]; py[n, last[n]] := py[m, 0];
-							succ[n] := m; pred[m] := n
-						END;
-						INC(m)
-					END;
-					INC(n)
-				END
-			END;
-			FOR cont := 0 TO outline.len[ORD(ch)] - 1 DO
-				(*done[cont] := (succ[cont] < 0) OR (pred[cont] < 0)*)	(* ignore open curves *)
-				done[cont] := FALSE
-			END;
-
-			(* append contour curves to path *)
-			outline.pos[ORD(ch)] := outline.path.elems;
-			cont := 0; k := 0;
-			WHILE cont < outline.len[ORD(ch)] DO
-				IF ~done[cont] THEN
-					n := cont; m := pred[n];
-					IF m < 0 THEN
-						outline.path.AddEnter(scale * px[n, 0], scale * py[n, 0], 0, 0)
-					ELSE
-						i := last[m];
-						outline.path.AddEnter(scale * px[n, 0], scale * py[n, 0], scale * (px[m, i] - px[m, i - 1]), scale * (py[m, i] - py[m, i - 1]))
-					END;
-					REPEAT
-						i := 1;
-						WHILE i <= last[n] DO
-							IF (kind[type[n]] = polygon) OR (i+2 > last[n]) THEN
-								outline.path.AddLine(scale * px[n, i], scale * py[n, i]);
-								INC(i)
-							ELSE
-								outline.path.AddBezier(scale * px[n, i+2], scale * py[n, i+2], scale * px[n, i], scale * py[n, i],
-								  scale * px[n, i+1], scale * py[n, i+1]);
-								INC(i, 3)
-							END
-						END;
-						done[n] := TRUE;
-						n := succ[n]
-					UNTIL (n < 0) OR (n = cont);
-					IF n < 0 THEN
-						outline.path.AddExit(0, 0)
-					ELSE
-						outline.path.AddExit(scale * (px[n, 1] - px[n, 0]), scale * (py[n, 1] - py[n, 0]))
-					END;
-					INC(k)
-				END;
-				INC(cont)
-			END;
-			outline.len[ORD(ch)] := SHORT(SHORT(k))
-		END;
-		outline.xmin := scale * outline.xmin; outline.ymin := scale * outline.ymin;
-		outline.xmax := scale * outline.xmax; outline.ymax := scale * outline.ymax
-	END LoadOutline;
-
-
-	(*--- Font Cache ---*)
-
-	(* enter font in font cache *)
-	PROCEDURE CacheFont (font: Font);
-	BEGIN {EXCLUSIVE}
-		font.prev := Cache.prev; Cache.prev.next := font;
-		font.next := Cache; Cache.prev := font
-	END CacheFont;
-
-	(* put character into cache *)
-	PROCEDURE CacheChar (font: Font; ch: CHAR; x, y, dx, dy: REAL; map: Raster.Image);
-		VAR char: Char; n, m: LONGINT;
-	BEGIN {EXCLUSIVE}
-		NEW(char); font.char[ORD(ch)] := char;
-		char.x := x; char.y := y; char.dx := dx; char.dy := dy; char.map := map;
-		INC(Chars); char.used := 4;	(* extra bonus for new character in cache *)
-		WHILE Chars = MaxCachedChars DO
-			font := Cache.next;
-			WHILE font # Cache DO
-				n := 0; m := 0;
-				WHILE n < 256 DO
-					char := font.char[n];
-					IF char # NIL THEN
-						char.used := char.used DIV 2;	(* age number of uses *)
-						IF char.used = 0 THEN	(* remove character from cache *)
-							DEC(Chars); font.char[n] := NIL
-						ELSE
-							INC(m)
-						END
-					END;
-					INC(n)
-				END;
-				IF m = 0 THEN	(* no characters cached => remove font from cache *)
-					font.prev.next := font.next; font.next.prev := font.prev
-				END;
-				font := font.next
-			END
-		END
-	END CacheChar;
-
-	(* return cached character *)
-	PROCEDURE CachedChar (font: Font; ch: CHAR): Char;
-		VAR char: Char;
-	BEGIN {EXCLUSIVE}
-		char := font.char[ORD(ch)];
-		IF char # NIL THEN INC(char.used) END;
-		RETURN char
-	END CachedChar;
-
-
-	(**--- Fonts ---**)
-
-	(* extract family and style from font name *)
-	PROCEDURE SplitName (name: ARRAY OF CHAR; VAR fam, style: ARRAY OF CHAR);
-		VAR i, j: LONGINT;
-	BEGIN
-		fam[0] := name[0];
-		i := 1;
-		WHILE (name[i] >= "a") & (name[i] <= "z") DO
-			fam[i] := name[i];
-			INC(i)
-		END;
-		fam[i] := 0X;
-		WHILE (name[i] >= "0") & (name[i] <= "9") DO INC(i) END;
-		IF (name[i] = "-") OR (name[i] = " ") THEN INC(i) END;
-		j := 0;
-		WHILE (name[i] # 0X) & (CAP(name[i]) >= "A") & (CAP(name[i]) <= "Z") DO
-			style[j] := name[i];
-			INC(i); INC(j)
-		END;
-		IF j = 1 THEN
-			CASE CAP(style[0]) OF
-			| "I": COPY("Italic", style)
-			| "B": COPY("Bold", style)
-			| "M": COPY("Medium", style)
-			| "J": COPY("BoldItalic", style)
-			ELSE style[1] := 0X
-			END
-		ELSE
-			style[j] := 0X
-		END
-	END SplitName;
-
-	(* create font name from family and style *)
-	PROCEDURE BuildName (fam, style: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
-	BEGIN
-		COPY(fam, name);
-		IF style # "" THEN
-			AppendCh(name, "-");
-			Append(name, style)
-		END
-	END BuildName;
-
-	(* open MetaFont *)
-	PROCEDURE OpenOutline (VAR family, style: ARRAY OF CHAR): Outline;
-		VAR fname: FontName; file: Files.File; r: Files.Reader; tag: INTEGER; outline: Outline;
-	BEGIN
-		COPY(family, fname); Append(fname, style); Append(fname, ".MTF");
-		file := Files.Old(fname);
-		IF file # NIL THEN
-			Files.OpenReader(r, file, 0);
-			r.RawInt(tag);
-			IF (tag = OldMetaFontTag) OR (tag = MetaFontTag) THEN
-				NEW(outline); LoadOutline(outline, r);
-				RETURN outline
-			END
-		END;
-		RETURN NIL
-	END OpenOutline;
-
-	PROCEDURE LoadRaster (VAR name: ARRAY OF CHAR): RasterFile;
-		VAR
-			rfile: RasterFile; file: Files.File; r: Files.Reader; id, ch: CHAR; type: SHORTINT; height, runs, i, j: INTEGER;
-			beg, end: ARRAY 32 OF INTEGER; size: LONGINT; adr: ADDRESS;
-	BEGIN
-		rfile := NIL;
-		file := Files.Old(name);
-		IF file = NIL THEN RETURN NIL END;
-		Files.OpenReader(r, file, 0);
-		r.Char(id); r.RawSInt(type);
-		IF (id = FontId) & (type = 0) THEN
-			NEW(rfile);
-			r.Char(ch); r.Char(ch); r.RawInt(height);
-			r.RawInt(rfile.xmin); r.RawInt(rfile.xmax);
-			r.RawInt(rfile.ymin); r.RawInt(rfile.ymax);
-			r.RawInt(runs);
-			i := 0;
-			WHILE i < runs DO
-				r.RawInt(beg[i]); r.RawInt(end[i]); INC(i)
-			END;
-			i := 0; size := 0;
-			WHILE i < runs DO
-				j := beg[i];
-				WHILE j < end[i] DO
-					NEW(rfile.char[j]);
-					r.RawInt(rfile.char[j].dx);
-					r.RawInt(rfile.char[j].x); r.RawInt(rfile.char[j].y);
-					r.RawInt(rfile.char[j].w); r.RawInt(rfile.char[j].h);
-					size := size + (rfile.char[j].w + 7) DIV 8 * rfile.char[j].h;
-					INC(j)
-				END;
-				INC(i)
-			END;
-			NEW(rfile.mem, size);
-			i := 0; adr := ADDRESSOF(rfile.mem[0]);
-			WHILE i < runs DO
-				j := beg[i];
-				WHILE j < end[i] DO
-					rfile.char[j].adr := adr;
-					size := (rfile.char[j].w + 7) DIV 8 * rfile.char[j].h;
-					WHILE size > 0 DO
-						r.Char(ch);
-						SYSTEM.PUT(adr, ch);
-						INC(adr); DEC(size)
-					END;
-					INC(j)
-				END;
-				INC(i)
-			END
-		END;
-		RETURN rfile
-	END LoadRaster;
-
-	(* open raster font *)
-	PROCEDURE OpenRaster (VAR family, style: ARRAY OF CHAR; ptsize: INTEGER; VAR mat: GfxMatrix.Matrix; outline: Outline): Font;
-		VAR
-			rfile: RasterFile; font: Font; scale, xmin, ymin, xmax, ymax: REAL; ppm, fppm: INTEGER;
-			ext, pstr: ARRAY 9 OF CHAR; name: FontName;
-	BEGIN
-		rfile := NIL; font := NIL;
-		scale := Math.sqrt(ABS(GfxMatrix.Det(mat)));
-
-		(* look for exactly matching raster font *)
-		IF scale < 2.5 THEN ppm := SHORT(ENTIER(ptsize * scale + 0.5)); ext := ".Scn.Fnt"
-		ELSIF scale < 4.5 THEN ppm := SHORT(ENTIER(ptsize * scale * DPI/300 + 0.5)); ext := ".Pr3.Fnt"
-		ELSE ppm := SHORT(ENTIER(ptsize * scale * DPI/600 + 0.5)); ext := ".Pr6.Fnt"
-		END;
-		COPY(family, name); fppm := ppm;
-		IntToStr(ppm, pstr); Append(name, pstr);
-		IF style = "BoldItalic" THEN AppendCh(name, "j")
-		ELSIF style # "" THEN AppendCh(name, CHR(ORD(CAP(style[0])) - ORD("A") + ORD("a")))
-		END;
-		Append(name, ext);
-		rfile := LoadRaster(name);
-
-		(* check available raster font files *)
-		IF rfile = NIL THEN
-			ppm := SHORT(ENTIER(ptsize * scale + 0.5));
-			Find(family, style, ppm, name, fppm);
-			IF name # "" THEN
-				rfile := LoadRaster(name)
-			END
-		END;
-
-		IF rfile # NIL THEN	(* have raster font of requested family *)
-			IF (fppm = ppm) & ~GfxMatrix.Rotated(mat) & (mat[0, 0] > 0) & (mat[1, 1] > 0) & (mat[0, 0] = mat[1, 1]) THEN
-				NEW(font); font.outline := outline; font.rfile := rfile; font.niceMaps := (outline = NIL) OR (scale < 5);
-				IF outline = NIL THEN font.class := FClass
-				ELSE font.class := OFClass
-				END;
-				font.xmin := rfile.xmin; font.ymin := rfile.ymin; font.xmax := rfile.xmax; font.ymax := rfile.ymax
-			ELSIF (outline = NIL) OR (scale < 2) THEN	(* use warped raster font *)
-				NEW(font); font.outline := outline; font.rfile := rfile; font.niceMaps := TRUE;
-				IF outline = NIL THEN font.class := WFClass
-				ELSE font.class := OWFClass
-				END;
-				scale := 1/scale * ppm/fppm;
-				GfxMatrix.Scale(mat, scale, scale, font.wmat);
-				GfxMatrix.ApplyToRect(font.wmat, rfile.xmin, rfile.ymin, rfile.xmax, rfile.ymax, xmin, ymin, xmax, ymax);
-				font.xmin := SHORT(ENTIER(xmin)); font.ymin := SHORT(ENTIER(ymin));
-				font.xmax := -SHORT(ENTIER(-xmax)); font.ymax := -SHORT(ENTIER(-ymax))
-			END
-		END;
-
-		IF (font = NIL) & (outline # NIL) THEN	(* use outline only, no raster *)
-			NEW(font); font.class := OClass; font.outline := outline; font.niceMaps := FALSE;
-			scale := ptsize * DPI/72.27;	(* scale to display resolution *)
-			GfxMatrix.Scale(mat, scale, scale, font.wmat);
-			GfxMatrix.ApplyToRect(font.wmat, outline.xmin, outline.ymin, outline.xmax, outline.ymax, xmin, ymin, xmax, ymax);
-			font.xmin := SHORT(ENTIER(xmin)); font.ymin := SHORT(ENTIER(ymin));
-			font.xmax := -SHORT(ENTIER(-xmax)); font.ymax := -SHORT(ENTIER(-ymax))
-		END;
-
-		RETURN font
-	END OpenRaster;
-
-	PROCEDURE OpenExtension (VAR family, style: ARRAY OF CHAR; ptsize: INTEGER; VAR m: GfxMatrix.Matrix): Font;
-		VAR
-			i, j, n, res: LONGINT;
-			enum: Files.Enumerator; time, date, size: LONGINT;  continue: BOOLEAN;
-			name: Files.FileName; msg, cmd: ARRAY 64 OF CHAR; flags: SET;
-	BEGIN
-		cmd := "";
-		NEW(enum); enum.Open(family, {});
-		continue := TRUE;
-		WHILE continue & enum.GetEntry(name, flags, time, date, size) DO
-			i := 0; j := 0;
-			WHILE name[i] # 0X DO
-				IF name[i] = "." THEN j := i END;
-				INC(i)
-			END;
-			IF j > 0 THEN
-				msg := "FontFormats"; i := 11;
-				WHILE name[j] # 0X DO msg[i] := name[j]; INC(i); INC(j) END;
-				Configuration.Get(msg, cmd, res);
-				continue := (res = Configuration.Ok);
-			END
-		END;
-		enum.Close;
-			(* if found, invoke it *)
-		IF cmd # "" THEN
-			OpenProc := NIL;
-			Commands.Call (cmd, {Commands.Wait}, res, msg);
-			IF res = Commands.Ok THEN
-				IF OpenProc # NIL THEN
-					RETURN OpenProc(family, style, ptsize, m)
-				END
-			ELSE
-				KernelLog.Enter; KernelLog.String("GfxFonts: "); KernelLog.String(msg); KernelLog.Exit
-			END
-		END;
-		RETURN NIL
-	END OpenExtension;
-
-	(**
-		open font given point size and transformation matrix. the transformation is applied to the display font at that size.
-		the preferred way to specify a font is "Family-Style" (e.g. "Oberon-Bold"), although others are accepted as well
-		("OberonBold", "Oberon10b")
-	**)
-	PROCEDURE Open* (name: ARRAY OF CHAR; ptsize: INTEGER; mat: GfxMatrix.Matrix): Font;
-		VAR family, style, fname: FontName; font, cand: Font;
-	BEGIN
-		mat[2, 0] := 0; mat[2, 1] := 0;
-		SplitName(name, family, style);
-		BuildName(family, style, fname);
-
-		(* search in cache *)
-		font := Cache.next; cand := NIL;
-		WHILE font # Cache DO
-			IF font.name = fname THEN
-				cand := font;	(* keep for deriving font *)
-				IF (ptsize = font.ptsize) & GfxMatrix.Equal(font.mat, mat) THEN
-					RETURN font
-				END
-			END;
-			font := font.next
-		END;
-
-		(* derive from existing font if possible *)
-		IF cand # NIL THEN
-			font := cand.class.derive(cand, ptsize, mat);
-			IF font # NIL THEN
-				COPY(fname, font.name); font.ptsize := ptsize; font.mat := mat;
-				CacheFont(font);
-				RETURN font
-			END
-		END;
-
-		(* try standard raster/outline fonts *)
-		font := OpenRaster(family, style, ptsize, mat, OpenOutline(family, style));
-		IF font # NIL THEN
-			COPY(fname, font.name); font.ptsize := ptsize; font.mat := mat;
-			CacheFont(font);
-			RETURN font
-		END;
-
-		(* try registered font formats *)
-		font := OpenExtension(family, style, ptsize, mat);
-		IF font # NIL THEN
-			COPY(fname, font.name); font.ptsize := ptsize; font.mat := mat;
-			CacheFont(font);
-			RETURN font
-		END;
-
-		RETURN NIL
-	END Open;
-
-	(** open font of specified point size at display resolution **)
-	PROCEDURE OpenSize* (name: ARRAY OF CHAR; ptsize: INTEGER): Font;
-	BEGIN
-		RETURN Open(name, ptsize, GfxMatrix.Identity)
-	END OpenSize;
-
-	(** return character advance vector **)
-	PROCEDURE GetWidth* (font: Font; ch: CHAR; VAR dx, dy: REAL);
-		VAR char: Char;
-	BEGIN
-		char := CachedChar(font, ch);
-		IF char # NIL THEN
-			dx := char.dx; dy := char.dy
-		ELSE
-			font.class.getwidth(font, ch, dx, dy)
-		END
-	END GetWidth;
-
-	(** return character bitmap **)
-	PROCEDURE GetMap* (font: Font; ch: CHAR; VAR x, y, dx, dy: REAL; VAR map: Raster.Image);
-		VAR char: Char;
-	BEGIN
-		char := CachedChar(font, ch);
-		IF char # NIL THEN
-			x := char.x; y := char.y; dx := char.dx; dy := char.dy; map := char.map
-		ELSE
-			font.class.getmap(font, ch, x, y, dx, dy, map);
-			CacheChar(font, ch, x, y, dx, dy, map)
-		END
-	END GetMap;
-
-	(** store character outline rooted at given position in given path **)
-	PROCEDURE GetOutline* (font: Font; ch: CHAR; x, y: REAL; path: GfxPaths.Path);
-	BEGIN
-		font.class.getoutline(font, ch, x, y, path)
-	END GetOutline;
-
-	(** compute advance vector for complete string **)
-	PROCEDURE GetStringWidth* (font: Font; str: ARRAY OF CHAR; VAR dx, dy: REAL);
-		VAR i: LONGINT; ddx, ddy: REAL;
-	BEGIN
-		i := 0; dx := 0; dy := 0;
-		WHILE str[i] # 0X DO
-			GetWidth(font, str[i], ddx, ddy);
-			dx := dx + ddx; dy := dy + ddy;
-			INC(i)
-		END
-	END GetStringWidth;
-
-
-	(*--- Derive Methods ---*)
-
-	(* cannot derive font if no outline is known *)
-	PROCEDURE FDerive (font: Font; ptsize: INTEGER; VAR mat: GfxMatrix.Matrix): Font;
-	BEGIN
-		RETURN NIL
-	END FDerive;
-
-	(* derive font with same outline *)
-	PROCEDURE ODerive (font: Font; ptsize: INTEGER; VAR mat: GfxMatrix.Matrix): Font;
-		VAR family, style: FontName;
-	BEGIN
-		SplitName(font.name, family, style);
-		RETURN OpenRaster(family, style, ptsize, mat, font.outline)
-	END ODerive;
-
-
-	(*--- GetWidth Methods ---*)
-
-	(* ... from raster font *)
-	PROCEDURE FGetWidth (font: Font; ch: CHAR; VAR dx, dy: REAL);
-		VAR rfile: RasterFile;
-	BEGIN
-		rfile := font.rfile;
-		IF rfile.char[ORD(ch)] # NIL THEN dx := rfile.char[ORD(ch)].dx ELSE dx :=0 END; dy := 0
-	END FGetWidth;
-
-	(* ... from warped raster font *)
-	PROCEDURE WFGetWidth (font: Font; ch: CHAR; VAR dx, dy: REAL);
-	BEGIN
-		FGetWidth(font, ch, dx, dy);
-		dy := dx * font.wmat[0, 1];
-		dx := dx * font.wmat[0, 0]
-	END WFGetWidth;
-
-	(* ... from outline *)
-	PROCEDURE OGetWidth (font: Font; ch: CHAR; VAR dx, dy: REAL);
-		VAR w: REAL;
-	BEGIN
-		w := font.outline.width[ORD(ch)];
-		dx := w * font.wmat[0, 0]; dy := w * font.wmat[0, 1]
-	END OGetWidth;
-
-
-	(*--- GetMap Methods ---*)
-
-	PROCEDURE WarpMap (src: Raster.Image; mat: GfxMatrix.Matrix; VAR x, y: REAL; VAR dst: Raster.Image);
-		VAR x0, y0, x1, y1: REAL; w, h: LONGINT; filter: GfxImages.Filter;
-	BEGIN
-		GfxImages.InitLinearFilter(filter);
-		GfxMatrix.Apply(mat, x, y, x, y);
-		x0 := 0; y0 := 0; x1 := 0; y1 := 0;
-		IF mat[0, 0] > 0 THEN x1 := src.width * mat[0, 0] ELSE x0 := src.width * mat[0, 0] END;
-		IF mat[0, 1] > 0 THEN y1 := src.width * mat[0, 1] ELSE y0 := src.width * mat[0, 1] END;
-		IF mat[1, 0] > 0 THEN x1 := x1 + src.height * mat[1, 0] ELSE x0 := x0 + src.height * mat[1, 0] END;
-		IF mat[1, 1] > 0 THEN y1 := y1 + src.height * mat[1, 1] ELSE y0 := y0 + src.height * mat[1, 1] END;
-		mat[2, 0] := -x0; mat[2, 1] := -y0;
-		x := x + x0; y := y + y0;
-		w := -ENTIER(-x1) - ENTIER(x0); h := -ENTIER(-y1) - ENTIER(y0);
-		IF w * h # 0 THEN
-			NEW(dst); Raster.Create(dst, w, h, Raster.A8);
-			GfxImages.Transform(src, dst, mat, filter)
-		ELSE
-			dst := NIL
-		END
-	END WarpMap;
-
-	(* ... from raster font *)
-	PROCEDURE FGetMap (font: Font; ch: CHAR; VAR x, y, dx, dy: REAL; VAR map: Raster.Image);
-		VAR char: RasterChar; stride: LONGINT;
-	BEGIN
-		char := font.rfile.char[ORD(ch)];
-		IF char = NIL THEN
-			dx := 0; dy := 0; x := 0; y := 0; map := NIL
-		ELSE
-			dx := char.dx; dy := 0;
-			IF char.w * char.h = 0 THEN
-				x := 0; y := 0; map := NIL
-			ELSE
-				x := char.x; y := -char.h-char.y; stride:=(char.w+7) DIV 8;
-				NEW(map); Raster.Init(map, char.w, char.h, Raster.A1, -stride, char.adr+(char.h-1)*stride)
-			END
-		END
-	END FGetMap;
-
-	(* ... by warping raster font *)
-	PROCEDURE WFGetMap (font: Font; ch: CHAR; VAR x, y, dx, dy: REAL; VAR map: Raster.Image);
-	BEGIN
-		FGetMap(font, ch, x, y, dx, dy, map);
-		dy := dx * font.wmat[0, 1];
-		dx := dx * font.wmat[0, 0];
-		IF map # NIL THEN
-			WarpMap(map, font.wmat, x, y, map)
-		END
-	END WFGetMap;
-
-	PROCEDURE AddElem (VAR data: GfxPaths.EnumData);
-		VAR px, py, x, y, xstep, ystep, steps: INTEGER; dx, ex, dy, ey, e: REAL;
-	BEGIN
-		WITH data: PathEnumData DO
-			CASE data.elem OF
-			| GfxPaths.Enter:
-				data.lx := data.x; data.ly := data.y;
-				data.px := SHORT(ENTIER(data.x + 0.5)); data.py := SHORT(ENTIER(data.y + 0.5))
-			| GfxPaths.Line:
-				px := SHORT(ENTIER(data.x + 0.5)); py := SHORT(ENTIER(data.y + 0.5));
-				x := data.px; y := data.py;
-				IF py = y THEN	(* horizontal line => ignore *)
-					data.px := px
-				ELSE
-					dx := data.x - data.lx; dy := data.y - data.ly;
-					IF dx >= 0 THEN xstep := 1; ex := data.lx - x
-					ELSE xstep := -1; dx := -dx; ex := x - data.lx
-					END;
-					IF dy >= 0 THEN ystep := 1; ey := data.ly - y
-					ELSE ystep := -1; dy := -dy; ey := y - data.ly
-					END;
-					e := dx * ey - dy * ex + 0.5 * (dy - dx);
-					steps := ABS(px - x) + ABS(py - y);
-					WHILE steps > 0 DO
-						IF (e >= 0) & ((e > 0) OR (xstep <= 0)) THEN
-							INC(y, ystep); e := e - dx;
-							data.region.AddPoint(x, y, ystep)
-						ELSE
-							INC(x, xstep); e := e + dy
-							(* don't have to insert point here because regions are sliced horizontally *)
-						END;
-						DEC(steps)
-					END;
-					data.px := px; data.py := py
-				END;
-				data.lx := data.x; data.ly := data.y
-			ELSE	(* ignore other elements *)
-			END
-		END
-	END AddElem;
-
-	PROCEDURE FillRect (llx, lly, urx, ury: INTEGER; VAR data: GfxRegions.EnumData);
-		VAR pix: Raster.Pixel; mode: Raster.Mode;
-	BEGIN
-		WITH data: RegEnumData DO
-			pix[Raster.a] := 0FFX;
-			Raster.InitMode(mode, Raster.srcCopy);
-			Raster.Fill(data.map, llx - data.dx, lly - data.dy, urx - data.dx, ury - data.dy, pix, mode)
-		END
-	END FillRect;
-
-	(* ... by filling interior of warped outline *)
-	PROCEDURE OGetMap (font: Font; ch: CHAR; VAR x, y, dx, dy: REAL; VAR map: Raster.Image);
-		VAR w: REAL; pathdata: PathEnumData; llx, lly, urx, ury: INTEGER; regdata: RegEnumData;
-		tmpPath: GfxPaths.Path; tmpRegion: GfxRegions.Region; (* was global --> now local for concurrency ... cost? *)
-	BEGIN
-		NEW(tmpPath);
-		NEW(tmpRegion); tmpRegion.Init(GfxRegions.Winding);
-		w := font.outline.width[ORD(ch)];
-		dx := w * font.wmat[0, 0]; dy := w * font.wmat[0, 1];
-		font.class.getoutline(font, ch, 0, 0, tmpPath);
-		tmpRegion.Clear();
-		pathdata.region := tmpRegion;
-		tmpPath.EnumFlattened(0.5, AddElem, pathdata);
-		IF tmpRegion.Empty() THEN
-			x := 0; y := 0; map := NIL
-		ELSE
-			llx := tmpRegion.llx; lly := tmpRegion.lly; urx := tmpRegion.urx; ury := tmpRegion.ury;
-			NEW(map); Raster.Create(map, urx - llx, ury - lly, Raster.A1);
-			regdata.map := map; regdata.dx := llx; regdata.dy := lly;
-			tmpRegion.Enumerate(llx, lly, urx, ury, FillRect, regdata);
-			x := llx; y := lly
-		END
-	END OGetMap;
-
-
-	(*--- GetOutline Methods ---*)
-
-	(* ... undefined outline for raster fonts *)
-	PROCEDURE FGetOutline (font: Font; ch: CHAR; x, y: REAL; path: GfxPaths.Path);
-		VAR rfile: RasterFile; w, h: INTEGER; l: REAL;
-	BEGIN
-		path.Clear();
-		rfile := font.rfile;
-		w := rfile.char[ORD(ch)].w; h := rfile.char[ORD(ch)].h;
-		IF w * h > 0 THEN
-			x := x + rfile.char[ORD(ch)].x; y := y + rfile.char[ORD(ch)].y;
-			l := 0.1*(rfile.ymax - rfile.ymin);
-			path.AddRect(x, y, x + w, y + h);
-			path.AddRect(x + l, y + h - l, x + w - l, y + l)
-		END
-	END FGetOutline;
-
-	(* ... undefined outline for warped raster fonts *)
-	PROCEDURE WFGetOutline (font: Font; ch: CHAR; x, y: REAL; path: GfxPaths.Path);
-		VAR rfile: RasterFile; w, h, bx, by: INTEGER; l: REAL; m: GfxMatrix.Matrix;
-	BEGIN
-		path.Clear();
-		rfile := font.rfile;
-		w := rfile.char[ORD(ch)].w; h := rfile.char[ORD(ch)].h;
-		IF w * h > 0 THEN
-			bx := rfile.char[ORD(ch)].x; by := rfile.char[ORD(ch)].y;
-			l := 0.1*(rfile.ymax - rfile.ymin);
-			path.AddRect(bx, by, bx + w, by + h);
-			path.AddRect(bx + l, by + h - l, bx + w - l, by + l);
-			m := font.wmat; m[2, 0] := m[2, 0] + x; m[2, 1] := m[2, 1] + y;
-			path.Apply(m)
-		END
-	END WFGetOutline;
-
-	(* ... from outline structure *)
-	PROCEDURE OGetOutline (font: Font; ch: CHAR; x, y: REAL; path: GfxPaths.Path);
-		VAR outline: Outline; len: LONGINT; mat: GfxMatrix.Matrix; s: GfxPaths.Scanner; scale, dx, dy, x0, y0, x1, y1, x2, y2: REAL;
-	BEGIN
-		path.Clear();
-		outline := font.outline; len := outline.len[ORD(ch)];
-		IF len > 0 THEN
-			scale := font.ptsize * DPI/72.27;
-			GfxMatrix.Scale(font.mat, scale, scale, mat);
-			mat[2, 0] := mat[2, 0] + x; mat[2, 1] := mat[2, 1] + y;
-			s.Open(outline.path, outline.pos[ORD(ch)]);
-			REPEAT
-				CASE s.elem OF
-				| GfxPaths.Enter:
-					GfxMatrix.Apply(mat, s.x, s.y, x, y); GfxMatrix.ApplyToVector(mat, s.dx, s.dy, dx, dy);
-					path.AddEnter(x, y, dx, dy)
-				| GfxPaths.Line:
-					GfxMatrix.Apply(mat, s.x, s.y, x, y);
-					path.AddLine(x, y)
-				| GfxPaths.Arc:
-					GfxMatrix.Apply(mat, s.x, s.y, x, y); GfxMatrix.Apply(mat, s.x0, s.y0, x0, y0);
-					GfxMatrix.Apply(mat, s.x1, s.y1, x1, y1); GfxMatrix.Apply(mat, s.x2, s.y2, x2, y2);
-					path.AddArc(x, y, x0, y0, x1, y1, x2, y2)
-				| GfxPaths.Bezier:
-					GfxMatrix.Apply(mat, s.x, s.y, x, y);
-					GfxMatrix.Apply(mat, s.x1, s.y1, x1, y1); GfxMatrix.Apply(mat, s.x2, s.y2, x2, y2);
-					path.AddBezier(x, y, x1, y1, x2, y2)
-				| GfxPaths.Exit:
-					GfxMatrix.Apply(mat, s.dx, s.dy, dx, dy);
-					path.AddExit(dx, dy);
-					DEC(len)
-				END;
-				s.Scan()
-			UNTIL len = 0
-		END
-	END OGetOutline;
-
-	PROCEDURE InitClasses;
-	BEGIN
-		NEW(FClass); FClass.derive := FDerive; FClass.getwidth := FGetWidth;
-		FClass.getmap := FGetMap; FClass.getoutline := FGetOutline;
-		NEW(OFClass); OFClass.derive := ODerive; OFClass.getwidth := FGetWidth;
-		OFClass.getmap := FGetMap; OFClass.getoutline := OGetOutline;
-		NEW(WFClass); WFClass.derive := FDerive; WFClass.getwidth := WFGetWidth;
-		WFClass.getmap := WFGetMap; WFClass.getoutline := WFGetOutline;
-		NEW(OWFClass); OWFClass.derive := ODerive; OWFClass.getwidth := WFGetWidth;
-		OWFClass.getmap := WFGetMap; OWFClass.getoutline := OGetOutline;
-		NEW(OClass); OClass.derive := ODerive; OClass.getwidth := OGetWidth;
-		OClass.getmap := OGetMap; OClass.getoutline := OGetOutline
-	END InitClasses;
-
-	PROCEDURE InitDefault;
-	BEGIN
-		Default := OpenSize("Oberon", 10)
-	END InitDefault;
-
-
-BEGIN
-	InitClasses;
-	NEW(Cache); Cache.next := Cache; Cache.prev := Cache; Chars := 0;
-	(* NEW(TmpPath);
-	NEW(TmpRegion); TmpRegion.Init(GfxRegions.Winding); *)
-	InitDefault
-END GfxFonts.
-
-From:	oswald@inf.ethz.ch
-Subject:	Re: Aos specific packages
-Date:	Thu, 22 Jun 2000 17:08:58 +0200 (MET DST)
-
-> What settings do I need?
-
-The usual suspects
-
-ImageFormats.Pict PictImages.Install
-ImageFormats.bmp BMPImages.Install
-ImageFormats.gif GIFImages.Install
-ImageFormats.jpg JPEGImages.Install
-
-FontFormats.TTF GfxOType.Install
-FontFormats.pk GfxPKFonts.Install
-
-Actually, the FontFormats entries have once again changed in
-their semantics. GfxFonts now searches "Arial*" if "Arial-..."
-is requested. If it finds e.g. "Arial.TTF" it uses the TTF
-extension as a key into FontFormats.
---
-eos
-
-
-(*
-to do:
-o change OpenExtension to use new style commands to avoid races
-*)

+ 0 - 794
source/AGfxImages.Mod

@@ -1,794 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxImages; (** non-portable *)	(* eos  **)
-(** AUTHOR "eos"; PURPOSE "Gfx raster image transformations"; *)
-
-	(*
-		24.05.2000 - adapted to new Raster module
-	*)
-
-	IMPORT
-		SYSTEM, Raster, GfxMatrix;
-
-
-	(**
-		Image transformations are decomposed into a series of one-dimensional shift and scale transforms. These
-		are delegated to a filter object provided by the caller. The caller controls visual quality and execution time
-		by selecting a filter which complies with its demands.
-	**)
-
-	TYPE
-		Image* = Raster.Image;
-
-		ShiftProc* = PROCEDURE (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; t: REAL);
-		ScaleProc* = PROCEDURE (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; xy, dxy: REAL);
-
-		(** transformation filter **)
-		Filter* = RECORD (Raster.Mode)
-			hshift*, vshift*: ShiftProc;	(** procedures for shifting rows and columns **)
-			hscale*, vscale*: ScaleProc;	(** procedures for scaling rows and columns **)
-		END;
-
-
-	VAR
-		PreCache, Cache: Image;	(* caches for image transformations *)
-		hshift*, vshift*: ShiftProc;
-
-	(**--- Filters ---**)
-
-	(** predefined filter procedures using box filter (i.e. no filtering): fast and ugly **)
-
-	PROCEDURE HShift* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; tx: REAL);
-	BEGIN
-		IF tx >= 0.5 THEN
-			dbit := dbit + dst.fmt.bpp; INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-			DEC(len)
-		END;
-		Raster.Bind(filter, src.fmt, dst.fmt);
-		filter.transfer(filter, sadr, sbit, dadr, dbit, len)
-	END HShift;
-
-	PROCEDURE VShift* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; ty: REAL);
-	BEGIN
-		IF ty >= 0.5 THEN
-			INC(dadr, dst.bpr);
-			DEC(len)
-		END;
-		Raster.Bind(filter, src.fmt, dst.fmt);
-		WHILE len > 0 DO
-			filter.transfer(filter, sadr, sbit, dadr, dbit, 1);
-			INC(sadr, src.bpr); INC(dadr, dst.bpr);
-			DEC(len)
-		END
-	END VShift;
-
-	PROCEDURE HScale* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, dlen: LONGINT; x, dx: REAL);
-		VAR i0, i1: LONGINT;
-	BEGIN
-		Raster.Bind(filter, src.fmt, dst.fmt);
-		i0 := 0;
-		WHILE dlen > 0 DO
-			i1 := ENTIER(x);
-			IF i0 < i1 THEN
-				IF i1 >= src.width THEN i1 := src.width-1 END;
-				sbit := sbit + (i1 - i0) * src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-				i0 := i1
-			END;
-			filter.transfer(filter, sadr, sbit, dadr, dbit, 1);
-			dbit := dbit + dst.fmt.bpp; INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-			x := x + dx; DEC(dlen)
-		END
-	END HScale;
-
-	PROCEDURE VScale* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, dlen: LONGINT; y, dy: REAL);
-		VAR j0, j1: LONGINT;
-	BEGIN
-		Raster.Bind(filter, src.fmt, dst.fmt);
-		j0 := 0;
-		WHILE dlen > 0 DO
-			j1 := ENTIER(y);
-			IF j0 < j1 THEN
-				IF j1 >= src.height THEN j1 := src.height-1 END;
-				INC(sadr, (j1 - j0) * src.bpr);
-				j0 := j1
-			END;
-			filter.transfer(filter, sadr, sbit, dadr, dbit, 1);
-			INC(dadr, dst.bpr);
-			y := y + dy; DEC(dlen)
-		END
-	END VScale;
-
-	(** predefined filter procedures for linearly filtered transformations: slow and less ugly **)
-
-	PROCEDURE LinearHShift* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; tx: REAL);
-		CONST r = Raster.r; g = Raster.g; b = Raster.b; a = Raster.a;
-		VAR w0, w1, sinc, dinc, i, red, green, blue, alpha: LONGINT; da: ADDRESS; spix, dpix: Raster.Pixel;
-	BEGIN
-		w0 := ENTIER(1000H*tx + 0.5); w1 := 1000H-w0;
-		IF (w0 < 10H) OR (w1 < 10H) THEN
-			HShift(filter, src, dst, sadr, sbit, dadr, dbit, len, tx)
-		ELSE
-			Raster.Bind(filter, Raster.PixelFormat, dst.fmt);
-			sinc := src.fmt.bpp; dinc := dst.fmt.bpp; da := dadr;
-			src.fmt.unpack(src.fmt, sadr, sbit, spix);
-			FOR i := 0 TO 3 DO dpix[i] := CHR(w1 * ORD(spix[i]) DIV 1000H) END;
-			filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-			INC(dbit, dinc); INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-			DEC(len);
-			WHILE len > 0 DO
-				red := w0 * ORD(spix[r]); green := w0 * ORD(spix[g]); blue := w0 * ORD(spix[b]); alpha := w0 * ORD(spix[a]);
-				INC(sbit, sinc); INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-				src.fmt.unpack(src.fmt, sadr, sbit, spix);
-				dpix[r] := CHR((red + w1 * ORD(spix[r])) DIV 1000H);
-				dpix[g] := CHR((green + w1 * ORD(spix[g])) DIV 1000H);
-				dpix[b] := CHR((blue + w1 * ORD(spix[b])) DIV 1000H);
-				dpix[a] := CHR((alpha + w1 * ORD(spix[a])) DIV 1000H);
-				filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-				INC(dbit, dinc); INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-				DEC(len)
-			END;
-			IF (da - dst.adr) DIV dst.bpr = (dadr - dst.adr) DIV dst.bpr THEN
-				FOR i := 0 TO 3 DO dpix[i] := CHR(w0 * ORD(spix[i]) DIV 1000H) END;
-				filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1)
-			END
-		END
-	END LinearHShift;
-
-	PROCEDURE LinearVShift* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, len: LONGINT; ty: REAL);
-		CONST r = Raster.r; g = Raster.g; b = Raster.b; a = Raster.a;
-		VAR w0, w1, i, red, green, blue, alpha: LONGINT; spix, dpix: Raster.Pixel;
-	BEGIN
-		w0 := ENTIER(1000H*ty + 0.5); w1 := 1000H-w0;
-		IF (w0 < 10H) OR (w1 < 10H) THEN
-			VShift(filter, src, dst, sadr, sbit, dadr, dbit, len, ty)
-		ELSE
-			Raster.Bind(filter, Raster.PixelFormat, dst.fmt);
-			src.fmt.unpack(src.fmt, sadr, sbit, spix);
-			FOR i := 0 TO 3 DO dpix[i] := CHR(w1 * ORD(spix[i]) DIV 1000H) END;
-			filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-			INC(dadr, dst.bpr);
-			DEC(len);
-			WHILE len > 0 DO
-				red := w0 * ORD(spix[r]); green := w0 * ORD(spix[g]); blue := w0 * ORD(spix[b]); alpha := w0 * ORD(spix[a]);
-				INC(sadr, src.bpr);
-				src.fmt.unpack(src.fmt, sadr, sbit, spix);
-				dpix[r] := CHR((red + w1 * ORD(spix[r])) DIV 1000H);
-				dpix[g] := CHR((green + w1 * ORD(spix[g])) DIV 1000H);
-				dpix[b] := CHR((blue + w1 * ORD(spix[b])) DIV 1000H);
-				dpix[a] := CHR((alpha + w1 * ORD(spix[a])) DIV 1000H);
-				filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-				INC(dadr, dst.bpr);
-				DEC(len)
-			END;
-			IF (dst.adr < dadr) & (dadr < dst.adr + dst.height * dst.bpr) THEN
-				FOR i := 0 TO 3 DO dpix[i] := CHR(w0 * ORD(spix[i]) DIV 1000H) END;
-				filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1)
-			END
-		END
-	END LinearVShift;
-
-	PROCEDURE LinearHScale* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, dlen: LONGINT; x, dx: REAL);
-		VAR i0, i1,  w1, w0, j: LONGINT; spix: ARRAY 2 OF Raster.Pixel; dpix: Raster.Pixel;
-	BEGIN
-		Raster.Bind(filter, Raster.PixelFormat, dst.fmt);
-		x := x+0.5;	(* displace sample position to midpoint between candidate pixels *)
-		i0 := 0;
-		src.fmt.unpack(src.fmt, sadr, sbit, spix[0]); spix[1] := spix[0];
-		WHILE dlen > 0 DO
-			i1 := ENTIER(x);
-			IF i1 > i0 THEN
-				INC(i0);
-				IF i0 >= src.width THEN
-					spix[0] := spix[1]
-				ELSIF i1 = i0 THEN
-					spix[0] := spix[1];
-					sbit := sbit + src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[1])
-				ELSIF i1 < src.width THEN
-					sbit := sbit + (i1 - i0) * src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[0]);
-					sbit := sbit + src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[1])
-				ELSE
-					sbit := sbit + (src.width - i0) * src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[0]); spix[1] := spix[0]
-				END;
-				i0 := i1
-			END;
-			w1 := ENTIER(1000H*(x - i1)); w0 := 1000H-w1;
-			FOR j := 0 TO 3 DO
-				dpix[j] := Raster.Clamp[200H + (ORD(spix[0, j]) * w0 + ORD(spix[1, j]) * w1) DIV 1000H]
-			END;
-			filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-			dbit := dbit + dst.fmt.bpp; INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-			x := x + dx; DEC(dlen)
-		END
-	END LinearHScale;
-
-	PROCEDURE LinearVScale* (VAR filter: Raster.Mode; src, dst: Image; sadr: ADDRESS; sbit: LONGINT; dadr: ADDRESS; dbit, dlen: LONGINT; y, dy: REAL);
-		VAR j0, j1, w1, w0, j: LONGINT; spix: ARRAY 2 OF Raster.Pixel; dpix: Raster.Pixel;
-	BEGIN
-		Raster.Bind(filter, Raster.PixelFormat, dst.fmt);
-		y := y+0.5;	(* displace sample position to midpoint between candidate pixels *)
-		j0 := 0;
-		src.fmt.unpack(src.fmt, sadr, sbit, spix[0]); spix[1] := spix[0];
-		WHILE dlen > 0 DO
-			j1 := ENTIER(y);
-			IF j1 > j0 THEN
-				INC(j0);
-				IF j0 >= src.height THEN
-					spix[0] := spix[1]
-				ELSIF j1 = j0 THEN
-					spix[0] := spix[1];
-					INC(sadr, src.bpr);
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[1])
-				ELSIF j1 < src.height THEN
-					INC(sadr, (j1 - j0) * src.bpr);
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[0]);
-					INC(sadr, src.bpr);
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[1])
-				ELSE
-					INC(sadr, src.bpr);
-					src.fmt.unpack(src.fmt, sadr, sbit, spix[0]); spix[1] := spix[0]
-				END;
-				j0 := j1
-			END;
-			w1 := ENTIER(1000H*(y - j1)); w0 := 1000H-w1;
-			FOR j := 0 TO 3 DO
-				dpix[j] := Raster.Clamp[200H + (ORD(spix[0, j]) * w0 + ORD(spix[1, j]) * w1) DIV 1000H]
-			END;
-			filter.transfer(filter, ADDRESSOF(dpix[0]), 0, dadr, dbit, 1);
-			INC(dadr, dst.bpr);
-			y := y + dy; DEC(dlen)
-		END
-	END LinearVScale;
-
-	(** initialize filter with compositing operation and transformation procedures **)
-	PROCEDURE InitFilter* (VAR filter: Filter; op: SHORTINT; hshift, vshift: ShiftProc; hscale, vscale: ScaleProc);
-	BEGIN
-		Raster.InitMode(filter, op);
-		filter.hshift := hshift; filter.vshift := vshift;
-		filter.hscale := hscale; filter.vscale := vscale
-	END InitFilter;
-
-	(* get temporary pixel format image for storing intermediate images *)
-	PROCEDURE GetTempImage (VAR img, cache: Raster.Image; w, h: LONGINT);
-		VAR size: LONGINT;
-	BEGIN
-		size := w * h;
-		IF (size >= 10000H) OR (cache = NIL) THEN
-			NEW(img)
-		ELSE
-			img := cache; cache := NIL
-		END;
-		Raster.Create(img, w, h, Raster.PixelFormat)
-	END GetTempImage;
-
-	PROCEDURE FreeTempImage (VAR img, cache: Raster.Image);
-	BEGIN
-		IF img.width * img.height < 10000H THEN
-			cache := img
-		END
-	END FreeTempImage;
-
-	(* depending on matrix elements, transpose/mirror image to avoid bottleneck problems *)
-	PROCEDURE Preprocess (VAR src: Raster.Image; VAR m: GfxMatrix.Matrix; VAR filter: Filter);
-		CONST
-			r = Raster.r; g = Raster.g; b = Raster.b;
-		VAR
-			dst: Raster.Image; mode: Raster.Mode; dinc, sinc, h, w, sbit: LONGINT;
-			dadr, sadr, sa, da: ADDRESS;
-			mat: GfxMatrix.Matrix; t: REAL;
-	BEGIN
-		IF ABS(m[0, 0] * m[1, 1]) >= ABS(m[0, 1] * m[1, 0]) THEN	(* no need to swap rows and columns *)
-			IF (m[0, 0] <= 0) OR (m[1, 1] <= 0) THEN
-				GetTempImage(dst, PreCache, src.width, src.height);
-				Raster.InitModeColor(mode, Raster.srcCopy, ORD(filter.col[r]), ORD(filter.col[g]), ORD(filter.col[b]));
-				Raster.Bind(mode, src.fmt, dst.fmt);
-				IF m[0, 0] >= 0 THEN dadr := dst.adr; dinc := 4
-				ELSE dadr := dst.adr + 4*(dst.width-1); dinc := -4
-				END;
-				IF m[1, 1] >= 0 THEN sadr := src.adr; sinc := src.bpr
-				ELSE sadr := src.adr + (src.height-1) * src.bpr; sinc := -src.bpr
-				END;
-				h := 0;
-				WHILE h < src.height DO
-					w := 0; sa := sadr; sbit := 0; da := dadr;
-					WHILE w < src.width DO
-						mode.transfer(mode, sa, sbit, da, 0, 1);
-						sbit := sbit + src.fmt.bpp; INC(sa, sbit DIV 8); sbit := sbit MOD 8;
-						INC(da, dinc); INC(w)
-					END;
-					INC(sadr, sinc); INC(dadr, dst.bpr); INC(h)
-				END;
-				IF m[0, 0] < 0 THEN
-					GfxMatrix.Init(mat, -1, 0, 0, 1, w, 0);
-					GfxMatrix.Concat(mat, m, m)
-				END;
-				IF m[1, 1] < 0 THEN
-					GfxMatrix.Init(mat, 1, 0, 0, -1, 0, h);
-					GfxMatrix.Concat(mat, m, m)
-				END;
-				src := dst;
-				FreeTempImage(dst, PreCache)	(* reuse allocated image in next call *)
-			END
-		ELSE	(* need to transpose *)
-			t := m[0, 0]; m[0, 0] := m[1, 0]; m[1, 0] := t;
-			t := m[0, 1]; m[0, 1] := m[1, 1]; m[1, 1] := t;
-			GetTempImage(dst, PreCache, src.height, src.width);
-			Raster.InitModeColor(mode, Raster.srcCopy, ORD(filter.col[r]), ORD(filter.col[g]), ORD(filter.col[b]));
-			Raster.Bind(mode, src.fmt, dst.fmt);
-			IF m[0, 0] <= 0 THEN dadr := dst.adr; dinc := dst.bpr
-			ELSE dadr := dst.adr + (dst.height-1) * dst.bpr; dinc := -dst.bpr
-			END;
-			IF m[1, 1] <= 0 THEN sadr := src.adr; sinc := src.bpr
-			ELSE sadr := src.adr + (src.height-1) * src.bpr; sinc := -src.bpr
-			END;
-			h := 0;
-			WHILE h < src.height DO
-				w := 0; sa := sadr; sbit := 0; da := dadr;
-				WHILE w < src.width DO
-					mode.transfer(mode, sa, sbit, da, 0, 1);
-					sbit := sbit + src.fmt.bpp; INC(sa, sbit DIV 8); sbit := sbit MOD 8;
-					INC(da, dinc); INC(w)
-				END;
-				INC(sadr, sinc); INC(dadr, 4); INC(h)
-			END;
-			IF m[0, 0] < 0 THEN
-				GfxMatrix.Init(mat, -1, 0, 0, 1, dst.width, 0);
-				GfxMatrix.Concat(mat, m, m)
-			END;
-			IF m[1, 1] < 0 THEN
-				GfxMatrix.Init(mat, 1, 0, 0, -1, 0, dst.height);
-				GfxMatrix.Concat(mat, m, m)
-			END;
-			src := dst;
-			FreeTempImage(dst, PreCache)
-		END
-	END Preprocess;
-
-	(* shift source row by fractional amount *)
-	PROCEDURE SkewRow (src, dst: Image; si, sj, w, di, dj: LONGINT; tx: REAL; VAR filter: Filter);
-		VAR sbit, dbit: LONGINT; sadr, dadr: ADDRESS;
-	BEGIN
-		ASSERT((0.0 <= tx) & (tx <= 1.0), 100);	(* rounding problem if using tx < 1.0 *)
-		IF si < 0 THEN INC(w, si); DEC(di, si); si := 0 END;
-		IF si + w > src.width THEN w := src.width - si END;
-		IF w > 0 THEN
-			sbit := si * src.fmt.bpp; sadr := src.adr + sj * src.bpr + sbit DIV 8; sbit := sbit MOD 8;
-			dbit := di * dst.fmt.bpp; dadr := dst.adr + dj * dst.bpr + dbit DIV 8; dbit := dbit MOD 8;
-			filter.hshift(filter, src, dst, sadr, sbit, dadr, dbit, w, tx)
-		END
-	END SkewRow;
-
-	(* shear rectangle in source image horizontally; clip to destination boundary *)
-	PROCEDURE SkewRows (src, dst: Image; si, sj, w, h, dj: LONGINT; x, dx: REAL; VAR filter: Filter);
-		VAR j, di, n: LONGINT;
-	BEGIN
-		j := 0;
-		IF dj < 0 THEN
-			j := -dj;
-			IF j >= h THEN RETURN END
-		END;
-		IF dj + h > dst.height THEN
-			h := dst.height - dj;
-			IF h <= 0 THEN RETURN END
-		END;
-
-		IF dx > 0 THEN
-			IF x + h * dx >= dst.width THEN
-				h := -ENTIER((x - dst.width)/dx)
-			END;
-			x := x + j * dx;
-			IF x + w < 0 THEN
-				n := -ENTIER((x + w)/dx);
-				INC(j, n); x := x + n * dx
-			END;
-			IF x < 0 THEN
-				n := j - ENTIER(x/dx);
-				IF n > h THEN n := h END;
-				WHILE j < n DO
-					di := ENTIER(x);
-					IF di + w > dst.width THEN w := dst.width END;
-					SkewRow(src, dst, si - di, sj + j, di + w, 0, dj + j, x - di, filter);
-					INC(j); x := x + dx
-				END
-			END;
-			WHILE j < h DO
-				di := ENTIER(x);
-				IF di + w > dst.width THEN w := dst.width - di END;
-				SkewRow(src, dst, si, sj + j, w, di, dj + j, x - di, filter);
-				INC(j); x := x + dx
-			END
-
-		ELSIF dx < 0 THEN
-			IF x + w + h * dx < 0 THEN
-				h := -ENTIER((x + w)/dx)
-			END;
-			x := x + j * dx;
-			IF x >= dst.width THEN
-				n := ENTIER((dst.width - x)/dx) + 1;
-				INC(j, n); x := x + n * dx
-			END;
-			n := j - ENTIER(x/dx);	(* row at which x drops below zero *)
-			IF n > h THEN n := h END;
-			WHILE j < n DO
-				di := ENTIER(x);
-				IF di + w < dst.width THEN
-					SkewRow(src, dst, si, sj + j, w, di, dj + j, x - di, filter)
-				ELSE
-					SkewRow(src, dst, si, sj + j, dst.width - di, di, dj + j, x - di, filter)
-				END;
-				INC(j); x := x + dx
-			END;
-			WHILE j < h DO
-				di := ENTIER(x);
-				IF di + w < dst.width THEN
-					SkewRow(src, dst, si - di, sj + j, di + w, 0, dj + j, x - di, filter)
-				ELSE
-					SkewRow(src, dst, si - di, sj + j, dst.width, 0, dj + j, x - di, filter)
-				END;
-				INC(j); x := x + dx
-			END
-
-		ELSIF x < 0 THEN
-			di := ENTIER(x);
-			IF di + w > dst.width THEN
-				si := si - di; x := x - di;
-				WHILE j < h DO
-					SkewRow(src, dst, si, sj + j, dst.width, 0, dj + j, x, filter);
-					INC(j)
-				END
-			ELSIF di + w >= 0 THEN
-				si := si - di; w := w + di; x := x - di;
-				WHILE j < h DO
-					SkewRow(src, dst, si, sj + j, w, 0, dj + j, x, filter);
-					INC(j)
-				END
-			END
-
-		ELSIF x < dst.width THEN
-			di := ENTIER(x); x := x - di;
-			IF di + w > dst.width THEN w := dst.width - di END;
-			WHILE j < h DO
-				SkewRow(src, dst, si, sj + j, w, di, dj + j, x, filter);
-				INC(j)
-			END
-		END
-	END SkewRows;
-
-	(* shift source column by fractional amount *)
-	PROCEDURE SkewCol (src, dst: Image; si, sj, h, di, dj: LONGINT; ty: REAL; VAR filter: Filter);
-		VAR sbit, dbit: LONGINT; sadr, dadr: ADDRESS;
-	BEGIN
-		ASSERT((0.0 <= ty) & (ty <= 1.0), 100);	(* rounding problem with ty < 1.0 *)
-		IF sj < 0 THEN INC(h, sj); DEC(dj, sj); sj := 0 END;
-		IF sj + h > src.height THEN h := src.height - sj END;
-		IF h > 0 THEN
-			sbit := si * src.fmt.bpp; sadr := src.adr + sj * src.bpr + sbit DIV 8; sbit := sbit MOD 8;
-			dbit := di * dst.fmt.bpp; dadr := dst.adr + dj * dst.bpr + dbit DIV 8; dbit := dbit MOD 8;
-			filter.vshift(filter, src, dst, sadr, sbit, dadr, dbit, h, ty)
-		END
-	END SkewCol;
-
-	(* shear rectangle in source image vertically; clip to destination boundary *)
-	PROCEDURE SkewCols (src, dst: Image; si, sj, w, h, di: LONGINT; y, dy: REAL; VAR filter: Filter);
-		VAR i, dj, n: LONGINT;
-	BEGIN
-		i := 0;
-		IF di < 0 THEN
-			i := -di;
-			IF i >= w THEN RETURN END
-		END;
-		IF di + w > dst.width THEN
-			w := dst.width - di;
-			IF w <= 0 THEN RETURN END
-		END;
-
-		IF dy > 0 THEN
-			IF y + w * dy >= dst.height THEN
-				w := -ENTIER((y - dst.height)/dy)
-			END;
-			y := y + i * dy;
-			IF y + h < 0 THEN
-				n := -ENTIER((y + h)/dy);
-				INC(i, n); y := y + n * dy
-			END;
-			IF y < 0 THEN
-				n := i - ENTIER(y/dy);
-				IF n > w THEN n := w END;
-				WHILE i < n DO
-					dj := ENTIER(y);
-					IF dj + h > dst.height THEN h := dst.height END;
-					SkewCol(src, dst, si + i, sj - dj, dj + h, di + i, 0, y - dj, filter);
-					INC(i); y := y + dy
-				END
-			END;
-			WHILE i < w DO
-				dj := ENTIER(y);
-				IF dj + h > dst.height THEN h := dst.height - dj END;
-				SkewCol(src, dst, si + i, sj, h, di + i, dj, y - dj, filter);
-				INC(i); y := y + dy
-			END
-
-		ELSIF dy < 0 THEN
-			IF y + h + w * dy < 0 THEN
-				w := -ENTIER((y + h)/dy)
-			END;
-			y := y + i * dy;
-			IF y >= dst.height THEN
-				n := ENTIER((dst.height - y)/dy) + 1;
-				INC(i, n); y := y + n * dy
-			END;
-			n := i - ENTIER(y/dy);	(* column at which y drops below zero *)
-			IF n > w THEN n := w END;
-			WHILE i < n DO
-				dj := ENTIER(y);
-				IF dj + h < dst.height THEN
-					SkewCol(src, dst, si + i, sj, h, di + i, dj, y - dj, filter)
-				ELSE
-					SkewCol(src, dst, si + i, sj, dst.height - dj, di + i, dj, y - dj, filter)
-				END;
-				INC(i); y := y + dy
-			END;
-			WHILE i < w DO
-				dj := ENTIER(y);
-				IF dj + h < dst.height THEN
-					SkewCol(src, dst, si + i, sj - dj, h + dj, di + i, 0, y - dj, filter)
-				ELSE
-					SkewCol(src, dst, si + i, sj - dj, dst.height, di + i, 0, y - dj, filter)
-				END;
-				INC(i); y := y + dy
-			END
-
-		ELSIF y < 0 THEN
-			dj := ENTIER(y);
-			IF dj + h > dst.height THEN
-				sj := sj - dj; y := y - dj;
-				WHILE i < w DO
-					SkewCol(src, dst, si + i, sj, dst.height, di + i, 0, y, filter);
-					INC(i)
-				END
-			ELSIF dj + h >= 0 THEN
-				sj := sj - dj; h := h + dj; y := y - dj;
-				WHILE i < w DO
-					SkewCol(src, dst, si + i, sj, h, di + i, 0, y, filter);
-					INC(i)
-				END
-			END
-
-		ELSIF y < dst.height THEN
-			dj := ENTIER(y); y := y - dj;
-			IF dj + h > dst.height THEN h := dst.height - di END;
-			WHILE i < w DO
-				SkewCol(src, dst, si + i, sj, h, di + i, dj, y, filter);
-				INC(i)
-			END
-		END
-	END SkewCols;
-
-	(** render translated image on destination **)
-	PROCEDURE Translate* (src, dst: Image; tx, ty: REAL; VAR filter: Filter);
-		VAR ti, tj, i, j, w, h: LONGINT; tmp: Image;
-	BEGIN
-		ti := ENTIER(tx); tx := tx - ti;
-		tj := ENTIER(ty); ty := ty - tj;
-		IF tx < 0.01 THEN
-			SkewCols(src, dst, 0, 0, src.width, src.height, ti, tj + ty, 0, filter)
-		ELSIF ty < 0.01 THEN
-			SkewRows(src, dst, 0, 0, src.width, src.height, tj, ti + tx, 0, filter)
-		ELSE
-			i := 0; j := 0; w := src.width; h := src.height;
-			IF ti < 0 THEN i := -ti; INC(w, ti) END;
-			IF ti + w >= dst.width THEN w := dst.width - ti - 1 END;
-			IF tj < 0 THEN j := -tj; INC(h, tj) END;
-			IF tj + h >= dst.height THEN h := dst.height - tj - 1 END;
-			GetTempImage(tmp, Cache, w, h+1);
-			SkewCols(src, tmp, i, j, w, h, 0, ty, 0, filter);
-			SkewRows(tmp, dst, 0, 0, tmp.width, tmp.height, tj, ti + tx, 0, filter);
-			FreeTempImage(tmp, Cache)
-		END
-	END Translate;
-
-	(** render scaled image on destination **)
-	PROCEDURE Scale* (src, dst: Image; sx, sy, tx, ty: REAL; VAR filter: Filter);
-		VAR xl, xr, yb, yt, w, h, sbit, dbit, i: LONGINT; sadr, dadr: ADDRESS; dy, y, dx, x: REAL; tmp: Image;
-	BEGIN
-		ASSERT((sx > 0) & (sy > 0), 100);
-		xl := ENTIER(tx); xr := -ENTIER(-(tx + sx * src.width));
-		IF xl < 0 THEN xl := 0 END;
-		IF xr > dst.width THEN
-			xr := dst.width;
-			IF xr <= xl THEN RETURN END;
-		END;
-		yb := ENTIER(ty); yt := -ENTIER(-(ty + sy * src.height));
-		IF yb < 0 THEN yb := 0 END;
-		IF yt > dst.height THEN
-			yt := dst.height;
-			IF yt <= yb THEN RETURN END
-		END;
-		w := xr - xl; h := yt - yb;
-
-		IF ABS(w - src.width) < 1 THEN
-			dy := 1.0/sy; y := (0.5 - (ty - ENTIER(ty))) * dy;
-			sadr := src.adr; sbit := 0;
-			dbit := xl * dst.fmt.bpp; dadr := dst.adr + yb * dst.bpr + dbit DIV 8; dbit := dbit MOD 8;
-			i := 0;
-			WHILE i < src.width DO
-				filter.vscale(filter, src, dst, sadr, sbit, dadr, dbit, h, y, dy);
-				sbit := sbit + src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-				dbit := dbit + dst.fmt.bpp; INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-				INC(i)
-			END
-
-		ELSIF ABS(h - src.height) < 1 THEN
-			dx := 1.0/sx; x := (0.5 - (tx - ENTIER(tx))) * dx;
-			sadr := src.adr; sbit := 0;
-			dbit := xl * dst.fmt.bpp; dadr := dst.adr + yb * dst.bpr + dbit DIV 8; dbit := dbit MOD 8;
-			i := 0;
-			WHILE i < src.height DO
-				filter.hscale(filter, src, dst, sadr, sbit, dadr, dbit, w, x, dx);
-				INC(sadr, src.bpr); INC(dadr, dst.bpr);
-				INC(i)
-			END
-
-		ELSE
-			GetTempImage(tmp, Cache, src.width, h);
-			dy := 1.0/sy; y := (0.5 - (ty - ENTIER(ty))) * dy;
-			sadr := src.adr; sbit := 0; dadr := tmp.adr; dbit := 0;
-			i := 0;
-			WHILE i < src.width DO
-				filter.vscale(filter, src, tmp, sadr, sbit, dadr, dbit, h, y, dy);
-				sbit := sbit + src.fmt.bpp; INC(sadr, sbit DIV 8); sbit := sbit MOD 8;
-				dbit := dbit + tmp.fmt.bpp; INC(dadr, dbit DIV 8); dbit := dbit MOD 8;
-				INC(i)
-			END;
-			dx := 1.0/sx; x := (0.5 - (tx - ENTIER(tx))) * dx;
-			sadr := tmp.adr; sbit := 0;
-			dbit := xl * dst.fmt.bpp; dadr := dst.adr + yb * dst.bpr + dbit DIV 8; dbit := dbit MOD 8;
-			i := 0;
-			WHILE i < h DO
-				filter.hscale(filter, tmp, dst, sadr, sbit, dadr, dbit, w, x, dx);
-				INC(sadr, tmp.bpr); INC(dadr, dst.bpr);
-				INC(i)
-			END;
-			FreeTempImage(tmp, Cache)
-		END
-	END Scale;
-
-	(** render rotated image on destination **)
-	PROCEDURE Rotate* (src, dst: Image; sin, cos, tx, ty: REAL; VAR filter: Filter);
-		VAR m: GfxMatrix.Matrix; tan, htan, wsin, hcos, x, y: REAL; wmax, h, iy, sw, sh: LONGINT; tmp: Image;
-	BEGIN
-		ASSERT(ABS(sin * sin + cos * cos - 1) < 0.0001, 100);
-		m[0, 0] := cos; m[0, 1] := sin; m[1, 0] := -sin; m[1, 1] := cos; m[2, 0] := tx; m[2, 1] := ty;
-		Preprocess(src, m, filter);
-		cos := m[0, 0]; sin := m[0, 1]; tx := m[2, 0]; ty := m[2, 1];
-		tan := sin/(1.0 + cos);	(* identity for tan(phi/2); 1/2 SQRT(3) <= cos <= 1 *)
-		sw := src.width; sh := src.height;
-		htan := ABS(tan) * sh;
-		wsin := ABS(sin) * sw;
-		hcos := cos * sh;
-		wmax := sw + ENTIER(htan) + 1;	(* width of intermediate image *)
-		h := ENTIER(wsin + hcos) + 2;	(* second extra pixel for ty - tj *)
-		GetTempImage(tmp, Cache, wmax, h + sh);	(* stack two intermediate images on top of each other *)
-		IF sin >= 0 THEN
-			x := htan; tx := tx - x; y := hcos - sh
-		ELSE
-			x := 0; y := wsin; tx := tx + wsin * tan; ty := ty - y
-		END;
-		iy := ENTIER(ty); y := y + (ty - iy);
-		SkewRows(src, tmp, 0, 0, sw, sh, h, x, -tan, filter);	(* first pass: skew horizontal scanlines *)
-		SkewCols(tmp, tmp, 0, h, wmax, sh, 0, y, sin, filter);	(* second pass: skew vertical scanlines *)
-		SkewRows(tmp, dst, 0, 0, wmax, h, iy, tx, -tan, filter);	(* third pass: skew horizontal scanlines *)
-		FreeTempImage(tmp, Cache)
-	END Rotate;
-
-	(** render horizontally sheared image on destination **)
-	PROCEDURE ShearRows* (src, dst: Image; sx, tx: REAL; VAR filter: Filter);
-	BEGIN
-		SkewRows(src, dst, 0, 0, src.width, src.height, 0, tx, sx, filter)
-	END ShearRows;
-
-	(** render vertically sheared image on destination **)
-	PROCEDURE ShearCols* (src, dst: Image; sy, ty: REAL; VAR filter: Filter);
-	BEGIN
-		SkewCols(src, dst, 0, 0, src.width, src.height, 0, ty, sy, filter)
-	END ShearCols;
-
-	(** render affinely transformed image on destination **)
-	PROCEDURE Transform* (src, dst: Image; m: GfxMatrix.Matrix; VAR filter: Filter);
-		CONST eps = 0.003;
-		VAR det, s, dx, x: REAL; iy, w, h, ix: LONGINT; tmp: Image;
-	BEGIN
-		Preprocess(src, m, filter);
-		IF (ABS(m[0, 0]) >= eps) & (ABS(m[1, 1]) >= eps) THEN	(* matrix isn't singular *)
-			IF (ABS(m[0, 1]) < eps) & (ABS(m[1, 0]) < eps) THEN	(* no rotation or shear *)
-				IF (ABS(m[0, 0]-1) < eps) & (ABS(m[1, 1]-1) < eps) THEN	(* not even scaled *)
-					Translate(src, dst, m[2, 0], m[2, 1], filter)
-				ELSE
-					Scale(src, dst, m[0, 0], m[1, 1], m[2, 0], m[2, 1], filter)
-				END
-			ELSE
-				det := m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
-				IF ABS(det) >= eps THEN
-					IF (ABS(det-1) < eps) & (ABS(m[0, 0] - m[1, 1]) < eps) & (ABS(m[0, 1] + m[1, 0]) < eps) THEN
-						Rotate(src, dst, m[0, 1], m[0, 0], m[2, 0], m[2, 1], filter)
-					ELSIF ABS(m[0, 1]) < eps THEN	(* horizontal shear *)
-						iy := ENTIER(m[2, 1]);
-						IF ABS(det-1) >= eps THEN	(* scaled *)
-							w := ENTIER(m[0, 0] * src.width)+1;
-							h := ENTIER(m[1, 1] * src.height)+1;
-							GetTempImage(tmp, Cache, w, h);
-							Scale(src, tmp, m[0, 0], m[1, 1], 0, m[2, 1] - iy, filter);
-							SkewRows(tmp, dst, 0, 0, tmp.width, tmp.height, iy, m[2, 0], m[1, 0]/m[1, 1], filter);
-							FreeTempImage(tmp, Cache)
-						ELSIF m[2, 1] - iy < eps THEN	(* integer translation *)
-							SkewRows(src, dst, 0, 0, src.width, src.height, iy, m[2, 0], m[1, 0], filter)
-						ELSE
-							GetTempImage(tmp, Cache, src.width, src.height+1);
-							Translate(src, tmp, 0, m[2, 1] - iy, filter);
-							SkewRows(tmp, dst, 0, 0, tmp.width, tmp.height, iy, m[2, 0], m[1, 0], filter);
-							FreeTempImage(tmp, Cache)
-						END
-					ELSIF ABS(m[1, 0]) < eps THEN	(* vertical shear *)
-						ix := ENTIER(m[2, 0]);
-						IF ABS(det-1) >= eps THEN	(* scaled *)
-							w := ENTIER(m[0, 0] * src.width)+1;
-							h := ENTIER(m[1, 1] * src.height)+1;
-							GetTempImage(tmp, Cache, w, h);
-							Scale(src, tmp, m[0, 0], m[1, 1], m[2, 0] - ix, 0, filter);
-							SkewCols(tmp, dst, 0, 0, tmp.width, tmp.height, ix, m[2, 1], m[0, 1]/m[0, 0], filter);
-							FreeTempImage(tmp, Cache)
-						ELSIF m[2, 0] - ix < eps THEN	(* integer translation *)
-							SkewCols(src, dst, 0, 0, src.width, src.height, ix, m[2, 1], m[0, 1], filter)
-						ELSE
-							GetTempImage(tmp, Cache, src.width+1, src.height);
-							Translate(src, tmp, m[2, 0] - ix, 0, filter);
-							SkewRows(tmp, dst, 0, 0, tmp.width, tmp.height, ix, m[2, 1], m[0, 1], filter);
-							FreeTempImage(tmp, Cache)
-						END
-					ELSE
-						(*
-							use the following identity:
-								[ a b ]	[ a         0       ] [        1           0 ] [ 1 b/a ]
-								[ c d ] = [ 0 (ad-bc)/a ] [ ca/(ad-bc) 1 ]  [ 0   1   ]
-						*)
-						s := det/m[0, 0];
-						w := ENTIER(m[0, 0] * src.width)+1;
-						h := ENTIER(s * src.height)+1;
-						dx := m[1, 0]/s; x := (h-1) * ABS(dx) + 2;
-						GetTempImage(tmp, Cache, w - 2*ENTIER(-x) + 1, 2*h);
-						Scale(src, tmp, m[0, 0], s, x, h, filter);
-						ix := ENTIER(m[2, 0]);
-						SkewRows(tmp, tmp, 0, h, tmp.width, h, 0, m[2, 0] - ix, dx, filter);
-						w := ENTIER(x);
-						IF dx >= 0 THEN
-							SkewCols(tmp, dst, w, 0, tmp.width - w, h, ix, m[2, 1], m[0, 1]/m[0, 0], filter)
-						ELSE
-							s := m[0, 1]/m[0, 0];
-							SkewCols(tmp, dst, 0, 0, tmp.width - w, h, ix - w, m[2, 1] - w * s, s, filter)
-						END;
-						FreeTempImage(tmp, Cache)
-					END
-				END
-			END
-		END
-	END Transform;
-
-	(** uses nearest-neighbor resampling (box filter); bad aliasing when downscaling **)
-	PROCEDURE InitNoFilter*(VAR filter: Filter);
-	BEGIN
-		InitFilter(filter, Raster.srcOverDst, HShift, VShift, HScale, VScale)
-	END InitNoFilter;
-
-	(** uses linear interpolation (triangle filter); blurry when upscaling **)
-	PROCEDURE InitLinearFilter*(VAR filter: Filter);
-	BEGIN
-		InitFilter(filter, Raster.srcOverDst, LinearHShift, LinearVShift, LinearHScale, LinearVScale);
-	END InitLinearFilter;
-
-BEGIN
-	hshift := HShift; vshift := VShift
-END GfxImages.

+ 0 - 345
source/AGfxMatrix.Mod

@@ -1,345 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxMatrix; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Affine transformations in 2D"; *)
-
-	(*
-		21.02.2000 - bugfix in Scaled: didn't recognize downscaling
-		15.04.2000 - added Atan2
-	*)
-
-	IMPORT
-		Streams, Math;
-
-
-	CONST
-		Eps = 1.0E-5;
-
-
-	TYPE
-		(**
-			Transformation matrix
-				3x2_matrices can represent any combination of affine transformations, i.e. of translation, rotation, scaling and
-				shearing.
-
-			Translate by tx, ty:
-				[  1  0 ]
-				[  0  1 ]
-				[ tx ty ]
-
-			Scale by sx, sy:
-				[ sx   0 ]
-				[  0  sy ]
-				[  0   0 ]
-
-			Rotate counter-clockwise by angle phi:
-				[  cos(phi)   sin(phi) ]
-				[ -sin(phi)  cos(phi) ]
-				[       0             0       ]
-
-			Shear along x_axis by factor f:
-				[ 1   0 ]
-				[ f    1 ]
-				[ 0   0 ]
-		**)
-
-		Matrix* = ARRAY 3, 2 OF REAL;
-
-
-	VAR
-		Identity*: Matrix;	(** identity matrix (read_only) **)
-
-
-	(**--- Matrix Computation ---**)
-
-	(** initialize matrix with given values **)
-	PROCEDURE Init* (VAR m: Matrix; m00, m01, m10, m11, m20, m21: REAL);
-	BEGIN
-		m[0, 0] := m00; m[0, 1] := m01;
-		m[1, 0] := m10; m[1, 1] := m11;
-		m[2, 0] := m20; m[2, 1] := m21
-	END Init;
-
-	(**
-		Procedures Get3PointTransform, Get2PointTransform and Invert may not be able to find a solution. In that case,
-		they return a singular matrix with all elements set to zero.
-	**)
-
-	(** calculate matrix that maps p0 to p1, q0 to q1, and r0 to r1 **)
-	PROCEDURE Get3PointTransform* (px0, py0, px1, py1, qx0, qy0, qx1, qy1, rx0, ry0, rx1, ry1: REAL; VAR res: Matrix);
-		VAR m: ARRAY 6, 7 OF REAL; i, j, k: LONGINT; max, t: REAL; v: ARRAY 6 OF REAL;
-	BEGIN
-		(* initialize set of linear equations for matrix coefficients *)
-		m[0, 0] := px0; m[0, 1] := py0; m[0, 2] := 1.0; m[0, 3] := 0.0; m[0, 4] := 0.0; m[0, 5] := 0.0; m[0, 6] := px1;
-		m[1, 0] := qx0; m[1, 1] := qy0; m[1, 2] := 1.0; m[1, 3] := 0.0; m[1, 4] := 0.0; m[1, 5] := 0.0; m[1, 6] := qx1;
-		m[2, 0] := rx0; m[2, 1] := ry0; m[2, 2] := 1.0; m[2, 3] := 0.0; m[2, 4] := 0.0;  m[2, 5] := 0.0; m[2, 6] := rx1;
-		m[3, 0] := 0.0; m[3, 1] := 0.0; m[3, 2] := 0.0; m[3, 3] := px0; m[3, 4] := py0; m[3, 5] := 1.0; m[3, 6] := py1;
-		m[4, 0] := 0.0; m[4, 1] := 0.0; m[4, 2] := 0.0; m[4, 3] := qx0; m[4, 4] := qy0; m[4, 5] := 1.0; m[4, 6] := qy1;
-		m[5, 0] := 0.0; m[5, 1] := 0.0; m[5, 2] := 0.0; m[5, 3] := rx0; m[5, 4] := ry0; m[5, 5] := 1.0; m[5, 6] := ry1;
-
-		(* Gaussian elimination with pivoting *)
-		FOR i := 0 TO 5 DO
-			k := i; max := ABS(m[i, i]);
-			FOR j := i+1 TO 5 DO
-				IF ABS(m[j, i]) > max THEN
-					k := j; max := ABS(m[j, i])
-				END
-			END;
-			IF max < Eps THEN	(* matrix is singular *)
-				Init(res, 0, 0, 0, 0, 0, 0);
-				RETURN
-			END;
-			IF k # i THEN	(* swap rows to bring largest element up *)
-				FOR j := i TO 6 DO
-					t := m[i, j]; m[i, j] := m[k, j]; m[k, j] := t
-				END
-			END;
-			FOR k := i+1 TO 5 DO
-				t := m[k, i]/m[i, i];
-				FOR j := i+1 TO 6 DO
-					m[k, j] := m[k, j] - t * m[i, j]
-				END
-			END
-		END;
-
-		(* solve equations *)
-		FOR i := 5 TO 0 BY -1 DO
-			t := m[i, 6];
-			FOR j := i+1 TO 5 DO
-				t := t - v[j] * m[i, j]
-			END;
-			v[i] := t/m[i, i]
-		END;
-
-		Init(res, v[0], v[3], v[1], v[4], v[2], v[5])
-	END Get3PointTransform;
-
-	(** calculate matrix that maps p0 to p1 and q0 to q1 **)
-	PROCEDURE Get2PointTransform* (px0, py0, px1, py1, qx0, qy0, qx1, qy1: REAL; VAR res: Matrix);
-		VAR rx0, ry0, rx1, ry1: REAL;
-	BEGIN
-		rx0 := px0 + py0 - qy0; ry0 := py0 + qx0 - px0;
-		rx1 := px1 + py1 - qy1; ry1 := py1 + qx1 - px1;
-		Get3PointTransform(px0, py0, px1, py1, qx0, qy0, qx1, qy1, rx0, ry0, rx1, ry1, res)
-	END Get2PointTransform;
-
-	(** calculate inverse of matrix **)
-	PROCEDURE Invert* (m: Matrix; VAR res: Matrix);
-		VAR det, inv: REAL;
-	BEGIN
-		det := m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
-		IF ABS(det) >= Eps THEN	(* matrix can be inverted; use Cramer's rule *)
-			inv := 1/det;
-			res[0, 0] := +inv * m[1, 1];
-			res[0, 1] := -inv * m[0, 1];
-			res[1, 0] := -inv * m[1, 0];
-			res[1, 1] := +inv * m[0, 0];
-			res[2, 0] := +inv * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0]);
-			res[2, 1] := +inv * (m[0, 1] * m[2, 0] - m[0, 0] * m[2, 1])
-		ELSE
-			Init(res, 0, 0, 0, 0, 0, 0)
-		END
-	END Invert;
-
-
-	(**--- Detection of Special Cases ---**)
-
-	(** return determinant of matrix **)
-	PROCEDURE Det* (VAR m: Matrix): REAL;
-	BEGIN
-		RETURN m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]
-	END Det;
-
-	(** return whether matrix is singular **)
-	PROCEDURE Singular* (VAR m: Matrix): BOOLEAN;
-	BEGIN
-		RETURN ABS(m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) < Eps
-	END Singular;
-
-	(** return whether matrix changes vector lengths **)
-	PROCEDURE Scaled* (VAR m: Matrix): BOOLEAN;
-	BEGIN
-		RETURN ABS(ABS(m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) - 1) > Eps
-	END Scaled;
-
-	(** return whether matrix includes rotation, shear, or mirror transformation **)
-	PROCEDURE Rotated* (VAR m: Matrix): BOOLEAN;
-	BEGIN
-		RETURN (m[0, 0] < -Eps) OR (m[1, 1] < -Eps) OR (ABS(m[0, 1]) > Eps) OR (ABS(m[1, 0]) > Eps)
-	END Rotated;
-
-	(** return whether matrices should be considered equal **)
-	PROCEDURE Equal* (VAR m, n: Matrix): BOOLEAN;
-	BEGIN
-		RETURN
-			(ABS(m[0, 0] - n[0, 0]) < Eps) & (ABS(m[0, 1] - n[0, 1]) < Eps) &
-			(ABS(m[1, 0] - n[1, 0]) < Eps) & (ABS(m[1, 1] - n[1, 1]) < Eps) &
-			(ABS(m[2, 0] - n[2, 0]) < Eps) & (ABS(m[2, 1] - n[2, 1]) < Eps)
-	END Equal;
-
-
-	(**--- Matrix Concatenation ---**)
-
-	(**
-		Combinations of single transformations are evaluated from left to right. Executing Translate, Rotate or Scale
-		pre-concatenates a corresponding matrix to the left of the given matrix parameter. This has the effect that
-		the new transformation is applied before all previously accumulated transformations. Every transformation is
-		therefore executed in the context of the coordinate system defined by the concatenation of all transformations
-		to its right.
-	**)
-
-	(** translation by (dx, dy) **)
-	PROCEDURE Translate* (m: Matrix; dx, dy: REAL; VAR res: Matrix);
-	BEGIN
-		res[0, 0] := m[0, 0]; res[0, 1] := m[0, 1];
-		res[1, 0] := m[1, 0]; res[1, 1] := m[1, 1];
-		res[2, 0] := m[2, 0] + dx * m[0, 0] + dy * m[1, 0];
-		res[2, 1] := m[2, 1] + dx * m[0, 1] + dy * m[1, 1]
-	END Translate;
-
-	(** scale by (sx, sy) **)
-	PROCEDURE Scale* (m: Matrix; sx, sy: REAL; VAR res: Matrix);
-	BEGIN
-		res[0, 0] := sx * m[0, 0]; res[0, 1] := sx * m[0, 1];
-		res[1, 0] := sy * m[1, 0]; res[1, 1] := sy * m[1, 1];
-		res[2, 0] := m[2, 0]; res[2, 1] := m[2, 1]
-	END Scale;
-
-	(** scale at (ox, oy) by (sx, sy) **)
-	PROCEDURE ScaleAt* (m: Matrix; ox, oy, sx, sy: REAL; VAR res: Matrix);
-		VAR tx, ty: REAL;
-	BEGIN
-		res[0, 0] := sx * m[0, 0]; res[0, 1] := sx * m[0, 1];
-		res[1, 0] := sy * m[1, 0]; res[1, 1] := sy * m[1, 1];
-		tx := ox * (1-sx); ty := oy * (1-sy);
-		res[2, 0] := tx * m[0, 0] + ty * m[1, 0] + m[2, 0];
-		res[2, 1] := tx * m[0, 1] + ty * m[1, 1] + m[2, 1]
-	END ScaleAt;
-
-	(** rotate counter-clockwise by angle specified by its sine and cosine **)
-	PROCEDURE Rotate* (m: Matrix; sin, cos: REAL; VAR res: Matrix);
-	BEGIN
-		res[0, 0] := cos * m[0, 0] + sin * m[1, 0]; res[0, 1] := cos * m[0, 1] + sin * m[1, 1];
-		res[1, 0] := -sin * m[0, 0] + cos * m[1, 0]; res[1, 1] := -sin * m[0, 1] + cos * m[1, 1];
-		res[2, 0] := m[2, 0]; res[2, 1] := m[2, 1]
-	END Rotate;
-
-	(** rotate counter-clockwise around (ox, oy) by angle specified by its sine and cosine **)
-	PROCEDURE RotateAt* (m: Matrix; ox, oy, sin, cos: REAL; VAR res: Matrix);
-		VAR tx, ty: REAL;
-	BEGIN
-		res[0, 0] := cos * m[0, 0] + sin * m[1, 0]; res[0, 1] := cos * m[0, 1] + sin * m[1, 1];
-		res[1, 0] := -sin * m[0, 0] + cos * m[1, 0]; res[1, 1] := -sin * m[0, 1] + cos * m[1, 1];
-		tx := ox * (1-cos) + oy * sin; ty := oy * (1-cos) - ox * sin;
-		res[2, 0] := tx * m[0, 0] + ty * m[1, 0] + m[2, 0];
-		res[2, 1] := tx * m[0, 1] + ty * m[1, 1] + m[2, 1]
-	END RotateAt;
-
-	(** concatenate matrices **)
-	PROCEDURE Concat* (m, n: Matrix; VAR res: Matrix);
-	BEGIN
-		res[0, 0] := m[0, 0] * n[0, 0] + m[0, 1] * n[1, 0];
-		res[0, 1] := m[0, 0] * n[0, 1] + m[0, 1] * n[1, 1];
-		res[1, 0] := m[1, 0] * n[0, 0] + m[1, 1] * n[1, 0];
-		res[1, 1] := m[1, 0] * n[0, 1] + m[1, 1] * n[1, 1];
-		res[2, 0] := m[2, 0] * n[0, 0] + m[2, 1] * n[1, 0] + n[2, 0];
-		res[2, 1] := m[2, 0] * n[0, 1] + m[2, 1] * n[1, 1] + n[2, 1]
-	END Concat;
-
-
-	(**--- Arctan of Vector ---**)
-
-	PROCEDURE Atan2* (x, y: REAL): REAL;
-		VAR phi: REAL;
-	BEGIN
-		IF (ABS(x) < 1.0) & (ABS(y) >= ABS(x * MAX(REAL))) THEN	(* y/x would overflow *)
-			IF y >= 0.0 THEN phi := Math.pi/2
-			ELSE phi := -Math.pi/2
-			END
-		ELSIF x > 0.0 THEN	(* 1st or 4th quadrant *)
-			phi := Math.arctan(y/x)
-		ELSIF x < 0.0 THEN	(* 2nd or 3rd quadrant *)
-			phi := Math.arctan(y/x) + Math.pi
-		END;
-		RETURN phi
-	END Atan2;
-
-
-	(**--- Matrix Application ---**)
-
-	(** apply transformation matrix to point **)
-	PROCEDURE Apply* (VAR m: Matrix; xin, yin: REAL; VAR xout, yout: REAL);
-	BEGIN
-		xout := xin * m[0, 0] + yin * m[1, 0] + m[2, 0];
-		yout := xin * m[0, 1] + yin * m[1, 1] + m[2, 1]
-	END Apply;
-
-	(** apply transformation matrix to vector (ignoring translation) **)
-	PROCEDURE ApplyToVector* (VAR m: Matrix; xin, yin: REAL; VAR xout, yout: REAL);
-	BEGIN
-		xout := xin * m[0, 0] + yin * m[1, 0];
-		yout := xin * m[0, 1] + yin * m[1, 1]
-	END ApplyToVector;
-
-	(** apply transformation matrix to distance **)
-	PROCEDURE ApplyToDist* (VAR m: Matrix; din: REAL; VAR dout: REAL);
-		VAR x, y: REAL;
-	BEGIN
-		x := din * m[0, 0]; y := din * m[0, 1];
-		IF ABS(y) < 1.0E-3 THEN dout := x
-		ELSE dout := Math.sqrt(x * x + y * y)
-		END
-	END ApplyToDist;
-
-	(** apply transformation matrix to axis-aligned rectangle; result is enclosing axis-aligned rectangle **)
-	PROCEDURE ApplyToRect* (VAR m: Matrix; ilx, ily, irx, iuy: REAL; VAR olx, oly, orx, ouy: REAL);
-		VAR l, h: REAL;
-	BEGIN
-		olx := m[2, 0]; orx := m[2, 0];
-		l := ilx * m[0, 0]; h := irx * m[0, 0];
-		IF l <= h THEN olx := olx + l; orx := orx + h ELSE olx := olx + h; orx := orx + l END;
-		l := ily * m[1, 0]; h := iuy * m[1, 0];
-		IF l <= h THEN olx := olx + l; orx := orx + h ELSE olx := olx + h; orx := orx + l END;
-		oly := m[2, 1]; ouy := m[2, 1];
-		l := ilx * m[0, 1]; h := irx * m[0, 1];
-		IF l <= h THEN oly := oly + l; ouy := ouy + h ELSE oly := oly + h; ouy := ouy + l END;
-		l := ily * m[1, 1]; h := iuy * m[1, 1];
-		IF l <= h THEN oly := oly + l; ouy := ouy + h ELSE oly := oly + h; ouy := ouy + l END
-	END ApplyToRect;
-
-	(** apply inverse of matrix to point **)
-	PROCEDURE Solve* (VAR m: Matrix; u, v: REAL; VAR x, y: REAL);
-		VAR det: REAL;
-	BEGIN
-		det := m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
-		IF ABS(det) >= Eps THEN	(* matrix can be inverted *)
-			u := u - m[2, 0]; v := v - m[2, 1];
-			x := (m[1, 1] * u - m[1, 0] * v)/det;
-			y := (m[0, 0] * v - m[0, 1] * u)/det
-		END
-	END Solve;
-
-
-	(**--- Matrix I/O ---**)
-
-	PROCEDURE Write* (VAR w: Streams.Writer; VAR m: Matrix);
-		VAR i: LONGINT;
-	BEGIN
-		FOR i := 0 TO 2 DO
-			w.RawReal(m[i, 0]); w.RawReal(m[i, 1])
-		END
-	END Write;
-
-	PROCEDURE Read* (VAR r: Streams.Reader; VAR m: Matrix);
-		VAR i: LONGINT;
-	BEGIN
-		FOR i := 0 TO 2 DO
-			r.RawReal(m[i, 0]); r.RawReal(m[i, 1])
-		END
-	END Read;
-
-
-BEGIN
-	Init(Identity, 1, 0, 0, 1, 0, 0)
-END GfxMatrix.

+ 0 - 1404
source/AGfxPaths.Mod

@@ -1,1404 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxPaths; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Two_dimensional paths consisting of lines, arcs and bezier curves"; *)
-
-	(*
-		9.2.98 - made behaviour of EnumSpline similar to that of EnumArc and EnumBezier (produces no Enter/Exit)
-		11.2.98 - eliminated offset parameter in Enter elements, optimized data structure (now always pair in CoordBlock)
-		12.2.98 - added length functions
-		18.3.98 - fixed bug in EnumQuery (kept wrong code for next line)
-		13.5.98 - fixed bug in EnumBezier (wrong calculation; used x instead of y)
-		15.9.98 - minor cleanup: removed position, Save/Restore, GetBBox; simplified scanner interface
-		26.11.98 - added procedure Close
-		26.1.99 - added procedure Split
-		21.5.99 - fixed major bug in ReverseTo (no update of destination path fields)
-		12.7.99 - fixed another bug in ReverseTo (wrong direction when reverting Exit element)
-		12.7.99 - approximate arc with line if radius is smaller than flatness
-		18.02.2000 - simpler initial step without sqrt in EnumArc
-		18.02.2000 - more robust bezier code (deals with folded curves and cusps)
-		27.02.2000 - fixed arc code for starting points that are not on the ellipse
-		04.05.2000 - fixed solve; one execution path never set number of solutions (noticed by gf)
-	*)
-
-	IMPORT
-		Math, GfxMatrix;
-
-
-	CONST
-		Stop* = 0; Enter* = 1; Line* = 2; Arc* = 3; Bezier* = 4; Exit* = 5;	(** path element types **)
-
-		ElemBlockSize = 16;	(* base of number of path elements *)
-		CoordBlockSize = 32;	(* base of number of path coordinates *)
-
-		MaxSplinePoints* = 128;	(** maximal number of control points in a spline **)
-
-		Left = 0; Right = 1; Bottom = 2; Top = 3;	(* clip codes *)
-
-
-	TYPE
-		(* internal path structures *)
-		ElemBlock = POINTER TO ElemBlockDesc;
-		ElemBlockDesc = RECORD
-			next: ElemBlock;
-			elem: ARRAY ElemBlockSize OF SHORTINT;
-			coords: INTEGER;
-		END;
-
-		CoordBlock = POINTER TO CoordBlockDesc;
-		CoordBlockDesc = RECORD
-			next: CoordBlock;
-			x, y: ARRAY CoordBlockSize OF REAL;
-		END;
-
-		(** path abstraction **)
-		(**
-			A paths consists of any number of subpaths, where each subpath starts with a Enter element, followed by
-			any number of curve elements, and terminated by an Exit element.
-
-			Enter
-				(x, y) is the starting point for the following curve element
-				(dx, dy) is the tangent vector at the end of an adjacent subpath or (0, 0) if there is none
-
-			Line
-				(x, y) is the end point of the line and the starting point of any subsequent curve
-
-			Arc
-				(x, y) is the end point of the arc and the starting point of any subsequent curve (may coincide with the
-						current point, resulting in a circle or ellipse)
-				(x0, y0) is the center of the circle/ellipse this arc is part of
-				(x1, y1) is the end point of the first half axis vector
-				(x2, y2) is the end point of the first half axis vector (not necessarily perpendicular to the first HAV)
-
-			Bezier
-				(x, y) is the end point of the cubic bezier curve and the starting point of any subsequent curve
-				(x1, y1) is the first control point of the cubic bezier curve
-				(x1, y1) is the second control point of the cubic bezier curve
-
-			Exit
-				(dx, dy) is the tangent vector at the starting point of an adjacent subpath or (0, 0) if there is none
-		**)
-		
-		Path* = OBJECT
-		VAR
-			elems* := 0, coords* := 0: INTEGER;	(** number of elements/coordinate pairs in path **)
-			firstEB := NIL, lastEB := NIL: ElemBlock;	(* path element types *)
-			firstCB := NIL, lastCB := NIL: CoordBlock;	(* path element coordinates *)
-			
-			(** discard previous contents and start new path **)
-			PROCEDURE Clear* ();
-			BEGIN
-				IF SELF.firstEB = NIL THEN NEW(SELF.firstEB) END;
-				SELF.lastEB := SELF.firstEB; SELF.lastEB.next := NIL;
-				IF SELF.firstCB = NIL THEN NEW(SELF.firstCB) END;
-				SELF.lastCB := SELF.firstCB; SELF.lastCB.next := NIL;
-				SELF.elems := 0; SELF.coords := 0;
-				SELF.firstEB.coords := 0
-			END Clear;
-
-			(** append enter element **)
-			PROCEDURE AddEnter* (x, y, dx, dy: REAL);
-			BEGIN
-				AddElem(SELF, Enter);
-				AddCoord(SELF, dx, dy);
-				AddCoord(SELF, x, y)
-			END AddEnter;
-
-			(** append line element **)
-			PROCEDURE AddLine* (x, y: REAL);
-			BEGIN
-				AddElem(SELF, Line);
-				AddCoord(SELF, x, y)
-			END AddLine;
-
-			(** append arc element **)
-			PROCEDURE AddArc* (x, y, x0, y0, x1, y1, x2, y2: REAL);
-			BEGIN
-				AddElem(SELF, Arc);
-				AddCoord(SELF, x0, y0);
-				AddCoord(SELF, x1, y1);
-				AddCoord(SELF, x2, y2);
-				AddCoord(SELF, x, y)
-			END AddArc;
-
-			(** append bezier element **)
-			PROCEDURE AddBezier* (x, y, x1, y1, x2, y2: REAL);
-			BEGIN
-				AddElem(SELF, Bezier);
-				AddCoord(SELF, x1, y1);
-				AddCoord(SELF, x2, y2);
-				AddCoord(SELF, x, y)
-			END AddBezier;
-
-			(** append exit element **)
-			PROCEDURE AddExit* (dx, dy: REAL);
-			BEGIN
-				AddElem(SELF, Exit);
-				AddCoord(SELF, dx, dy)
-			END AddExit;
-
-			(** append subpath for axis-aligned rectangle **)
-			PROCEDURE AddRect* (llx, lly, urx, ury: REAL);
-			BEGIN
-				SELF.AddEnter(llx, lly, 0, lly - ury);
-				SELF.AddLine(urx, lly); SELF.AddLine(urx, ury); SELF.AddLine(llx, ury); SELF.AddLine(llx, lly);
-				SELF.AddExit(urx - llx, 0)
-			END AddRect;
-
-			(** append one path path another **)
-			PROCEDURE Append* (from: Path);
-				VAR pos, epos, cpos, n: INTEGER; eb: ElemBlock; cb: CoordBlock; elem: SHORTINT;
-			BEGIN
-				pos := 0; epos := 0; cpos := 0; eb := from.firstEB; cb := from.firstCB;
-				WHILE pos < from.elems DO
-					IF epos = ElemBlockSize THEN
-						eb := eb.next; epos := 0
-					END;
-					elem := eb.elem[epos]; INC(epos);
-					AddElem(SELF, elem);
-					n := Coords[elem];
-					WHILE n > 0 DO
-						IF cpos = CoordBlockSize THEN
-							cb := cb.next; cpos := 0
-						END;
-						AddCoord(SELF, cb.x[cpos], cb.y[cpos]);
-						INC(cpos); DEC(n)
-					END;
-					INC(pos)
-				END
-			END Append;
-
-			(** enumerate path elements **)
-			PROCEDURE Enumerate* (enum: Enumerator; VAR data: EnumData);
-				VAR eb: ElemBlock; cb: CoordBlock; pos, epos, cpos: INTEGER;
-
-				PROCEDURE get (VAR x, y: REAL);
-				BEGIN
-					IF cpos = CoordBlockSize THEN
-						cb := cb.next; cpos := 0;
-					END;
-					x := cb.x[cpos]; y := cb.y[cpos]; INC(cpos);
-				END get;
-
-			BEGIN
-				eb := SELF.firstEB; cb := SELF.firstCB;
-				pos := 0; epos := 0; cpos := 0;
-				WHILE pos < SELF.elems DO
-					IF epos = ElemBlockSize THEN
-						eb := eb.next; epos := 0;
-					END;
-					data.elem := eb.elem[epos];
-					CASE data.elem OF
-					| Enter: get(data.dx, data.dy); get(data.x, data.y);
-					| Line: get(data.x, data.y);
-					| Arc: get(data.x0, data.y0); get(data.x1, data.y1); get(data.x2, data.y2); get(data.x, data.y);
-					| Bezier: get(data.x1, data.y1); get(data.x2, data.y2); get(data.x, data.y);
-					| Exit: get(data.dx, data.dy);
-					END;
-					enum(data);
-					INC(pos); INC(epos);
-				END
-			END Enumerate;
-
-			(** enumerate flattened path, i.e. arcs and bezier curves will be approximated with lines **)
-			PROCEDURE EnumFlattened* (flatness: REAL; enum: Enumerator; VAR data: EnumData);
-				VAR eb: ElemBlock; cb: CoordBlock; pos, epos, cpos: INTEGER; x0, y0, x1, y1, x2, y2, x, y: REAL;
-
-				PROCEDURE get (VAR x, y: REAL);
-				BEGIN
-					IF cpos = CoordBlockSize THEN
-						cb := cb.next; cpos := 0;
-					END;
-					x := cb.x[cpos]; y := cb.y[cpos]; INC(cpos);
-				END get;
-
-			BEGIN
-				eb := SELF.firstEB; cb := SELF.firstCB;
-				pos := 0; epos := 0; cpos := 0;
-				WHILE pos < SELF.elems DO
-					IF epos = ElemBlockSize THEN
-						eb := eb.next; epos := 0
-					END;
-					data.elem := eb.elem[epos];
-					CASE data.elem OF
-					| Enter:
-						get(data.dx, data.dy); get(data.x, data.y);
-						enum(data)
-					| Line:
-						get(data.x, data.y);
-						enum(data)
-					| Arc:
-						get(x0, y0); get(x1, y1); get(x2, y2); get(x, y);
-						EnumArc(x0, y0, x1, y1, x2, y2, x, y, flatness, enum, data)
-					| Bezier:
-						get(x1, y1); get(x2, y2); get(x, y);
-						EnumBezier(x1, y1, x2, y2, x, y, flatness, enum, data);
-						(* why this? data.elem := Line; data.x := x; data.y := y; enum(data) *)
-					| Exit:
-						get(data.dx, data.dy);
-						enum(data);
-					END;
-					INC(pos); INC(epos)
-				END
-			END EnumFlattened;
-
-			(** calculate path length **)
-			PROCEDURE Length* (flatness: REAL): REAL;
-				VAR data: LengthData;
-			BEGIN
-				data.len := 0;
-				SELF.EnumFlattened(flatness, EnumLength, data);
-				RETURN data.len
-			END Length;
-
-			(** return whether path is empty **)
-			PROCEDURE Empty* (): BOOLEAN;
-			BEGIN
-				RETURN SELF.elems = 0
-			END Empty;
-
-			(** calculate bounding box of path **)
-			PROCEDURE GetBox* (VAR llx, lly, urx, ury: REAL);
-				VAR data: QueryData;
-			BEGIN
-				data.llx := MAX(REAL); data.lly := MAX(REAL); data.urx := MIN(REAL); data.ury := MIN(REAL);
-				SELF.EnumFlattened(1, EnumBoxElem, data);
-				llx := data.llx; lly := data.lly; urx := data.urx; ury := data.ury
-			END GetBox;
-			
-			(**--- Path Operations ---**)
-
-			(** put reversed source path into destination path; dst remains unchanged if src is empty **)
-			PROCEDURE ReverseTo* (dst: Path);
-				VAR
-					elems, sepos, scpos, depos, dcpos: INTEGER; dstEB, nextEB, srcEB, eb: ElemBlock;
-					dstCB, nextCB, srcCB: CoordBlock; dx, dy, x, y, x0, y0, x1, y1, x2, y2: REAL;
-
-				PROCEDURE get (VAR x, y: REAL);
-				BEGIN
-					IF scpos = CoordBlockSize THEN
-						srcCB := srcCB.next; scpos := 0
-					END;
-					x := srcCB.x[scpos]; y := srcCB.y[scpos]; INC(scpos)
-				END get;
-
-				PROCEDURE put (x, y: REAL);
-					VAR cb: CoordBlock;
-				BEGIN
-					IF dcpos = 0 THEN
-						IF nextCB # NIL THEN cb := nextCB; nextCB := cb.next
-						ELSE NEW(cb)
-						END;
-						cb.next := dstCB; dstCB := cb;
-						dcpos := CoordBlockSize
-					END;
-					DEC(dcpos); INC(dstEB.coords);
-					dstCB.x[dcpos] := x; dstCB.y[dcpos] := y
-				END put;
-
-			BEGIN
-				ASSERT(SELF # dst, 100);
-				elems := SELF.elems;
-				IF elems > 0 THEN
-					IF dst.firstEB # NIL THEN dstEB := dst.firstEB; dstEB.coords := 0; nextEB := dstEB.next; dstEB.next := NIL
-					ELSE NEW(dstEB); nextEB := NIL
-					END;
-					IF dst.firstCB # NIL THEN dstCB := dst.firstCB; nextCB := dstCB.next; dstCB.next := NIL
-					ELSE NEW(dstCB); nextCB := NIL
-					END;
-					dst.lastEB := dstEB; dst.lastCB := dstCB;
-					srcEB := SELF.firstEB; srcCB := SELF.firstCB;
-					sepos := 0; scpos := 0;
-					depos := (SELF.elems-1) MOD ElemBlockSize + 1; dcpos := (SELF.coords-1) MOD CoordBlockSize + 1;
-					REPEAT
-						(*
-							store reverted path in dst:
-								- segment end points become end points of their inverted successors
-								- order of control points is reversed
-								- directions are inverted
-						*)
-						IF sepos = ElemBlockSize THEN
-							srcEB := srcEB.next; sepos := 0
-						END;
-						IF depos = 0 THEN
-							IF nextEB # NIL THEN eb := nextEB; eb.coords := 0; nextEB := eb.next
-							ELSE NEW(eb)
-							END;
-							eb.next := dstEB; dstEB := eb;
-							depos := ElemBlockSize
-						END;
-						DEC(depos);
-						CASE srcEB.elem[sepos] OF
-						| Enter:
-							dstEB.elem[depos] := Exit;
-							get(dx, dy); get(x, y);
-							put(-dx, -dy); put(x, y)
-						| Line:
-							dstEB.elem[depos] := Line;
-							get(x, y);
-							put(x, y)
-						| Arc:
-							dstEB.elem[depos] := Arc;
-							get(x0, y0); get(x1, y1); get(x2, y2); get(x, y);
-							put(x1, y1); put(x2, y2); put(x0, y0); put(x, y)
-						| Bezier:
-							dstEB.elem[depos] := Bezier;
-							get(x1, y1); get(x2, y2); get(x, y);
-							put(x1, y1); put(x2, y2); put(x, y)
-						| Exit:
-							dstEB.elem[depos] := Enter;
-							get(dx, dy);
-							put(-dx, -dy)
-						END;
-						INC(sepos); DEC(elems)
-					UNTIL elems = 0;
-					dst.firstEB := dstEB; dst.firstCB := dstCB;
-					dst.elems := SELF.elems; dst.coords := SELF.coords
-				END
-			END ReverseTo;
-
-			(** return copy of source path in destination path **)
-			PROCEDURE CopyTo* (dst: Path);
-				VAR srcEB, dstEB: ElemBlock; n: INTEGER; srcCB, dstCB: CoordBlock;
-			BEGIN
-				IF SELF # dst THEN
-					IF dst.firstEB = NIL THEN NEW(dst.firstEB) END;
-					srcEB := SELF.firstEB; dstEB := dst.firstEB;
-					LOOP
-						IF srcEB = SELF.lastEB THEN n := (SELF.elems-1) MOD ElemBlockSize + 1
-						ELSE n := ElemBlockSize
-						END;
-						WHILE n > 0 DO
-							DEC(n); dstEB.elem[n] := srcEB.elem[n]
-						END;
-						dstEB.coords := srcEB.coords;
-						IF srcEB = SELF.lastEB THEN EXIT END;
-						IF dstEB.next = NIL THEN NEW(dstEB.next) END;
-						srcEB := srcEB.next; dstEB := dstEB.next
-					END;
-					dst.lastEB := dstEB; dstEB.next := NIL;
-
-					IF dst.firstCB = NIL THEN NEW(dst.firstCB) END;
-					srcCB := SELF.firstCB; dstCB := dst.firstCB;
-					LOOP
-						IF srcCB = SELF.lastCB THEN n := (SELF.coords-1) MOD CoordBlockSize + 1
-						ELSE n := CoordBlockSize
-						END;
-						WHILE n > 0 DO
-							DEC(n); dstCB.x[n] := srcCB.x[n]; dstCB.y[n] := srcCB.y[n]
-						END;
-						IF srcCB = SELF.lastCB THEN EXIT END;
-						IF dstCB.next = NIL THEN NEW(dstCB.next) END;
-						srcCB := srcCB.next; dstCB := dstCB.next
-					END;
-					dst.lastCB := dstCB; dstCB.next := NIL;
-					dst.elems := SELF.elems; dst.coords := SELF.coords
-				END
-			END CopyTo;
-
-			(** apply transformation to all coordinates in path **)
-			PROCEDURE Apply* (VAR mat: GfxMatrix.Matrix);
-				VAR eb: ElemBlock; cb: CoordBlock; pos, epos, cpos: INTEGER;
-
-				PROCEDURE point (VAR b: CoordBlock; VAR idx: INTEGER);
-				BEGIN
-					IF idx = CoordBlockSize THEN
-						b := b.next; idx := 0
-					END;
-					GfxMatrix.Apply(mat, b.x[idx], b.y[idx], b.x[idx], b.y[idx]);
-					INC(idx)
-				END point;
-
-				PROCEDURE vector (VAR b: CoordBlock; VAR idx: INTEGER);
-				BEGIN
-					IF idx = CoordBlockSize THEN
-						b := b.next; idx := 0
-					END;
-					GfxMatrix.ApplyToVector(mat, b.x[idx], b.y[idx], b.x[idx], b.y[idx]);
-					INC(idx)
-				END vector;
-
-			BEGIN
-				eb := SELF.firstEB; cb := SELF.firstCB;
-				pos := 0; epos := 0; cpos := 0;
-				WHILE pos < SELF.elems DO
-					IF epos = ElemBlockSize THEN
-						eb := eb.next; epos := 0
-					END;
-					CASE eb.elem[epos] OF
-					| Enter: vector(cb, cpos); point(cb, cpos)
-					| Line: point(cb, cpos)
-					| Arc: point(cb, cpos); point(cb, cpos); point(cb, cpos); point(cb, cpos)
-					| Bezier: point(cb, cpos); point(cb, cpos); point(cb, cpos)
-					| Exit: vector(cb, cpos)
-					END;
-					INC(pos); INC(epos)
-				END
-			END Apply;
-			
-			(** try to close disconnected enter/exit points by modifying their direction vectors **)
-			PROCEDURE Close* ();
-				CONST
-					eps = 0.001;
-
-				VAR
-					pos, epos, cpos, p, spos: INTEGER; eb: ElemBlock; cb, b, sb: CoordBlock; dx, dy, cx, cy, sx, sy, sdx, sdy, x, y, edx, edy: REAL;
-					data: DirData;
-
-				PROCEDURE get (VAR x, y: REAL);
-				BEGIN
-					IF cpos = CoordBlockSize THEN
-						cb := cb.next; cpos := 0
-					END;
-					x := cb.x[cpos]; y := cb.y[cpos];
-					INC(cpos)
-				END get;
-
-			BEGIN
-				pos := 0; epos := 0; cpos := 0; eb := SELF.firstEB; cb := SELF.firstCB;
-				WHILE pos < SELF.elems DO
-					IF epos = ElemBlockSize THEN
-						eb := eb.next; epos := 0
-					END;
-					CASE eb.elem[epos] OF
-					| Enter:
-						b := cb; p := cpos;
-						get(dx, dy); get(cx, cy);
-						IF (dx = 0) & (dy = 0) THEN
-							sb := b; spos := p; sx := cx; sy := cy; sdx := 0; sdy := 0
-						END
-					| Line:
-						get(x, y); dx := x - cx; dy := y - cy; cx := x; cy := y;
-						IF (sdx = 0) & (sdy = 0) THEN
-							sdx := dx; sdy := dy
-						END
-					| Arc:
-						data.sdx := 0; data.sdy := 0; data.cx := cx; data.cy := cy; data.x := cx; data.y := cy;
-						get(data.x0, data.y0); get(data.x1, data.y1); get(data.x2, data.y2); get(cx, cy);
-						EnumArc(data.x0, data.y0, data.x1, data.y1, data.x2, data.y2, cx, cy, 1.0, GetDir, data);
-						IF (sdx = 0) & (sdy = 0) THEN
-							sdx := data.sdx; sdy := data.sdy
-						END;
-						dx := data.edx; dy := data.edy
-					| Bezier:
-						get(x, y);
-						IF (sdx = 0) & (sdy = 0) THEN
-							sdx := x - cx; sdy := y - cy
-						END;
-						get(x, y); get(cx, cy); dx := cx - x; dy := cy - y
-					| Exit:
-						b := cb; p := cpos;
-						get(edx, edy);
-						IF (edx = 0) & (edy = 0) & (ABS(x - sx) <= eps) & (ABS(y - sy) <= eps) THEN
-							IF spos = CoordBlockSize THEN
-								sb := sb.next; spos := 0
-							END;
-							sb.x[spos] := dx; sb.y[spos] := dy;
-							IF p = CoordBlockSize THEN
-								b := b.next; p := 0
-							END;
-							b.x[p] := sdx; b.y[p] := sdy
-						END
-					END;
-					INC(pos); INC(epos)
-				END
-			END Close;
-
-			(** split subpath in two at given offset (resulting subpaths may be flattened in the process) **)
-			PROCEDURE Split* (offset: REAL; head, tail: Path);
-				VAR data: SplitData;
-			BEGIN
-				IF offset <= 0 THEN
-					SELF.CopyTo(tail); head.Clear()
-				ELSIF offset >= SELF.Length(1) THEN
-					SELF.CopyTo(head); tail.Clear()
-				ELSE
-					head.Clear(); tail.Clear();
-					data.offset := offset; data.head := head; data.tail := tail;
-					SELF.EnumFlattened(1, EnumSplit, data)
-				END
-			END Split;
-
-			(** return projection of point onto path **)
-			PROCEDURE ProjectToPath* (x, y: REAL; VAR u, v: REAL);
-				VAR data: ProjectData;
-			BEGIN
-				data.px := x; data.py := y; data.dist := MAX(REAL); data.rx := MAX(REAL); data.ry := MAX(REAL);
-				SELF.EnumFlattened(1, EnumProject, data);
-				u := data.rx; v := data.ry
-			END ProjectToPath;
-
-			(**--- Path Queries ---**)
-
-			(** return whether rectangle is completely inside (closed) path **)
-			PROCEDURE InPath* (llx, lly, urx, ury: REAL; evenOdd: BOOLEAN): BOOLEAN;
-				VAR data: QueryData;
-			BEGIN
-				data.thorough := TRUE; data.sum := 0; data.hit := FALSE; data.llx := llx; data.lly := lly; data.urx := urx; data.ury := ury;
-				SELF.EnumFlattened(1, EnumQuery, data);
-				RETURN data.hit OR evenOdd & ODD(ABS(data.sum) DIV 2) OR ~evenOdd & (data.sum # 0)
-			END InPath;
-
-			(** return whether rectangle intersects SELF **)
-			PROCEDURE OnPath* (llx, lly, urx, ury: REAL): BOOLEAN;
-				VAR data: QueryData;
-			BEGIN
-				data.thorough := FALSE; data.hit := FALSE; data.llx := llx; data.lly := lly; data.urx := urx; data.ury := ury;
-				SELF.EnumFlattened(1, EnumQuery, data);
-				RETURN data.hit
-			END OnPath;
-
-		END Path;
-
-		(** path scanner **)
-		(**
-			Path scanners can be used to iterate over a path under client control. The scanner's elem field specifies what
-			the current element is, whereas the remaining fields contain the parameters for that element. A Stop element
-			indicates that the end of the path has been reached.
-		**)		
-		Scanner* = RECORD
-			path*: Path;	(** visited path **)
-			pos*: INTEGER;	(** element position **)
-			elem*: INTEGER;	(** current path element **)
-			x*, y*: REAL;	(** current end coordinates **)
-			dx*, dy*: REAL;	(** direction vector **)
-			x0*, y0*, x1*, y1*, x2*, y2*: REAL;	(** additional control point coordinates **)
-			curEB: ElemBlock;	(* current element block *)
-			curCB: CoordBlock;	(* current coordinate block *)
-			epos, cpos: INTEGER;	(* next element and coordinate position within current block *)
-			
-			(** open scanner on path and load parameters of element at given position **)
-			PROCEDURE Open* (path: Path; pos: INTEGER);
-			BEGIN
-				SELF.path := path;
-				SELF.curEB := path.firstEB; SELF.curCB := path.firstCB;
-				SELF.pos := pos; SELF.epos := pos; SELF.cpos := 0;
-				WHILE SELF.epos > ElemBlockSize DO
-					DEC(SELF.epos, ElemBlockSize);
-					INC(SELF.cpos, SELF.curEB.coords);
-					SELF.curEB := SELF.curEB.next
-				END;
-				pos := 0;
-				WHILE pos < SELF.epos DO
-					SELF.cpos := SELF.cpos + Coords[SELF.curEB.elem[pos]]; INC(pos)
-				END;
-				WHILE SELF.cpos > CoordBlockSize DO
-					DEC(SELF.cpos, CoordBlockSize);
-					SELF.curCB := SELF.curCB.next
-				END;
-				IF SELF.pos = path.elems THEN
-					SELF.elem := Stop
-				ELSE
-					IF SELF.epos = ElemBlockSize THEN	(* at end of current block *)
-						SELF.curEB := SELF.curEB.next; SELF.epos := 0
-					END;
-					SELF.elem := SELF.curEB.elem[SELF.epos]; INC(SELF.epos);
-					CASE SELF.elem OF
-					| Enter: get(SELF.dx, SELF.dy); get(SELF.x, SELF.y)
-					| Line: get(SELF.x, SELF.y)
-					| Arc: get(SELF.x0, SELF.y0); get(SELF.x1, SELF.y1); get(SELF.x2, SELF.y2); get(SELF.x, SELF.y)
-					| Bezier: get(SELF.x1, SELF.y1); get(SELF.x2, SELF.y2); get(SELF.x, SELF.y)
-					| Exit: get(SELF.dx, SELF.dy)
-					END
-				END
-			END Open;
-
-			(** advance to next element and load its parameters **)
-			PROCEDURE Scan* ();
-			BEGIN
-				IF SELF.pos < SELF.path.elems THEN
-					INC(SELF.pos);
-					IF SELF.pos = SELF.path.elems THEN
-						SELF.elem := Stop
-					ELSE
-						IF SELF.epos = ElemBlockSize THEN	(* at end of current block *)
-							SELF.curEB := SELF.curEB.next; SELF.epos := 0
-						END;
-						SELF.elem := SELF.curEB.elem[SELF.epos]; INC(SELF.epos);
-						CASE SELF.elem OF
-						| Enter: get(SELF.dx, SELF.dy); get(SELF.x, SELF.y)
-						| Exit: get(SELF.dx, SELF.dy)
-						| Line: get(SELF.x, SELF.y)
-						| Arc: get(SELF.x0, SELF.y0); get(SELF.x1, SELF.y1); get(SELF.x2, SELF.y2); get(SELF.x, SELF.y)
-						| Bezier: get(SELF.x1, SELF.y1); get(SELF.x2, SELF.y2); get(SELF.x, SELF.y)
-						END
-					END
-				END
-			END Scan;
-			
-			PROCEDURE get (VAR x, y: REAL);
-			BEGIN
-				IF SELF.cpos = CoordBlockSize THEN
-					SELF.curCB := SELF.curCB.next; SELF.cpos := 0
-				END;
-				x := SELF.curCB.x[SELF.cpos]; y := SELF.curCB.y[SELF.cpos]; INC(SELF.cpos)
-			END get;			
-
-		END (* Scanner *);
-
-		(** path enumeration **)
-		EnumData* = RECORD
-			elem*: INTEGER;	(** current path element **)
-			x*, y*, dx*, dy*, x0*, y0*, x1*, y1*, x2*, y2*: REAL;	(** element parameters **)
-		END;
-
-		Enumerator* = PROCEDURE (VAR data: EnumData);
-
-		ProjectData = RECORD (EnumData)
-			px, py: REAL;	(* point coordinates *)
-			rx, ry: REAL;	(* projection coordinates *)
-			sx, sy: REAL;	(* previous coordinates *)
-			dist: REAL;	(* distance of projection to original point *)
-		END;
-
-		QueryData = RECORD (EnumData)
-			llx, lly, urx, ury: REAL;	(* query rectangle *)
-			sx, sy: REAL;	(* previous coordinates *)
-			code: SET;	(* clip code of previous point *)
-			sum: LONGINT;	(* number of ray crossings for inside test *)
-			hit, thorough: BOOLEAN;
-		END;
-
-		LengthData = RECORD (EnumData)
-			sx, sy: REAL;	(* previous coordinates *)
-			len: REAL;
-		END;
-
-		DirData = RECORD (EnumData)
-			cx, cy: REAL;
-			sdx, sdy: REAL;
-			edx, edy: REAL;
-		END;
-
-		SplitData = RECORD (EnumData)
-			head, tail: Path;
-			offset: REAL;
-			sx, sy: REAL;
-			sdx, sdy: REAL
-		END;
-
-
-	VAR
-		Coords: ARRAY Exit+1 OF SHORTINT;	(* number of coordinate pairs for each element type *)
-
-
-	(**--- Path Construction ---**)
-
-	PROCEDURE AddElem (path: Path; elem: SHORTINT);
-		VAR elems: INTEGER; eb: ElemBlock;
-	BEGIN
-		elems := path.elems MOD ElemBlockSize;
-		IF (elems = 0) & (path.elems > 0) THEN
-			NEW(eb); path.lastEB.next := eb; path.lastEB := eb;
-		END;
-		path.lastEB.elem[elems] := elem;
-		INC(path.elems)
-	END AddElem;
-
-	PROCEDURE AddCoord (path: Path; x, y: REAL);
-		VAR coords: INTEGER; cb: CoordBlock;
-	BEGIN
-		coords := path.coords MOD CoordBlockSize;
-		IF (coords = 0) & (path.coords > 0) THEN
-			NEW(cb); path.lastCB.next := cb; path.lastCB := cb
-		END;
-		path.lastCB.x[coords] := x; path.lastCB.y[coords] := y;
-		INC(path.coords); INC(path.lastEB.coords)
-	END AddCoord;
-
-
-	(**--- Enumerating (Flattened) Paths ---**)
-
-	(**
-		In addition to being scanned, path elements may also be enumerated. The advantage of enumerating path
-		elements is that arcs and bezier curves can be enumerated as a sequence of lines approximating the original
-		curve. Besides, natural splines can enumerated in terms of regular path elements.
-	**)
-
-	(** enumerate arc as a sequence of lines with maximal error 'flatness'; current point must be in (data.x, data.y) **)
-	PROCEDURE EnumArc* (x0, y0, x1, y1, x2, y2, x, y, flatness: REAL; enum: Enumerator; VAR data: EnumData);
-		CONST
-			eps = 1.0E-3;
-		VAR
-			lx, ly, sense, xs, ys, xe, ye, dt, p2, tmp, p1, dx1, dx2, dy1, dy2, sx, sy, tx, ty, limit, dx, dy, tlen, ex, ey: REAL;
-			positive: BOOLEAN;
-	BEGIN
-		(* algorithm: D. Fellner & C. Helmberg, Robust Rendering of General Ellipses and Elliptical Arcs, ACM TOG July 1993 *)
-		data.elem := Line;
-		x1 := x1 - x0; y1 := y1 - y0;
-		x2 := x2 - x0; y2 := y2 - y0;
-		IF ABS(x1 * y2 - y1 * x2) <= eps * ABS(x1 * x2 + y1 * y2) THEN	(* approximate with line *)
-			data.x := x; data.y := y; enum(data);
-			RETURN
-		END;
-
-		lx := ABS(x1) + ABS(x2); ly := ABS(y1) + ABS(y2);
-		IF (lx <= ly) & (lx <= flatness) OR (ly <= lx) & (ly <= flatness) THEN	(* radius smaller than flatness *)
-			data.x := x; data.y := y; enum(data);
-			RETURN
-		END;
-
-		IF flatness < eps THEN flatness := eps END;
-		IF x1 * y2 > y1 * x2 THEN sense := 1 ELSE sense := -1 END;
-		xs := data.x - x0; ys := data.y - y0;
-		xe := x - x0; ye := y - y0;
-		IF lx >= ly THEN dt := flatness/lx
-		ELSE dt := flatness/ly
-		END;
-
-		(* find first point on arc *)
-		p2 := xs * y2 - ys * x2;
-		IF ABS(p2) < eps THEN	(* (x2, y2) on start vector *)
-			tmp := x1; x1 := x2; x2 := -tmp;
-			tmp := y1; y1 := y2; y2 := -tmp;
-			p1 := 0
-		ELSE
-			p1 := xs * y1 - ys * x1
-		END;
-		IF ABS(p1) < eps THEN	(* (x1, y1) on start vector *)
-			IF xs * x1 + ys * y1 < -eps THEN	(* on opposite side of origin *)
-				x1 := -x1; y1 := -y1;
-				x2 := -x2; y2 := -y2
-			END;
-			IF ABS(x1 - xs) + ABS(y1 - ys) > flatness THEN
-				data.x := x0 + x1; data.y := y0 + y1;
-				enum(data)
-			END;
-			dx1 := 0; dx2 := 0; dy1 := 0; dy2 := 0
-		ELSE	(* search start point on ellipse *)
-			IF (p1 > 0) = (p2 > 0) THEN
-				tmp := x1; x1 := x2; x2 := -tmp;
-				tmp := y1; y1 := y2; y2 := -tmp;
-				p1 := p2
-			END;
-			IF p1 * sense > 0 THEN
-				x1 := -x1; y1 := -y1;
-				x2 := -x2; y2 := -y2
-			END;
-			dx1 := 0; dx2 := 0; dy1 := 0; dy2 := 0;
-			REPEAT
-				tmp := dx1;
-				dx1 := (x2 - 0.5 * dx2) * dt; dx2 := (x1 + 0.5 * tmp) * dt;
-				x1 := x1 + dx1; x2 := x2 - dx2;
-				tmp := dy1;
-				dy1 := (y2 - 0.5 * dy2) * dt; dy2 := (y1 + 0.5 * tmp) * dt;
-				y1 := y1 + dy1; y2 := y2 - dy2
-			UNTIL (xs * y1 - ys * x1) * sense >= 0;
-			data.x := x0 + x1; data.y := y0 + y1;
-			enum(data)
-		END;
-
-		sx := x1; sy := y1;	(* start point of current line *)
-		tx := 0; ty := 0;	(* (approximate) tangent vector at start point *)
-		limit := flatness * flatness;
-		positive := ((ye * x1 - xe * y1) * sense > 0);
-		LOOP
-			tmp := dx1;
-			dx1 := (x2 - 0.5 * dx2) * dt; dx2 := (x1 + 0.5 * tmp) * dt;
-			x1 := x1 + dx1; x2 := x2 - dx2;
-			tmp := dy1;
-			dy1 := (y2 - 0.5 * dy2) * dt; dy2 := (y1 + 0.5 * tmp) * dt;
-			y1 := y1 + dy1; y2 := y2 - dy2;
-			p1 := (ye * x1 - xe * y1) * sense;
-			IF p1 > 0 THEN
-				positive := TRUE
-			ELSIF positive THEN
-				EXIT
-			END;
-			dx := x1 - sx; dy := y1 - sy;
-			IF (tx = 0) & (ty = 0) THEN	(* first point *)
-				tx := dx; ty := dy; tlen := tx * tx + ty * ty
-			ELSE
-				tmp := dx * ty - dy * tx;
-				IF (tmp * tmp)/tlen > limit THEN	(* distance from new point to tangent vector is greater than flatness *)
-					sx := ex; sy := ey;
-					data.x := x0 + sx; data.y := y0 + sy;
-					enum(data);
-					tx := dx; ty := dy; tlen := tx * tx + ty * ty
-				END
-			END;
-			ex := x1; ey := y1
-		END;
-		data.x := x; data.y := y;
-		enum(data)
-	END EnumArc;
-
-	(** enumerate bezier curve as a sequence of lines with maximal error 'flatness'; current point must be in (data.x, data.y) **)
-	PROCEDURE EnumBezier* (x1, y1, x2, y2, x, y, flatness: REAL; enum: Enumerator; VAR data: EnumData);
-		CONST eps = 1.0E-8;
-		VAR f2, ax, bx, t, x01, x11, x12, x22, x23, y01, y11, y12, y22, y23: REAL;
-
-		PROCEDURE subdiv (t, x0, x1, x2, x3: REAL; VAR a1, a2, m, b1, b2: REAL);
-			VAR s, x12: REAL;
-		BEGIN
-			s := 1-t;
-			a1 := s * x0 + t * x1; b2 := s * x2 + t * x3; x12 := s * x1 + t * x2;
-			a2 := s * a1 + t * x12; b1 := s * x12 + t * b2;
-			m := s * a2 + t * b1
-		END subdiv;
-
-		PROCEDURE draw (x1, y1, x2, y2, x, y: REAL);
-			VAR x01, x11, x12, x22, x23, y01, y11, y12, y22, y23, dx, dy, ex, ey, cp: REAL;
-		BEGIN
-			subdiv(0.5, data.x, x1, x2, x, x01, x11, x12, x22, x23);
-			subdiv(0.5, data.y, y1, y2, y, y01, y11, y12, y22, y23);
-			dx := x12 - data.x; dy := y12 - data.y;
-			ex := x - data.x; ey := y - data.y;
-			cp := dx*ey - dy*ex;
-			IF cp*cp <= f2 * (ex*ex + ey*ey) THEN	(* flat enough *)
-				data.x := x; data.y := y; enum(data)
-			ELSE
-				draw(x01, y01, x11, y11, x12, y12);
-				draw(x22, y22, x23, y23, x, y)
-			END
-		END draw;
-
-		PROCEDURE solve (a, b, c: REAL; VAR t1, t2: REAL; VAR n: INTEGER);
-			VAR d, e, t: REAL;
-		BEGIN
-			n := 0; d := b * b - a * c;
-			IF d >= 0 THEN
-				d := Math.sqrt(d); e := -b + d;
-				IF (a * e > 0) & (ABS(e) < ABS(a)) THEN
-					t1 := e/a; n := 1;
-					e := -b - d;
-					IF (d > 0) & (a * e > 0) & (ABS(e) < ABS(a)) THEN
-						t2 := e/a; n := 2;
-						IF t2 < t1 THEN t := t1; t1 := t2; t2 := t END
-					END
-				ELSE
-					e := -b - d;
-					IF (a * e > 0) & (ABS(e) < ABS(a)) THEN
-						t1 := e/a; n := 1
-					END
-				END
-			END;
-			ASSERT((n = 0) OR (n = 1) & (0 < t1) & (t1 < 1) OR (n = 2) & (0 < t1) & (t1 < t2) & (t2 < 1))
-		END solve;
-
-		PROCEDURE norm2y (x1, y1, x2, y2, x, y: REAL);
-			VAR t1, t2, x01, x11, x12, x22, x23, y01, y11, y12, y22, y23: REAL; n: INTEGER;
-		BEGIN
-			solve(y - data.y + 3*(y1 - y2), data.y - 2*y1 + y2, y1 - data.y, t1, t2, n);
-			IF n = 0 THEN
-				draw(x1, y1, x2, y2, x, y)
-			ELSE
-				subdiv(t1, data.x, x1, x2, x, x01, x11, x12, x22, x23);
-				subdiv(t1, data.y, y1, y2, y, y01, y11, y12, y22, y23);
-				draw(x01, y01, x11, y11, x12, y12);
-				IF n = 2 THEN
-					t2 := (t2 - t1)/(1-t1);
-					subdiv(t2, data.x, x22, x23, x, x01, x11, x12, x22, x23);
-					subdiv(t2, data.y, y22, y23, y, y01, y11, y12, y22, y23);
-					draw(x01, y01, x11, y11, x12, y12)
-				END;
-				draw(x22, y22, x23, y23, x, y)
-			END
-		END norm2y;
-
-		PROCEDURE norm2x (x1, y1, x2, y2, x, y: REAL);
-			VAR t1, t2, x01, x11, x12, x22, x23, y01, y11, y12, y22, y23: REAL; n: INTEGER;
-		BEGIN
-			solve(x - data.x + 3*(x1 - x2), data.x - 2*x1 + x2, x1 - data.x, t1, t2, n);
-			IF n = 0 THEN
-				norm2y(x1, y1, x2, y2, x, y)
-			ELSE
-				subdiv(t1, data.x, x1, x2, x, x01, x11, x12, x22, x23);
-				subdiv(t1, data.y, y1, y2, y, y01, y11, y12, y22, y23);
-				norm2y(x01, y01, x11, y11, x12, y12);
-				IF n = 2 THEN
-					t2 := (t2 - t1)/(1-t1);
-					subdiv(t2, data.x, x22, x23, x, x01, x11, x12, x22, x23);
-					subdiv(t2, data.y, y22, y23, y, y01, y11, y12, y22, y23);
-					norm2y(x01, y01, x11, y11, x12, y12)
-				END;
-				norm2y(x22, y22, x23, y23, x, y)
-			END
-		END norm2x;
-
-		PROCEDURE norm1y (x1, y1, x2, y2, x, y: REAL);
-			VAR ay, by, t, x01, x11, x12, x22, x23, y01, y11, y12, y22, y23: REAL;
-		BEGIN
-			ay := y - data.y + 3*(y1 - y2); by := data.y - 2*y1 + y2;
-			IF (ay * by < 0) & (ABS(by) < ABS(ay)) THEN
-				t := -by/ay;
-				subdiv(t, data.x, x1, x2, x, x01, x11, x12, x22, x23);
-				subdiv(t, data.y, y1, y2, y, y01, y11, y12, y22, y23);
-				norm2x(x01, y01, x11, y11, x12, y12);
-				norm2x(x22, y22, x23, y23, x, y)
-			ELSE
-				norm2x(x1, y1, x2, y2, x, y)
-			END
-		END norm1y;
-
-	BEGIN
-		data.elem := Line;
-		f2 := flatness * flatness;
-		IF f2 < eps THEN f2 := eps END;
-		ax := x - data.x + 3*(x1 - x2); bx := data.x - 2*x1 + x2;
-		IF (ax * bx < 0) & (ABS(bx) < ABS(ax)) THEN
-			t := -bx/ax;
-			subdiv(t, data.x, x1, x2, x, x01, x11, x12, x22, x23);
-			subdiv(t, data.y, y1, y2, y, y01, y11, y12, y22, y23);
-			norm1y(x01, y01, x11, y11, x12, y12);
-			norm1y(x22, y22, x23, y23, x, y)
-		ELSE
-			norm1y(x1, y1, x2, y2, x, y)
-		END
-	END EnumBezier;
-
-	(*
-	 * The code for the spline evaluation has been adapted from Beat Stamm's Graphic module. It handles natural open
-	 * and closed splines.
-	 *)
-
-	PROCEDURE SolveClosed (n: LONGINT; VAR x, y, d: ARRAY OF REAL);
-		VAR hn, dn, d0, d1, t1, t2: REAL; a, b, c, u: ARRAY MaxSplinePoints OF REAL; i: LONGINT;
-	BEGIN
-		hn := 1/(x[n - 1] - x[n - 2]); dn := 3 * (y[n - 1] - y[n - 2]) * hn * hn;
-		b[0] := 1/(x[1] - x[0]); a[0] := hn + 2*b[0]; c[0] := b[0];
-		d0 := 3 * (y[1] - y[0]) * b[0] * b[0]; d[0] := dn + d0;
-		u[0] := 1;
-		i := 1;
-		WHILE i < n - 2 DO
-			b[i] := 1/(x[i + 1] - x[i]); a[i] := 2 * (c[i - 1] + b[i]); c[i] := b[i];
-			d1 := 3 * (y[i + 1] - y[i]) * b[i] * b[i]; d[i] := d0 + d1; d0 := d1;
-			u[i] := 0;
-			INC(i)
-		END;
-		a[i] := 2 * b[i - 1] + hn; d[i] := dn + d0; u[i] := 1;
-
-		i := 0;
-		WHILE i < n - 2 DO
-			c[i] := c[i]/a[i];
-			a[i + 1] := a[i + 1] - c[i] * b[i];
-			INC(i)
-		END;
-		i := 1;
-		WHILE i < n - 1 DO
-			t1 := c[i - 1];
-			t2 := t1 * d[i - 1];
-			d[i] := d[i] - t2;
-			t2 := t1 * u[i - 1];
-			u[i] := u[i] - t2;
-			INC(i)
-		END;
-		d[n - 2] := d[n - 2]/a[n - 2];
-		u[n - 2] := u[n - 2]/a[n - 2];
-		i := n - 3;
-		WHILE i >= 0 DO
-			t1 := b[i] * d[i + 1];
-			d[i] := (d[i] - t1)/a[i];
-			t1 := b[i] * u[i + 1];
-			u[i] := (u[i] - t1)/a[i];
-			DEC(i)
-		END;
-
-		d0 := (d[0] + d[n - 2])/(u[0] + u[n - 2] + x[n - 1] - x[n - 2]);
-		i := 0;
-		WHILE i < n - 1 DO
-			d[i] := d[i] - d0 * u[i];
-			INC(i)
-		END;
-		d[n - 1] := d[0]
-	END SolveClosed;
-
-	PROCEDURE Solve (n: LONGINT; VAR x, y, d: ARRAY OF REAL);
-		VAR a, b, c: ARRAY MaxSplinePoints OF REAL; d0, d1, t: REAL; i: LONGINT;
-	BEGIN
-		b[0] := 1/(x[1] - x[0]); a[0] := 2*b[0]; c[0] := b[0];
-		d0 := 3 * (y[1] - y[0]) * b[0] * b[0]; d[0] := d0;
-		i := 1;
-		WHILE i < n - 1 DO
-			b[i] := 1/(x[i + 1] - x[i]); a[i] := 2 * (c[i - 1] + b[i]); c[i] := b[i];
-			d1 := 3 * (y[i + 1] - y[i]) * b[i] * b[i]; d[i] := d0 + d1; d0 := d1;
-			INC(i)
-		END;
-		a[i] := 2 * b[i - 1]; d[i] := d0;
-
-		i := 0;
-		WHILE i < n - 1 DO
-			c[i] := c[i]/a[i];
-			a[i + 1] := a[i + 1] - c[i] * b[i];
-			INC(i)
-		END;
-		i := 1;
-		WHILE i < n DO
-			t := c[i - 1] * d[i - 1];
-			d[i] := d[i] - t;
-			INC(i)
-		END;
-		d[n - 1] := d[n - 1]/a[n - 1];
-		i := n - 2;
-		WHILE i >= 0 DO
-			t := b[i] * d[i + 1];
-			d[i] := (d[i] - t)/a[i];
-			DEC(i)
-		END
-	END Solve;
-
-	(** enumerate natural spline as sequence of path elements; current point must be in (data.x, data.y) **)
-	PROCEDURE EnumSpline* (VAR x, y: ARRAY OF REAL; n: LONGINT; closed: BOOLEAN; enum: Enumerator; VAR data: EnumData);
-		VAR s, xp, yp: ARRAY MaxSplinePoints OF REAL; i: LONGINT; dx, dy, ds, ds2, bx, by, t: REAL;
-	BEGIN
-		ASSERT((n >= 2) & (n <= MaxSplinePoints));
-		ASSERT(~closed OR (x[0] = x[n - 1]) & (y[0] = y[n - 1]));
-		IF ~closed & (n = 2) THEN
-			data.elem := Line; data.x := x[1]; data.y := y[1]; enum(data)
-		ELSIF closed & (n = 3) THEN
-			data.elem := Arc; data.x0 := 0.5*(x[0] + x[1]); data.y0 := 0.5*(y[0] + y[1]); data.x1 := x[0]; data.y1 := y[0];
-			data.x2 := data.x0 + (data.y0 - data.y); data.y2 := data.y0 + (data.x - data.x0); enum(data)
-		ELSE
-			(* use arc length for parametrizing the spline *)
-			s[0] := 0.0;
-			i := 1;
-			WHILE i < n DO
-				dx := x[i] - x[i - 1]; dy := y[i] - y[i - 1];
-				s[i] := s[i - 1] + Math.sqrt(dx * dx + dy * dy) + 0.01;	(* make sure s[i] > s[i - 1] *)
-				INC(i)
-			END;
-
-			(* calculate derivatives *)
-			IF closed THEN
-				SolveClosed(n, s, x, xp);
-				SolveClosed(n, s, y, yp)
-			ELSE
-				Solve(n, s, x, xp);
-				Solve(n, s, y, yp)
-			END;
-			data.elem := Bezier;
-			i := 1;
-			WHILE i < n DO
-				ds := 1.0/(s[i] - s[i - 1]); ds2 := ds * ds;
-				dx := ds * (x[i] - x[i - 1]);
-				dy := ds * (y[i] - y[i - 1]);
-				bx := ds * (3*dx - 2*xp[i - 1] - xp[i]);
-				by := ds * (3*dy - 2*yp[i - 1] - yp[i]);
-				t := 1/ds;
-				data.x1 := x[i - 1] + (1/3)*xp[i - 1]*t;
-				data.y1 := y[i - 1] + (1/3)*yp[i - 1]*t;
-				t := 1/ds2;
-				data.x2 := 2*data.x1 - x[i - 1] + (1/3) * bx * t;
-				data.y2 := 2*data.y1 - y[i - 1] + (1/3) * by * t;
-				data.x := x[i]; data.y := y[i];
-				enum(data);
-				INC(i)
-			END
-		END
-	END EnumSpline;
-
-	
-	(**--- Path Queries ---**)
-
-	PROCEDURE Code (VAR data: QueryData; x, y: REAL): SET;
-		VAR code: SET;
-	BEGIN
-		code := {};
-		IF x < data.llx THEN INCL(code, Left)
-		ELSIF x > data.urx THEN INCL(code, Right)
-		END;
-		IF y < data.lly THEN INCL(code, Bottom)
-		ELSIF y > data.ury THEN INCL(code, Top)
-		END;
-		RETURN code
-	END Code;
-
-	PROCEDURE EnumQuery (VAR data: EnumData);
-		VAR x, y: REAL; code, cc: SET;
-	BEGIN
-		(*
-			The procedure uses a simplified version of the Cohen-Sutherland clipping algorithm. The endpoint of
-			the current line is consecutively clipped against all sides of the rectangle until both points of the line
-			are outside the rectangle with respect to one single rectangle border or until the clipped endpoint
-			is inside the rectangle.
-		*)
-		WITH data: QueryData DO
-			IF ~data.hit THEN
-				IF data.elem = Enter THEN
-					data.code := Code(data, data.x, data.y);
-					IF data.code = {} THEN	(* point inside rectangle *)
-						data.hit := TRUE
-					ELSE
-						data.sx := data.x; data.sy := data.y
-					END
-				ELSIF (data.elem = Line) & ((data.x # data.sx) OR (data.y # data.sy)) THEN
-					x := data.x; y := data.y;
-					LOOP
-						code := Code(data, x, y);
-						IF code = {} THEN	(* point inside rectangle *)
-							data.hit := TRUE;
-							EXIT
-						END;
-						cc := data.code * code;
-						IF cc # {} THEN	(* no intersection with rectangle *)
-							IF data.thorough THEN
-								(*
-									For every line crossing the rectangle's middle y coordinate, accumulate how often the rectangle's
-									midpoint lies to the left/right of the line
-								*)
-								y := 0.5*(data.lly + data.ury);
-								IF (data.sy <= y) & (y < data.y) OR (data.y <= y) & (y < data.sy) THEN
-									x := 0.5*(data.llx + data.urx);
-									IF (data.x - data.sx) * (y - data.sy) >= (data.y - data.sy) * (x - data.sx) THEN
-										INC(data.sum)
-									ELSE
-										DEC(data.sum)
-									END
-								END
-							END;
-							data.code := Code(data, data.x, data.y); data.sx := data.x; data.sy := data.y;
-							EXIT
-						END;
-						IF Left IN code THEN
-							y := data.sy + (y - data.sy) * (data.llx - data.sx)/(x - data.sx);
-							x := data.llx
-						ELSIF Right IN code THEN
-							y := data.sy + (y - data.sy) * (data.urx - data.sx)/(x - data.sx);
-							x := data.urx
-						ELSIF Bottom IN code THEN
-							x := data.sx + (x - data.sx) * (data.lly - data.sy)/(y - data.sy);
-							y := data.lly
-						ELSE	(* Top IN code *)
-							x := data.sx + (x - data.sx) * (data.ury - data.sy)/(y - data.sy);
-							y := data.ury
-						END
-					END
-				END
-			END
-		END
-	END EnumQuery;
-
-	PROCEDURE EnumBoxElem (VAR data: EnumData);
-	BEGIN
-		WITH data: QueryData DO
-			IF data.elem IN {Enter, Line} THEN
-				IF data.x < data.llx THEN data.llx := data.x END;
-				IF data.x > data.urx THEN data.urx := data.x END;
-				IF data.y < data.lly THEN data.lly := data.y END;
-				IF data.y > data.ury THEN data.ury := data.y END
-			END
-		END
-	END EnumBoxElem;
-
-	(** calculate bounding box of path **)
-	PROCEDURE GetBox* (path: Path; VAR llx, lly, urx, ury: REAL); (**DEPRECATED -- SVGRenderer *)
-	BEGIN
-		path.GetBox(llx, lly, urx, ury);
-	END GetBox;
-
-	(** calculate line length **)
-	PROCEDURE LineLength* (x0, y0, x1, y1: REAL): REAL;
-		VAR dx, dy: REAL;
-	BEGIN
-		dx := x1 - x0; dy := y1 - y0;
-		RETURN Math.sqrt(dx * dx + dy * dy)
-	END LineLength;
-
-	PROCEDURE EnumLength (VAR data: EnumData);
-		VAR dx, dy: REAL;
-	BEGIN
-		WITH data: LengthData DO
-			IF data.elem = Line THEN
-				dx := data.x - data.sx; dy := data.y - data.sy;
-				data.len := data.len + Math.sqrt(dx * dx + dy * dy)
-			END;
-			data.sx := data.x; data.sy := data.y
-		END
-	END EnumLength;
-
-	(** calculate arc length **)
-	PROCEDURE ArcLength* (sx, sy, ex, ey, x0, y0, x1, y1, x2, y2, flatness: REAL): REAL;
-		VAR data: LengthData;
-	BEGIN
-		data.x := sx; data.y := sy; data.sx := sx; data.sy := sy; data.len := 0;
-		EnumArc(x0, y0, x1, y1, x2, y2, ex, ey, flatness, EnumLength, data);
-		RETURN data.len
-	END ArcLength;
-
-	(** calculate bezier length **)
-	PROCEDURE BezierLength* (x0, y0, x1, y1, x2, y2, x3, y3, flatness: REAL): REAL;
-		VAR data: LengthData;
-	BEGIN
-		data.x := x0; data.y := y0; data.sx := x0; data.sy := y0; data.len := 0;
-		EnumBezier(x1, y1, x2, y2, x3, y3, flatness, EnumLength, data);
-		RETURN data.len
-	END BezierLength;
-
-
-	(**--- Path Operations ---**)
-
-	(** apply transformation to all coordinates in path **)
-	PROCEDURE Apply* (path: Path; VAR mat: GfxMatrix.Matrix); (**DEPRECATED -- Used in SVGRenderer *)
-	BEGIN
-		path.Apply(mat);
-	END Apply;
-
-	PROCEDURE GetDir (VAR data: EnumData);
-	BEGIN
-		WITH data: DirData DO
-			IF (data.sdx = 0) & (data.sdy = 0) THEN
-				data.sdx := data.x - data.cx; data.sdy := data.y - data.cy
-			END;
-			data.edx := data.x - data.cx; data.edy := data.y - data.cy;
-			data.cx := data.x; data.cy := data.y
-		END
-	END GetDir;
-
-	PROCEDURE EnumSplit (VAR data: EnumData);
-		VAR dx, dy, d, s, sx, sy: REAL;
-	BEGIN
-		WITH data: SplitData DO
-			CASE data.elem OF
-			| Enter:
-				IF data.offset > 0 THEN data.head.AddEnter(data.x, data.y, data.dx, data.dy)
-				ELSE data.tail.AddEnter(data.x, data.y, data.dx, data.dy)
-				END;
-				data.sx := data.x; data.sy := data.y
-			| Line:
-				IF data.offset > 0 THEN	(* still appending to head *)
-					dx := data.x - data.sx; dy := data.y - data.sy; d := Math.sqrt(dx * dx + dy * dy);
-					IF d > 0 THEN
-						IF d < data.offset THEN	(* doesn't reach split offset *)
-							data.head.AddLine(data.x, data.y);
-							data.offset := data.offset - d; data.sx := data.x; data.sy := data.y
-						ELSIF d > data.offset THEN	(* split within line *)
-							s := data.offset/d;
-							sx := data.sx + s * dx; sy := data.sy + s * dy;
-							data.head.AddLine(sx, sy); data.head.AddExit(dx, dy);	(* leave head... *)
-							data.tail.AddEnter(sx, sy, dx, dy); data.tail.AddLine(data.x, data.y);	(* ...and enter tail *)
-							data.offset := data.offset - d	(* now < 0 *)
-						ELSE	(* d = offset: delay until next line/exit *)
-							data.offset := 0; data.sx := data.x; data.sy := data.y; data.sdx := dx; data.sdy := dy
-						END
-					END
-				ELSIF data.offset < 0 THEN	(* appending to tail *)
-					data.tail.AddLine(data.x, data.y)
-				ELSE	(* split point at previous line end point *)
-					data.head.AddLine(data.sx, data.sy); data.head.AddExit(dx, dy);	(* leave head... *)
-					data.tail.AddEnter(data.sx, data.sy, data.sdx, data.sdy);	(* ...and enter tail *)
-					data.tail.AddLine(data.x, data.y);
-					data.offset := -1
-				END
-			| Exit:
-				IF data.offset > 0 THEN data.head.AddExit(data.dx, data.dy)
-				ELSIF data.offset < 0 THEN data.tail.AddExit(data.dx, data.dy)
-				ELSE data.head.AddLine(data.sx, data.sy); data.head.AddExit(data.dx, data.dy); data.offset := -1
-				END
-			END
-		END
-	END EnumSplit;
-
-	(**--- Geometry Support ---**)
-
-	(** compute intersection of two lines **)
-	PROCEDURE IntersectLines* (x1, y1, dx1, dy1, x2, y2, dx2, dy2: REAL; VAR x, y: REAL);
-		VAR d, t: REAL;
-	BEGIN
-		d := dx1 * dy2 - dy1 * dx2;
-		t := (x2 - x1) * dy2 - (y2 - y1) * dx2;
-		IF (ABS(d) >= 1) OR (ABS(d) * MAX(REAL) >= ABS(t)) THEN
-			t := t/d;
-			x := x1 + t * dx1; y := y1 + t * dy1
-		ELSE
-			x := 0.5*(x2 - x1); y := 0.5*(y2 - y1)
-		END
-	END IntersectLines;
-
-	(** compute intersection(s) of line with circle; returns number of solutions in nsol **)
-	PROCEDURE IntersectLineCircle* (sx, sy, tx, ty, mx, my, r: REAL; VAR x1, y1, x2, y2: REAL; VAR nsol: LONGINT);
-		VAR dx, dy, cx, cy, a2, b, c, d, t: REAL;
-	BEGIN
-		dx := tx - sx; dy := ty - sy;
-		cx := sx - mx; cy := sy - my;
-		a2 := 2 * (dx * dx + dy * dy);
-		b := 2 * (dx * cx + dy * cy);
-		c := cx * cx + cy * cy - r * r;
-		d := b * b - 2 * a2 * c;
-		IF d < 0 THEN
-			nsol := 0
-		ELSE
-			d := Math.sqrt(d);
-			IF (d >= b) & (d - b <= a2) THEN
-				t := (d - b)/a2;
-				x1 := sx + t * dx; y1 := sy + t * dy;
-				IF (b + d <= 0) & (b + d >= -a2) THEN
-					t := (b + d)/a2;
-					x2 := sx - t * dx; y2 := sy - t * dy;
-					nsol := 2
-				ELSE
-					nsol := 1
-				END
-			ELSIF (b + d <= 0) & (b + d >= -a2) THEN
-				t := (b + d)/a2;
-				x2 := sx - t * dx; y2 := sy - t * dy;
-				nsol := 1
-			END
-		END
-	END IntersectLineCircle;
-
-	(** return projection of point onto line **)
-	PROCEDURE ProjectToLine* (px, py, qx, qy, x, y: REAL; VAR u, v: REAL);
-		VAR vx, vy, vv, wx, wy, w, d: REAL;
-	BEGIN
-		vx := qx - px; vy := qy - py;
-		vv := vx * vx + vy * vy;
-		wx := x - px; wy := y - py;
-		w := wx * vx + wy * vy;
-		IF (vv >= 1) OR (vv * MAX(REAL) >= ABS(w)) THEN
-			d := w/vv;
-			u := px + d * vx; v := py + d * vy
-		ELSE
-			u := px; v := py
-		END
-	END ProjectToLine;
-
-	(** return projection of point onto ellipse at origin **)
-	PROCEDURE ProjectToEllipse* (ax, ay, bx, by, x, y: REAL; VAR u, v: REAL);
-		VAR a, sina, cosa, b, shear, l: REAL;
-	BEGIN
-		IF ABS(ax * by - ay * bx) < 1.0E-10 THEN
-			u := 0.0; v := 0.0
-		ELSE	(* find parameters to rotate, shear and scale ellipse to unit circle *)
-			a := Math.sqrt(ax * ax + ay * ay);
-			sina := ay/a; cosa := ax/a;
-			b := cosa * by - sina * bx;
-			shear := (cosa * bx + sina * by)/b;
-			v := cosa * y - sina * x;
-			u := (cosa * x + sina * y - shear * v)/a;
-			v := v/b;
-			l := Math.sqrt(u * u + v * v);
-			u := u/l; v := v/l;
-
-			(* map u, v back to original coordinates *)
-			y := v * b;
-			x := u * a + shear * y;
-			u := cosa * x - sina * y;
-			v := sina * x + cosa * y
-		END
-	END ProjectToEllipse;
-
-	PROCEDURE EnumProject (VAR data: EnumData);
-		VAR x, y, dx, dy, d:REAL;
-	BEGIN
-		WITH data: ProjectData DO
-			IF data.elem = Enter THEN
-				data.sx := data.x; data.sy := data.y
-			ELSIF data.elem = Line THEN
-				ProjectToLine(data.sx, data.sy, data.x, data.y, data.px, data.py, x, y);
-				dx := data.px - x; dy := data.py - y;
-				d := dx * dx + dy * dy;
-				IF d < data.dist THEN
-					dx := data.x - data.sx; dy := data.y - data.sy;
-					IF ((x - data.sx) * dx + (y - data.sy) * dy >= 0) & ((data.x - x) * dx + (data.y - y) * dy >= 0) THEN
-						data.rx := x; data.ry := y; data.dist := d
-					END
-				END;
-				data.sx := data.x; data.sy := data.y
-			END
-		END
-	END EnumProject;
-
-BEGIN
-	Coords[Enter] := 2; Coords[Line] := 1; Coords[Arc] := 4; Coords[Bezier] := 3; Coords[Exit] := 1
-END GfxPaths.

+ 0 - 1359
source/AGfxRaster.Mod

@@ -1,1359 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxRaster; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Gfx contexts rendering on abstract raster devices"; *)
-
-	(*
-		(probably only interesting for extensions rendering to concrete devices)
-	*)
-
-	(*
-		12.2.98 - eliminated offset parameter from subpath begin
-		12.2.98 - fixed bug with path not being allocated for mode=Clip in BeginPath
-		12.2.98 - fixed bug with ASSERT being executed before rc was assigned in RestoreClip
-		17.2.98 - implemented RenderPath
-		24.2.98 - adaption to new cap and join styles
-		27.2.98 - fixed bug with filled paths (didn't update (u, v) in context)
-		6.4.98 - fixed bug in RenderPath (didn't alloc and init pathReg for thick lines)
-		6.4.98 - fixed bug with thick lines (didn't treat (hx, hy) = (0, 0) correctly)
-		8.4.98 - made filled regions more robust regarding open boundary curves
-		8.4.98 - fixed bug in BeginSubpath (didn't set rc.u and rc.v for mode = {Record})
-		15.4.98 - fixed clipState (now only updated while stroking, removed OutIn state, added InOut state)
-		15.4.98 - improved autoclose logic for filled paths (now only connects open subpaths)
-		18.9.98 - minor changes to comply with new GfxMaps, GfxFonts and Gfx interfaces
-		21.9.98 - implemented special cases for circles and axis-aligned ellipses
-		10.3.99 - new dash pattern
-		1.4.99 - bugfix in Rect: incorrect use of GfxRegions.AddPoint when mode included Clip
-		13.5.99 - remodeled cap and join styles
-		[26.5.99 - use 300 dpi metrics in Show (taken out again 14.7.99)]
-		25.8.99 - replace GfxMaps by GfxImages
-		8.10.99 - added Close functionality
-		26.10.99 - removed auto-close logic for regions
-		7.12.99 - fixed bug in AddCircle and AddEllipse (must add enter/exit points on different scanlines)
-		13.02.2000 - new get/set clip methods
-		18.02.2000 - fixed typo in HairCircle
-		04.04.2000 - fixed bug in Arc: didn't set (u, v) when recording
-	*)
-
-	IMPORT
-		Math, GfxMatrix, GfxImages, GfxPaths, GfxRegions, GfxFonts, Gfx;
-
-
-	CONST
-		In* = 0; Out* = 1; InOut* = 2;	(** clip states **)
-
-
-	TYPE
-		(** region based clip areas **)
-		ClipArea* = POINTER TO ClipAreaDesc;
-		ClipAreaDesc* = RECORD (Gfx.ClipAreaDesc)
-			reg*: GfxRegions.Region;	(** corresponding region **)
-		END;
-
-		(** abstract raster context **)
-		Context* = OBJECT { ABSTRACT } (Gfx.Context)
-		VAR
-			clipReg*: GfxRegions.Region;	(** region inside clip path **)
-			col*: Gfx.Color;	(** current color **)
-			pat*: Gfx.Pattern;	(** current pattern **)
-			clipState*: SHORTINT;	(* current clip state (In, Out, InOut) *)
-			useRegion, lateStroke: BOOLEAN;	(* flags for path operations *)
-			cp: GfxPaths.Path;	(* current path if lateStroke is set *)
-			pathReg: GfxRegions.Region;	(* region surrounded by current path *)
-			plx, ply, prx, puy: INTEGER;	(* rectangle around last point *)
-			border: INTEGER;	(* safety border in proportion to style limit around points *)
-			devWidth: REAL;	(* half the line width in device space *)
-			u, v: REAL;	(* current position in device space *)
-			su, sv: REAL;	(* position of first point for thick lines *)
-			du, dv: REAL;	(* current direction for thick lines *)
-			tu, tv: REAL;	(* point on thick line perpendicular to current inner join vertex *)
-			px, py: LONGINT;	(* current pixel position *)
-			fu, fv: REAL;	(* current outline position for filled areas *)
-			offset: REAL;	(* current offset *)
-			deferred: BOOLEAN;	(* indicates that no cap/join has been drawn for start of thick line yet *)
-			u0, v0, u1, v1: REAL;	(* endpoints of first thick line of current subpath *)
-
-			(** initialize raster context **)
-			PROCEDURE InitRaster* ();
-			BEGIN
-(*				SELF.InitContext( );*)
-				IF SELF.clipReg = NIL THEN
-					NEW(SELF.clipReg);
-				END;
-				SELF.clipReg.Init(GfxRegions.Winding)
-			END InitRaster;
-
-			PROCEDURE SetColPat* (col: Gfx.Color; pat: Gfx.Pattern);
-			BEGIN
-				SELF.col := col; SELF.pat := pat
-			END SetColPat;
-
-		(**--- Coordinate System ---**)
-
-			(** reset current transformation matrix **)
-			PROCEDURE{ABSTRACT} ResetCTM*(); END ResetCTM;
-
-		(**--- Clipping ---**)
-
-			(** reset clip path **)
-			PROCEDURE{ABSTRACT} ResetClip*();
-			BEGIN
-				SELF.clipReg.Clear()
-			END ResetClip;
-
-			(** get bounding box of clipping path in user coordinates **)
-			PROCEDURE GetClipRect*(VAR llx, lly, urx, ury: REAL);
-				VAR inv: GfxMatrix.Matrix; reg: GfxRegions.Region;
-			BEGIN
-				GfxMatrix.Invert(SELF.ctm, inv);
-				reg := SELF.clipReg;
-				GfxMatrix.ApplyToRect(inv, reg.llx, reg.lly, reg.urx, reg.ury, llx, lly, urx, ury)
-			END GetClipRect;
-
-			(** get current clipping area **)
-			PROCEDURE GetClip*(): Gfx.ClipArea;
-				VAR clip: ClipArea;
-			BEGIN
-				NEW(clip); NEW(clip.reg); SELF.clipReg.Copy(clip.reg);
-				RETURN clip
-			END GetClip;
-
-			(** restore saved clipping path **)
-			PROCEDURE SetClip*(clip: Gfx.ClipArea);
-			BEGIN
-				ASSERT(clip IS ClipArea, 100);
-				clip(ClipArea).reg.Copy(SELF.clipReg)
-			END SetClip;
-
-		(**--- Current Path ---**)
-			
-			(** start new path **)
-			PROCEDURE DoBegin*(mode: SET);
-			BEGIN
-				SELF.mode := mode * {Gfx.Record..Gfx.EvenOdd};
-				SELF.cam := SELF.ctm;	(* preserve current transformation for attributes *)
-				IF Gfx.Record IN SELF.mode THEN
-					IF SELF.path = NIL THEN NEW(SELF.path) END;
-					SELF.path.Clear()
-				END;
-				SELF.useRegion := {Gfx.Clip, Gfx.Fill} * SELF.mode # {};
-				SELF.lateStroke := SELF.useRegion & (Gfx.Stroke IN SELF.mode);
-				SELF.deferred := FALSE;
-				IF SELF.lateStroke THEN
-					IF SELF.cp = NIL THEN NEW(SELF.cp) END;
-					SELF.cp.Clear()
-				END;
-				IF Gfx.Stroke IN SELF.mode THEN
-					StrokePrepare(SELF)
-				END;
-				IF SELF.useRegion OR (Gfx.Stroke IN SELF.mode) & (SELF.devWidth > 0.75) THEN
-					IF SELF.pathReg = NIL THEN NEW(SELF.pathReg) END;
-					IF Gfx.EvenOdd IN SELF.mode THEN
-						SELF.pathReg.Init(GfxRegions.EvenOdd)
-					ELSE
-						SELF.pathReg.Init(GfxRegions.Winding)
-					END
-				END
-			END DoBegin;
-
-			(** exit current subpath (if open) and end current path **)
-			PROCEDURE DoEnd*();
-				VAR data: PathData;
-			BEGIN
-				IF Gfx.Clip IN SELF.mode THEN
-					SELF.clipReg.Intersect(SELF.pathReg)
-				END;
-				IF Gfx.Fill IN SELF.mode THEN
-					SELF.pathReg.Intersect(SELF.clipReg);
-					IF ~SELF.pathReg.Empty() THEN
-						SELF.clipState := In;
-						SELF.SetColPat(SELF.fillCol, SELF.fillPat);
-						FillRegion(SELF)
-					END
-				END;
-				IF SELF.lateStroke THEN
-					SELF.SetColPat(SELF.strokeCol, SELF.strokePat);
-					data.context := SELF;
-					SELF.cp.Enumerate(StrokePathElem, data)
-				END
-			END DoEnd;
-
-			(** start subpath at inner point **)
-			PROCEDURE DoEnter*(x, y, dx, dy: REAL);
-				VAR u, v, du, dv: REAL;
-			BEGIN
-				GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-				GfxMatrix.ApplyToVector(SELF.ctm, dx, dy, du, dv);
-				IF Gfx.Record IN SELF.mode THEN
-					SELF.path.AddEnter(u, v, du, dv)
-				END;
-				IF SELF.lateStroke THEN
-					SELF.cp.AddEnter(u, v, du, dv)
-				END;
-				IF SELF.useRegion THEN
-					EnterLine(SELF, u, v);
-					SELF.u := u; SELF.v := v
-				ELSIF Gfx.Stroke IN SELF.mode THEN
-					StrokeEnter(SELF, u, v, du, dv)
-				ELSE
-					SELF.u := u; SELF.v := v
-				END;
-				IF (dx = 0) & (dy = 0) THEN
-					SELF.deferred := TRUE;
-					SELF.u0 := u; SELF.v0 := v
-				END;
-				SELF.cpx := x; SELF.cpy := y
-			END DoEnter;
-
-			(** end subpath at inner point **)
-			PROCEDURE DoExit*(dx, dy: REAL);
-				VAR du, dv: REAL;
-			BEGIN
-				GfxMatrix.ApplyToVector(SELF.ctm, dx, dy, du, dv);
-				IF Gfx.Record IN SELF.mode THEN
-					SELF.path.AddExit(du, dv)
-				END;
-				IF SELF.lateStroke THEN
-					SELF.cp.AddExit(du, dv)
-				END;
-				IF ~SELF.useRegion & (Gfx.Stroke IN SELF.mode) THEN
-					StrokeExit(SELF, du, dv)
-				END;
-				SELF.deferred := FALSE
-			END DoExit;
-
-			(** close current subpath **)
-			PROCEDURE DoClose*();
-				CONST eps = 0.001;
-			BEGIN
-				IF ~SELF.deferred THEN
-					SELF.DoExit(0, 0)
-				ELSE
-					IF (ABS(SELF.u - SELF.u0) > eps) OR (ABS(SELF.v - SELF.v0) > eps) THEN
-						IF Gfx.Record IN SELF.mode THEN
-							SELF.path.AddLine(SELF.u0, SELF.v0)
-						END;
-						IF SELF.lateStroke THEN
-							SELF.cp.AddLine(SELF.u0, SELF.v0)
-						END;
-						IF SELF.useRegion THEN
-							AddLine(SELF, SELF.u0, SELF.v0)
-						ELSIF Gfx.Stroke IN SELF.mode THEN
-							StrokeLineTo(SELF, SELF.u0, SELF.v0)
-						END
-					END;
-					IF Gfx.Record IN SELF.mode THEN
-						SELF.path.AddExit(0, 0);
-						SELF.path.Close()
-					END;
-					IF SELF.lateStroke THEN
-						SELF.cp.AddExit(0, 0);
-						SELF.cp.Close()
-					END;
-					IF ~SELF.useRegion & (Gfx.Stroke IN SELF.mode) THEN
-						StrokeClose(SELF)
-					END;
-					SELF.deferred := FALSE
-				END
-			END DoClose;
-
-			(** append line to current path **)
-			PROCEDURE DoLine*(x, y: REAL);
-				VAR u, v: REAL;
-			BEGIN
-				GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-				IF (u # SELF.u) OR (v # SELF.v) THEN
-					IF Gfx.Record IN SELF.mode THEN
-						SELF.path.AddLine(u, v)
-					END;
-					IF SELF.lateStroke THEN
-						SELF.cp.AddLine(u, v)
-					END;
-					IF SELF.useRegion THEN
-						AddLine(SELF, u, v);
-						SELF.u := u; SELF.v := v
-					ELSIF Gfx.Stroke IN SELF.mode THEN
-						StrokeLineTo(SELF, u, v)
-					ELSE
-						SELF.u := u; SELF.v := v
-					END;
-					SELF.cpx := x; SELF.cpy := y
-				END
-			END DoLine;
-
-			(** append arc to current path **)
-			PROCEDURE DoArc*(x, y, x0, y0, x1, y1, x2, y2: REAL);
-				VAR u, v, u0, v0, u1, v1, u2, v2, ru, rv: REAL; data: PathData;
-			BEGIN
-				GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-				GfxMatrix.Apply(SELF.ctm, x0, y0, u0, v0);
-				GfxMatrix.Apply(SELF.ctm, x1, y1, u1, v1);
-				GfxMatrix.Apply(SELF.ctm, x2, y2, u2, v2);
-				IF Gfx.Record IN SELF.mode THEN
-					SELF.path.AddArc(u, v, u0, v0, u1, v1, u2, v2)
-				END;
-				IF SELF.lateStroke THEN
-					SELF.cp.AddArc(u, v, u0, v0, u1, v1, u2, v2)
-				END;
-				IF SELF.useRegion THEN
-					IF IsEllipse(u0, v0, u1, v1, u2, v2, SELF.u, SELF.v, u, v, SELF.flatness, ru, rv) THEN
-						IF ABS(ABS(ru) - ABS(rv)) < SELF.flatness THEN
-							IF ru * rv > 0 THEN
-								AddCircle(SELF, ENTIER(u0), ENTIER(v0), ENTIER(ABS(ru) + 0.5))
-							ELSE
-								AddCircle(SELF, ENTIER(u0), ENTIER(v0), -ENTIER(ABS(ru) + 0.5))
-							END
-						ELSE
-							AddEllipse(SELF, ENTIER(u0), ENTIER(v0), ENTIER(ru+0.5), ENTIER(rv+0.5))
-						END
-					ELSE
-						data.context := SELF(Context); data.x := SELF.u; data.y := SELF.v;
-						GfxPaths.EnumArc(u0, v0, u1, v1, u2, v2, u, v, SELF.flatness, AddPathElem, data);
-						SELF.u := u; SELF.v := v
-					END
-				ELSIF Gfx.Stroke IN SELF.mode THEN
-					IF (SELF.dashPatLen = 0) & (SELF.devWidth <= 0.75) &
-						IsEllipse(u0, v0, u1, v1, u2, v2, SELF.u, SELF.v, u, v, SELF.flatness, ru, rv)
-					THEN
-						IF ABS(ABS(ru) - ABS(rv)) < SELF.flatness THEN
-							IF ru * rv > 0 THEN
-								HairCircle(SELF, ENTIER(u0), ENTIER(v0), ENTIER(ABS(ru) + 0.5))
-							ELSE
-								HairCircle(SELF, ENTIER(u0), ENTIER(v0), -ENTIER(ABS(ru) + 0.5))
-							END
-						ELSE
-							HairEllipse(SELF, ENTIER(u0), ENTIER(v0), ENTIER(ABS(ru) + 0.5), ENTIER(ABS(rv) + 0.5))
-						END
-					ELSE
-						data.context := SELF; data.x := SELF.u; data.y := SELF.v;
-						GfxPaths.EnumArc(u0, v0, u1, v1, u2, v2, u, v, SELF.flatness, StrokePathElem, data)
-					END
-				ELSE
-					SELF.u := u; SELF.v := v
-				END;
-				SELF.cpx := x; SELF.cpy := y
-			END DoArc;
-
-			(** append cubic bezier to current path **)
-			PROCEDURE DoBezier*(x, y, x1, y1, x2, y2: REAL);
-				VAR u, v, u1, v1, u2, v2: REAL; data: PathData;
-			BEGIN
-				GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-				GfxMatrix.Apply(SELF.ctm, x1, y1, u1, v1);
-				GfxMatrix.Apply(SELF.ctm, x2, y2, u2, v2);
-				IF Gfx.Record IN SELF.mode THEN
-					SELF.path.AddBezier(u, v, u1, v1, u2, v2)
-				END;
-				IF SELF.lateStroke THEN
-					SELF.cp.AddBezier(u, v, u1, v1, u2, v2)
-				END;
-				IF SELF.useRegion THEN
-					data.context := SELF(Context); data.x := SELF.u; data.y := SELF.v;
-					GfxPaths.EnumBezier(u1, v1, u2, v2, u, v, SELF.flatness, AddPathElem, data);
-					SELF.u := u; SELF.v := v
-				ELSIF Gfx.Stroke IN SELF.mode THEN
-					data.context := SELF(Context); data.x := SELF.u; data.y := SELF.v;
-					GfxPaths.EnumBezier(u1, v1, u2, v2, u, v, SELF.flatness, StrokePathElem, data)
-				END;
-				SELF.cpx := x; SELF.cpy := y
-			END DoBezier;
-
-			(** painting operators (potential for optimization) **)
-			PROCEDURE DoRect*(x0, y0, x1, y1: REAL);
-				VAR u0, v0, u1, v1, t: REAL; enter, exit, llx, lly, urx, ury: INTEGER;
-			BEGIN
-				IF ~(Gfx.Record IN SELF.mode) & ~GfxMatrix.Rotated(SELF.ctm) THEN
-					GfxMatrix.Apply(SELF.ctm, x0, y0, u0, v0);
-					GfxMatrix.Apply(SELF.ctm, x1, y1, u1, v1);
-					enter := -1; exit := 1;
-					IF u0 > u1 THEN t := u0; u0 := u1; u1 := t END;
-					IF v0 > v1 THEN t := v0; v0 := v1; v1 := t; enter := -enter; exit := -exit END;
-					IF SELF.lateStroke THEN
-						SELF.cp.AddRect(u0, v0, u1, v1)
-					END;
-					IF SELF.useRegion THEN
-						llx := SHORT(ENTIER(u0 + 0.5)); lly := SHORT(ENTIER(v0 + 0.5));
-						urx := SHORT(ENTIER(u1 + 0.5)); ury := SHORT(ENTIER(v1 + 0.5));
-						IF Gfx.Clip IN SELF.mode THEN
-							WHILE lly < ury DO
-								SELF.pathReg.AddPoint(llx, lly, enter);
-								INC(lly);
-								SELF.pathReg.AddPoint(urx, lly, exit)
-							END
-						ELSIF SELF.clipReg.RectOverlaps(llx, lly, urx, ury) THEN
-							IF SELF.clipReg.RectInside(llx, lly, urx, ury) THEN SELF.clipState := In
-							ELSE SELF.clipState := InOut
-							END;
-							SELF.SetColPat(SELF.fillCol, SELF.fillPat);
-							SELF.rect(llx, lly, urx, ury)
-						END
-					ELSIF Gfx.Stroke IN SELF.mode THEN
-						IF (SELF.dashPatLen > 0) OR (SELF.devWidth > 0.75) THEN
-							StrokeEnter(SELF, u0, v0, 0, v0 - v1);
-							StrokeLineTo(SELF, u1, v0); StrokeLineTo(SELF, u1, v1); StrokeLineTo(SELF, u0, v1); StrokeLineTo(SELF, u0, v0);
-							StrokeExit(SELF, u1 - u0, 0)
-						ELSE
-							llx := SHORT(ENTIER(u0)); lly := SHORT(ENTIER(v0));
-							urx := SHORT(ENTIER(u1)); ury := SHORT(ENTIER(v1));
-							IF SELF.clipReg.RectOverlaps(llx, lly, urx, ury) THEN
-								IF SELF.clipReg.RectInside(llx, lly, urx, ury) THEN SELF.clipState := In
-								ELSE SELF.clipState := InOut
-								END;
-								SELF.rect(llx, lly, urx, lly+1); SELF.rect(urx, lly, urx+1, ury+1);
-								SELF.rect(llx, ury, urx, ury+1); SELF.rect(llx, lly+1, llx+1, ury)
-							END
-						END
-					END
-				ELSE
-					SELF.DoRect^(x0, y0, x1, y1); (* default implementation *)
-				END
-			END DoRect;
-
-			PROCEDURE DoEllipse*(x, y, rx, ry: REAL);
-				VAR u, v, ru, rv: REAL; data: PathData;
-			BEGIN
-				IF ~(Gfx.Record IN SELF.mode) & ~GfxMatrix.Rotated(SELF.ctm) THEN
-					GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-					GfxMatrix.ApplyToVector(SELF.ctm, rx, ry, ru, rv);
-					IF SELF.lateStroke THEN
-						SELF.cp.AddEnter(u + ru, v, 0, rv);
-						SELF.cp.AddArc(u + ru, v, u, v, u + ru, v, u, v + rv);
-						SELF.cp.AddExit(0, rv)
-					END;
-					IF SELF.useRegion THEN
-						IF ABS(ABS(ru) - ABS(rv)) < SELF.flatness THEN
-							IF ru * rv > 0 THEN AddCircle(SELF, ENTIER(u), ENTIER(v), ENTIER(ABS(ru) + 0.5))
-							ELSE AddCircle(SELF, ENTIER(u), ENTIER(v), -ENTIER(ABS(rv) + 0.5))
-							END
-						ELSE
-							AddEllipse(SELF, ENTIER(u), ENTIER(v), ENTIER(ru + 0.5), ENTIER(rv + 0.5))
-						END
-					ELSIF Gfx.Stroke IN SELF.mode THEN
-						IF (SELF.dashPatLen = 0) & (SELF.devWidth <= 0.75) THEN
-							IF ABS(ABS(ru) - ABS(rv)) < SELF.flatness THEN
-								IF ru * rv > 0 THEN HairCircle(SELF, ENTIER(u), ENTIER(v), ENTIER(ABS(ru) + 0.5))
-								ELSE HairCircle(SELF, ENTIER(u), ENTIER(v), -ENTIER(ABS(ru) + 0.5))
-								END
-							ELSE
-								HairEllipse(SELF, ENTIER(u), ENTIER(v), ENTIER(ru + 0.5), ENTIER(rv + 0.5))
-							END
-						ELSE
-							StrokeEnter(SELF, u + ru, v, 0, rv);
-							data.context := SELF; data.x := SELF.u; data.y := SELF.v;
-							GfxPaths.EnumArc(u, v, u + ru, v, u, v + rv, u + ru, v, SELF.flatness, StrokePathElem, data);
-							StrokeExit(SELF, 0, rv)
-						END
-					END
-				ELSE
-					SELF.DoEllipse^(x, y, rx, ry)
-				END
-			END DoEllipse;
-
-			(** append character outlines to current path at current point; advance current point to position after last character **)
-			PROCEDURE DoShow*(x, y: REAL; VAR str: ARRAY OF CHAR);
-				VAR
-					mat: GfxMatrix.Matrix; font: GfxFonts.Font; u, v, bu, bv, du, dv: REAL;
-					i: LONGINT; img: GfxImages.Image; data: PathData;
-					TmpPath: GfxPaths.Path;	(* was global: cost of making it local? *)
-					filter: GfxImages.Filter;
-			BEGIN
-				GfxMatrix.Concat(SELF.font.mat, SELF.ctm, mat);
-				font := GfxFonts.Open(SELF.font.name, SELF.font.ptsize, mat);
-				GfxMatrix.Apply(SELF.ctm, x, y, u, v);
-				IF (SELF.mode * {Gfx.Record..Gfx.EvenOdd} = {Gfx.Fill}) & (SELF.fillPat = NIL) THEN
-					mat := SELF.ctm; SELF.ctm := GfxMatrix.Identity;
-					SELF.SetColPat(SELF.fillCol, SELF.fillPat);
-					i := 0; GfxImages.InitNoFilter(filter);
-					WHILE str[i] # 0X DO
-						GfxFonts.GetMap(font, str[i], bu, bv, du, dv, img);
-						IF img # NIL THEN
-							SELF.DrawImage(u + bu, v + bv, img, filter)
-						END;
-						u := u + du; v := v + dv;
-						INC(i)
-					END;
-					SELF.ctm := mat
-				ELSE
-					NEW(TmpPath);
-					i := 0;
-					WHILE str[i] # 0X DO
-						GfxFonts.GetOutline(font, str[i], u, v, TmpPath);
-						GfxFonts.GetWidth(font, str[i], du, dv);
-						u := u + du; v := v + dv;
-						IF Gfx.Record IN SELF.mode THEN
-							SELF.path.Append(TmpPath)
-						END;
-						IF SELF.lateStroke THEN
-							SELF.cp.Append(TmpPath)
-						END;
-						IF SELF.useRegion THEN
-							data.context := SELF;
-							TmpPath.Enumerate(AddPathElem, data);
-							SELF.u := SELF.fu; SELF.v := SELF.fv
-						ELSIF Gfx.Stroke IN SELF.mode THEN
-							data.context := SELF;
-							TmpPath.Enumerate(StrokePathElem, data)
-						END;
-						INC(i)
-					END
-				END;
-				GfxMatrix.Solve(SELF.ctm, u, v, SELF.cpx, SELF.cpy)
-			END DoShow;
-
-			PROCEDURE DoRender*(mode: SET);
-				VAR data: PathData;
-			BEGIN
-				IF mode * {Gfx.Clip, Gfx.Fill} # {} THEN
-					IF SELF.pathReg = NIL THEN NEW(SELF.pathReg) END;
-					IF Gfx.EvenOdd IN mode THEN
-						SELF.pathReg.Init(GfxRegions.EvenOdd)
-					ELSE
-						SELF.pathReg.Init(GfxRegions.Winding)
-					END;
-					data.context := SELF;
-					SELF.path.Enumerate(AddPathElem, data);
-					SELF.pathReg.Intersect(SELF.clipReg);
-					IF Gfx.Clip IN mode THEN
-						SELF.pathReg.Copy(SELF.clipReg)
-					END;
-					IF Gfx.Fill IN mode THEN
-						SELF.clipState := In;
-						SELF.SetColPat(SELF.fillCol, SELF.fillPat);
-						FillRegion(SELF)
-					END
-				END;
-				IF Gfx.Stroke IN mode THEN
-					SELF.cam := SELF.ctm;
-					StrokePrepare(SELF);
-					IF SELF.devWidth > 0.75 THEN
-						IF SELF.pathReg = NIL THEN NEW(SELF.pathReg) END;
-						IF Gfx.EvenOdd IN SELF.mode THEN
-							SELF.pathReg.Init(GfxRegions.EvenOdd)
-						ELSE
-							SELF.pathReg.Init(GfxRegions.Winding)
-						END
-					END;
-					data.context := SELF;
-					SELF.path.Enumerate(StrokePathElem, data)
-				END
-			END DoRender;
-
-			(** images and patterns **)
-			PROCEDURE{ABSTRACT} DrawImage*(x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter); END DrawImage;
-
-			PROCEDURE{ABSTRACT} dot*(x, y: LONGINT); END dot; (** current dot procedure **)
-			PROCEDURE{ABSTRACT} rect*(lx, ly, rx, uy: LONGINT); END rect; (** current rect procedure **)
-		END Context;
-
-		(* path data for filling paths *)
-		PathData = RECORD (GfxPaths.EnumData)
-			context: Context;
-		END;
-
-		RegData = RECORD (GfxRegions.EnumData)
-			context: Context;
-		END;
-
-
-	(**--- Ellipse Test ---**)
-
-	(** return if arc parameter describe axis-aligned ellipse and, if so, the radii in x and y direction **)
-	PROCEDURE IsEllipse* (x0, y0, x1, y1, x2, y2, sx, sy, ex, ey, flatness: REAL; VAR rx, ry: REAL): BOOLEAN;
-		CONST eps = 0.01;
-		VAR x, y: REAL;
-	BEGIN
-		IF (ABS(x1 - x0) < eps) & (ABS(y2 - y0) < eps) THEN
-			rx := x0 - x2; ry := y0 - y1
-		ELSIF (ABS(x2 - x0) < eps) & (ABS(y1 - y0) < eps) THEN
-			rx := x1 - x0; ry := y2 - y0
-		ELSE
-			RETURN FALSE
-		END;
-		flatness := 0.25*flatness;
-		IF (ABS(rx - ENTIER(rx + 0.5)) < flatness) & (ABS(ry - ENTIER(ry + 0.5)) < flatness) &
-			(ABS(x0 - 0.5 - ENTIER(x0)) < flatness) & (ABS(y0 - 0.5 - ENTIER(y0)) < flatness) &
-			(ABS(sx - ex) < eps) & (ABS(sy - ey) < eps)
-		THEN
-			GfxPaths.ProjectToEllipse(rx, 0, 0, ry, sx, sy, x, y);
-			RETURN (ABS(sx - x) < eps) & (ABS(sy - y) < eps)
-		ELSE
-			RETURN FALSE
-		END
-	END IsEllipse;
-
-
-	(**--- Clipping ---**)
-
-	PROCEDURE InitClipState (rc: Context; x, y: INTEGER);
-	BEGIN
-		rc.plx := x - rc.border; rc.ply := y - rc.border;
-		rc.prx := x + rc.border; rc.puy := y + rc.border;
-		IF rc.clipReg.RectInside(rc.plx, rc.ply, rc.prx, rc.puy) THEN
-			rc.clipState := In
-		ELSIF rc.clipReg.RectOverlaps(rc.plx, rc.ply, rc.prx, rc.puy) THEN
-			rc.clipState := InOut
-		ELSE
-			rc.clipState := Out
-		END
-	END InitClipState;
-
-	PROCEDURE UpdateClipState (rc: Context; x, y: INTEGER);
-		VAR plx, ply, prx, puy: INTEGER;
-	BEGIN
-		plx := x - rc.border; ply := y - rc.border; prx := x + rc.border; puy := y + rc.border;
-		GfxRegions.IncludeRect(rc.plx, rc.ply, rc.prx, rc.puy, plx, ply, prx, puy);
-		IF rc.clipReg.RectInside(rc.plx, rc.ply, rc.prx, rc.puy) THEN
-			rc.clipState := In
-		ELSIF rc.clipReg.RectOverlaps(rc.plx, rc.ply, rc.prx, rc.puy) THEN
-			rc.clipState := InOut
-		ELSE
-			rc.clipState := Out
-		END;
-		rc.plx := plx; rc.ply := ply; rc.prx := prx; rc.puy := puy
-	END UpdateClipState;
-
-
-	(*--- Hairlines ---*)
-
-	PROCEDURE HairLineEnter (rc: Context; u, v: REAL);
-	BEGIN
-		rc.u := u; rc.v := v;
-		rc.px := ENTIER(u); rc.py := ENTIER(v);
-		InitClipState(rc, SHORT(rc.px), SHORT(rc.py));
-		rc.dot(rc.px, rc.py)
-	END HairLineEnter;
-
-	PROCEDURE HairLineTo (rc: Context; u, v: REAL);
-		VAR px, py, xstep, ystep, steps: LONGINT; du, dv, eu, ev, e: REAL;
-	BEGIN
-		px := ENTIER(u); py := ENTIER(v);
-		UpdateClipState(rc, SHORT(px), SHORT(py));
-		IF px = rc.px THEN	(* horizontal line *)
-			IF py > rc.py THEN rc.rect(px, rc.py + 1, px + 1, py + 1)
-			ELSIF py < rc.py THEN rc.rect(px, py, px + 1, rc.py)
-			END;
-			rc.py := py
-		ELSIF py = rc.py THEN	(* vertical line *)
-			IF px > rc.px THEN rc.rect(rc.px + 1, py, px + 1, py + 1)
-			ELSE rc.rect(px, py, rc.px, py + 1)
-			END;
-			rc.px := px
-		ELSE
-			du := u - rc.u; dv := v - rc.v;
-
-			(* bring parameters into first quadrant *)
-			IF du >= 0 THEN xstep := 1; eu := rc.u - (rc.px + 0.5)
-			ELSE xstep := -1; du := -du; eu := rc.px + 0.5 - rc.u
-			END;
-			IF dv >= 0 THEN ystep := 1; ev := rc.v - (rc.py + 0.5)
-			ELSE ystep := -1; dv := -dv; ev := rc.py + 0.5 - rc.v
-			END;
-
-			IF du >= dv THEN	(* x-dominant case *)
-				e := du * ev - dv * eu + dv - 0.5*du;
-				steps := ABS(px - rc.px);
-				WHILE steps > 0 DO
-					IF (e >= 0) & ((e > 0) OR (ystep <= 0)) THEN
-						INC(rc.py, ystep); e := e - du
-					END;
-					INC(rc.px, xstep); e := e + dv;
-					rc.dot(rc.px, rc.py);
-					DEC(steps)
-				END
-			ELSE	(* y-dominant case *)
-				e := dv * eu - du * ev + du - 0.5*dv;
-				steps := ABS(py - rc.py);
-				WHILE steps > 0 DO
-					IF (e >= 0) & ((e > 0) OR (xstep <= 0)) THEN
-						INC(rc.px, xstep); e := e - dv
-					END;
-					INC(rc.py, ystep); e := e + du;
-					rc.dot(rc.px, rc.py);
-					DEC(steps)
-				END
-			END
-		END;
-		rc.u := u; rc.v := v
-	END HairLineTo;
-
-	PROCEDURE HairCircle (rc: Context; mx, my, r: LONGINT);
-		VAR llx, lly, urx, ury: INTEGER; x, y, d, de, dse: LONGINT;
-	BEGIN
-		llx := SHORT(mx - r); lly := SHORT(my - r); urx := SHORT(mx + r + 1); ury := SHORT(my + r + 1);
-		IF rc.clipReg.RectOverlaps(llx, lly, urx, ury) THEN
-			IF rc.clipReg.RectInside(llx, lly, urx, ury) THEN rc.clipState := In
-			ELSE rc.clipState := InOut
-			END;
-			x := 0; y := r; d := 1-r; de := 3; dse := -2*r + 5;
-			rc.dot(mx, my + y); rc.dot(mx, my - y); rc.dot(mx + y, my); rc.dot(mx - y, my);
-			WHILE y > x DO
-				IF d < 0 THEN
-					INC(d, de); INC(de, 2); INC(dse, 2); INC(x)
-				ELSE
-					INC(d, dse); INC(de, 2); INC(dse, 4); INC(x); DEC(y)
-				END;
-				rc.dot(mx + x, my + y); rc.dot(mx + y, my + x);
-				rc.dot(mx + x, my - y); rc.dot(mx - y, my + x);
-				rc.dot(mx - x, my - y); rc.dot(mx - y, my - x);
-				rc.dot(mx - x, my + y); rc.dot(mx + y, my - x)
-			END
-		END
-	END HairCircle;
-
-	PROCEDURE HairEllipse (rc: Context; mx, my, rx, ry: LONGINT);
-		VAR llx, lly, urx, ury: INTEGER; x, y, aa, bb, da, db, dy, dx, d: LONGINT;
-	BEGIN
-		llx := SHORT(mx - rx); lly := SHORT(my - ry); urx := SHORT(mx + rx + 1); ury := SHORT(my + ry + 1);
-		IF rc.clipReg.RectOverlaps(llx, lly, urx, ury) THEN
-			IF rc.clipReg.RectInside(llx, lly, urx, ury) THEN rc.clipState := In
-			ELSE rc.clipState := InOut
-			END;
-			x := rx; y := 0;
-			aa := rx * rx; bb := ry * ry;
-			da := -8*aa; db := -8*bb;
-			dy := -4*aa; dx := -db * (x-1);
-			d := bb * (4*x - 1);
-			WHILE db * x < da * y DO
-				rc.dot(mx + x, my + y); rc.dot(mx + x, my - y);
-				rc.dot(mx - x, my + y); rc.dot(mx - x, my - y);
-				INC(d, dy); INC(dy, da); INC(y);
-				IF d < 0 THEN
-					INC(d, dx); INC(dx, db); DEC(x)
-				END
-			END;
-			dx := 4 * bb * (2*x - 1); dy := da * (y+1);
-			d := d - bb * (4*x - 1) - aa * (4*y + 1);
-			WHILE x >= 0 DO
-				rc.dot(mx + x, my + y); rc.dot(mx + x, my - y);
-				rc.dot(mx - x, my + y); rc.dot(mx - x, my - y);
-				INC(d, dx); INC(dx, db); DEC(x);
-				IF d >= 0 THEN
-					INC(d, dy); INC(dy, da); INC(y)
-				END
-			END
-		END
-	END HairEllipse;
-
-
-	(*--- Filled Areas ---*)
-
-	PROCEDURE EnterLine (rc: Context; u, v: REAL);
-	BEGIN
-		rc.fu := u; rc.fv := v;
-		rc.px := ENTIER(u + 0.5); rc.py := ENTIER(v + 0.5);
-	END EnterLine;
-
-	PROCEDURE AddLine (rc: Context; u, v: REAL);
-		VAR px, py, x, y, xstep, ystep, steps: LONGINT; du, dv, eu, ev, e: REAL;
-	BEGIN
-		px := ENTIER(u + 0.5); py := ENTIER(v + 0.5);
-		x := rc.px; y := rc.py;
-		IF py = y THEN	(* horizontal line => ignore *)
-			rc.px := px
-		ELSE
-			du := u - rc.fu; dv := v - rc.fv;
-			IF du >= 0 THEN xstep := 1; eu := rc.fu - x
-			ELSE xstep := -1; du := -du; eu := x - rc.fu
-			END;
-			IF dv >= 0 THEN ystep := 1; ev := rc.fv - y
-			ELSE ystep := -1; dv := -dv; ev := y - rc.fv
-			END;
-			e := du * ev - dv * eu + 0.5 * (dv - du);
-			steps := ABS(px - x) + ABS(py - y);
-			WHILE steps > 0 DO
-				IF (e >= 0) & ((e > 0) OR (xstep <= 0)) THEN
-					INC(y, ystep); e := e - du;
-					rc.pathReg.AddPoint(SHORT(x), SHORT(y), SHORT(ystep))
-				ELSE
-					INC(x, xstep); e := e + dv
-					(* don't have to insert point here because regions are sliced horizontally *)
-				END;
-				DEC(steps)
-			END;
-			rc.px := px; rc.py := py
-		END;
-		rc.fu := u; rc.fv := v
-	END AddLine;
-
-	PROCEDURE AddCircle (rc: Context; mx, my, r: LONGINT);
-		VAR x, y, d, de, ds, sgn: LONGINT;
-	BEGIN
-		x := 0; y := r; d := 1-r; de := 3; ds := -2*r + 2;
-		IF r > 0 THEN sgn := 1 ELSE sgn := -1 END;
-		WHILE y > x DO
-			REPEAT
-				INC(d, de); INC(de, 2); INC(x, sgn)
-			UNTIL d >= 0;
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my + y), 1);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my - y), -1);
-			INC(d, ds); INC(ds, 2); DEC(y);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my + y), -1);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my - y), 1)
-		END;
-		WHILE y > 0 DO
-			IF d <= 0 THEN
-				INC(d, de); INC(de, 2); INC(x, sgn)
-			END;
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my + y), 1);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my - y), -1);
-			INC(d, ds); INC(ds, 2); DEC(y);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my + y), -1);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my - y), 1)
-		END
-	END AddCircle;
-
-	PROCEDURE AddEllipse (rc: Context; mx, my, rx, ry: LONGINT);
-		VAR x, y, aa, bb, da, db, dy, dx, d, sgn: LONGINT;
-	BEGIN
-		x := ABS(rx); y := 0;
-		aa := rx * rx; bb := ry * ry;
-		da := -8*aa; db := -8*bb;
-		dy := -4*aa; dx := -db * (x-1);
-		d := bb * (4*x - 1);
-		IF rx * ry > 0 THEN sgn := 1
-		ELSE x := -x; sgn := -1
-		END;
-		WHILE db * x * sgn < da * y DO
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my + y), -1);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my - y), 1);
-			INC(y);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my + y), 1);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my - y), -1);
-			IF d < 0 THEN
-				INC(d, dx); INC(dx, db); DEC(x, sgn)
-			END;
-			INC(d, dy); INC(dy, da)
-		END;
-		dx := 4 * bb * (2*ABS(x) - 1); dy := da * (y+1);
-		d := d - bb * (4*ABS(x) - 1) - aa * (4*y + 1);
-		WHILE x * sgn > 0 DO
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my + y), -1);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my - y), 1);
-			INC(y);
-			rc.pathReg.AddPoint(SHORT(mx - x), SHORT(my + y), 1);
-			rc.pathReg.AddPoint(SHORT(mx + x), SHORT(my - y), -1);
-			REPEAT
-				INC(d, dx); INC(dx, db); DEC(x, sgn)
-			UNTIL d >= 0;
-			INC(d, dy); INC(dy, da)
-		END
-	END AddEllipse;
-
-	PROCEDURE EnumRegion (lx, ly, rx, uy: INTEGER; VAR data: GfxRegions.EnumData);
-		VAR rc: Context;
-	BEGIN
-		rc := data(RegData).context;
-		rc.rect(lx, ly, rx, uy)
-	END EnumRegion;
-
-	PROCEDURE FillRegion (rc: Context);
-		VAR data: RegData; reg: GfxRegions.Region;
-	BEGIN
-		reg := rc.pathReg;
-		IF ~reg.Empty() THEN
-			data.context := rc;
-			reg.Enumerate(reg.llx, reg.lly, reg.urx, reg.ury, EnumRegion, data)
-		END
-	END FillRegion;
-
-	PROCEDURE AddPathElem (VAR data: GfxPaths.EnumData);
-		VAR rc: Context; x, y: REAL;
-	BEGIN
-		rc := data(PathData).context;
-		CASE data.elem OF
-		| GfxPaths.Enter:
-			EnterLine(rc, data.x, data.y)
-		| GfxPaths.Line:
-			AddLine(rc, data.x, data.y)
-		| GfxPaths.Arc:
-			IF IsEllipse(data.x0, data.y0, data.x1, data.y1, data.x2, data.y2, data.x, data.y, rc.u, rc.v, rc.flatness, x, y) THEN
-				IF ABS(ABS(x) - ABS(y)) < rc.flatness THEN
-					IF x * y > 0 THEN
-						AddCircle(rc, ENTIER(data.x0), ENTIER(data.y0), ENTIER(ABS(x) + 0.5))
-					ELSE
-						AddCircle(rc, ENTIER(data.x0), ENTIER(data.y0), -ENTIER(ABS(x) + 0.5))
-					END
-				ELSE
-					AddEllipse(rc, ENTIER(data.x0), ENTIER(data.y0), ENTIER(x+0.5), ENTIER(y+0.5))
-				END
-			ELSE
-				x := data.x; y := data.y;
-				data.x := rc.fu; data.y := rc.fv;
-				GfxPaths.EnumArc(data.x0, data.y0, data.x1, data.y1, data.x2, data.y2, x, y, rc.flatness, AddPathElem, data)
-			END
-		| GfxPaths.Bezier:
-			x := data.x; y := data.y;
-			data.x := rc.fu; data.y := rc.fv;
-			GfxPaths.EnumBezier(data.x1, data.y1, data.x2, data.y2, x, y, rc.flatness, AddPathElem, data)
-		| GfxPaths.Exit:
-		END
-	END AddPathElem;
-
-
-	(*--- Thick Lines ---*)
-
-	PROCEDURE StrokeHalfJoin (rc: Context; cu, cv, u, v, du, dv, hu, hv: REAL; part: LONGINT);
-		VAR limit, bu, bv, t: REAL; data: PathData;
-	BEGIN
-		IF (hu # 0) OR (hv # 0) THEN
-			IF du * hv > dv * hu THEN
-				du := -du; dv := -dv; part := 1 - part	(* turn right turns into left turns *)
-			END;
-			limit := rc.devWidth * rc.styleLimit;
-			IF part = 0 THEN
-				IF (rc.joinStyle = Gfx.BevelJoin) OR (rc.joinStyle = Gfx.MiterJoin) & (hu * hu + hv * hv > limit * limit) THEN
-					GfxPaths.IntersectLines(cu, cv, hu, hv, cu + dv, cv - du, -hv, hu, bu, bv);
-					EnterLine(rc, bu, bv); AddLine(rc, cu + dv, cv - du)
-				ELSIF rc.joinStyle = Gfx.MiterJoin THEN
-					bu := cu + hu; bv := cv + hv; EnterLine(rc, bu, bv); AddLine(rc, cu + dv, cv - du)
-				ELSIF rc.joinStyle = Gfx.RoundJoin THEN
-					t := Math.sqrt((du * du + dv * dv)/(hu * hu + hv * hv));
-					bu := cu + t * hu; bv := cv + t * hv; EnterLine(rc, bu, bv);
-					data.context := rc; data.x := rc.fu; data.y := rc.fv;
-					GfxPaths.EnumArc(cu, cv, cu - du, cv - dv, cu + dv, cv - du, cu + dv, cv - du, rc.flatness, AddPathElem, data)
-				ELSE
-					bu := cu + dv; bv := cv - du;
-					EnterLine(rc, bu, bv)
-				END;
-				AddLine(rc, u + dv, v - du); AddLine(rc, u - dv, v + du); AddLine(rc, cu, cv); AddLine(rc, bu, bv)
-			ELSE (* part = 1 *)
-				EnterLine(rc, cu, cv); AddLine(rc, u - dv, v + du); AddLine(rc, u + dv, v - du); AddLine(rc, cu + dv, cv - du);
-				IF (rc.joinStyle = Gfx.BevelJoin) OR (rc.joinStyle = Gfx.MiterJoin) & (hu * hu + hv * hv > limit * limit) THEN
-					GfxPaths.IntersectLines(cu, cv, hu, hv, cu + dv, cv - du, -hv, hu, bu, bv);
-					AddLine(rc, bu, bv)
-				ELSIF rc.joinStyle = Gfx.MiterJoin THEN
-					AddLine(rc, cu + hu, cv + hv)
-				ELSIF rc.joinStyle = Gfx.RoundJoin THEN
-					t := Math.sqrt((du * du + dv * dv)/(hu * hu + hv * hv));
-					data.context := rc; data.x := rc.fu; data.y := rc.fv;
-					GfxPaths.EnumArc(cu, cv, cu - du, cv - dv, cu + dv, cv - du, cu + t * hu, cv + t * hv, rc.flatness, AddPathElem, data)
-				END;
-				AddLine(rc, cu, cv)
-			END
-		END
-	END StrokeHalfJoin;
-
-	PROCEDURE StrokeFullJoin (rc: Context; su, sv, cu, cv, eu, ev, idu, idv, odu, odv, hu, hv: REAL);
-		VAR t, limit: REAL; data: PathData;
-	BEGIN
-		IF (hu # 0) OR (hv # 0) THEN
-			IF idu * odv < idv * odu THEN	(* turn right turns into left turns *)
-				t := idu; idu := -odu; odu := -t;
-				t := idv; idv := -odv; odv := -t;
-				t := su; su := eu; eu := t;
-				t := sv; sv := ev; ev := t
-			END;
-			limit := rc.devWidth * rc.styleLimit;
-			EnterLine(rc, su - idv, sv + idu); AddLine(rc, su + idv, sv - idu); AddLine(rc, cu + idv, cv - idu);
-			IF (rc.joinStyle = Gfx.BevelJoin) OR (rc.joinStyle = Gfx.MiterJoin) & (hu * hu + hv * hv > limit * limit) THEN
-				AddLine(rc, cu + odv, cv - odu)
-			ELSIF rc.joinStyle = Gfx.MiterJoin THEN
-				AddLine(rc, cu + hu, cv + hv)
-			ELSIF rc.joinStyle = Gfx.RoundJoin THEN
-				data.context := rc; data.x := rc.fu; data.y := rc.fv;
-				GfxPaths.EnumArc(cu, cv, cu + idv, cv - idu, cu + idu, cv + idv, cu + odv, cv - odu, rc.flatness, AddPathElem, data)
-			ELSE
-				AddLine(rc, cu, cv); AddLine(rc, cu + odv, cv - odu)
-			END;
-			AddLine(rc, eu + odv, ev - odu); AddLine(rc, eu - odv, ev + odu); AddLine(rc, su - idv, sv + idu)
-		END
-	END StrokeFullJoin;
-
-	PROCEDURE StrokeCap (rc: Context; u, v, du, dv: REAL);
-		VAR data: PathData;
-	BEGIN
-		IF rc.capStyle = Gfx.RoundCap THEN
-			EnterLine(rc, u - dv, v + du);
-			data.context := rc; data.x := rc.fu; data.y := rc.fv;
-			GfxPaths.EnumArc(u, v, u - dv, v + du, u - du, v - dv, u + dv, v - du, rc.flatness, AddPathElem, data);
-			AddLine(rc, u - dv, v + du)
-		ELSIF rc.capStyle = Gfx.SquareCap THEN
-			EnterLine(rc, u - dv, v + du);
-			AddLine(rc, u - du - dv, v - dv + du); AddLine(rc, u - du + dv, v - dv - du); AddLine(rc, u + dv, v - du);
-			AddLine(rc, u - dv, v + du)
-		END
-	END StrokeCap;
-
-	PROCEDURE ThickVerticalLine (rc: Context; lu, v0, v1: REAL);
-		VAR left, right, bot, top: LONGINT;
-	BEGIN
-		IF v0 < v1 THEN
-			left := ENTIER(lu + 0.5); right := left + ENTIER(2*rc.devWidth + 0.5);
-			bot := ENTIER(v0 + 0.5); top := ENTIER(v1 + 0.5)
-		ELSE
-			right := ENTIER(lu + 0.5); left := right - ENTIER(2*rc.devWidth + 0.5);
-			bot := ENTIER(v1 + 0.5); top := ENTIER(v0 + 0.5)
-		END;
-		rc.rect(SHORT(left), SHORT(bot), SHORT(right), SHORT(top))
-	END ThickVerticalLine;
-
-	PROCEDURE ThickHorizontalLine (rc: Context; rv, u0, u1: REAL);
-		VAR left, right, bot, top: LONGINT;
-	BEGIN
-		IF u0 < u1 THEN
-			left := ENTIER(u0 + 0.5); right := ENTIER(u1 + 0.5);
-			bot := ENTIER(rv + 0.5); top := bot + ENTIER(2*rc.devWidth + 0.5)
-		ELSE
-			left := ENTIER(u1 + 0.5); right := ENTIER(u0 + 0.5);
-			top := ENTIER(rv + 0.5); bot := top - ENTIER(2*rc.devWidth + 0.5)
-		END;
-		rc.rect(SHORT(left), SHORT(bot), SHORT(right), SHORT(top))
-	END ThickHorizontalLine;
-
-	PROCEDURE ThickLine (rc: Context; su, sv, eu, ev, du, dv: REAL);
-	BEGIN
-		IF ABS(eu - su) < 0.5 THEN
-			ThickVerticalLine(rc, su - dv, sv, ev)
-		ELSIF ABS(ev - sv) < 0.5 THEN
-			ThickHorizontalLine(rc, sv - du, su, eu)
-		ELSE
-			EnterLine(rc, su - dv, sv + du);
-			AddLine(rc, su + dv, sv - du); AddLine(rc, eu + dv, ev - du);
-			AddLine(rc, eu - dv, ev + du); AddLine(rc, su - dv, sv + du)
-		END
-	END ThickLine;
-
-	PROCEDURE TrimJoinLength (cu, cv, u, v, du, dv, hu, hv: REAL; VAR tu, tv: REAL);
-	BEGIN
-		IF (u - cu + hu) * du + (v - cv + hv) * dv < 0 THEN	(* joint is longer than allowed *)
-			tu := u; tv := v
-		ELSIF du * hv > dv * hu THEN	(* right turn *)
-			tu := cu - hu - dv; tv := cv - hv + du
-		ELSE
-			tu := cu - hu + dv; tv := cv - hv - du
-		END
-	END TrimJoinLength;
-
-	PROCEDURE ThickEnter (rc: Context; u, v, idu, idv: REAL);
-	BEGIN
-		rc.su := u; rc.sv := v;
-		rc.u := u; rc.v := v;
-		rc.du := idu; rc.dv := idv;
-		InitClipState(rc, SHORT(ENTIER(u)), SHORT(ENTIER(v)))
-	END ThickEnter;
-
-	PROCEDURE ThickLineTo (rc: Context; u, v: REAL);
-		VAR cu, cv, idu, idv, odu, odv, hu, hv, tu, tv: REAL;
-	BEGIN
-		cu := rc.u; cv := rc.v;
-		idu := rc.du; idv := rc.dv;
-		Gfx.GetNormVector(u - cu, v - cv, rc.devWidth, odu, odv);
-		IF (cu = rc.su) & (cv = rc.sv) THEN	(* first call *)
-			IF rc.deferred THEN	(* defer rendering of starting cap in case path is later closed *)
-				rc.u1 := u; rc.v1 := v;	(* remember direction of first line *)
-				rc.tu := cu; rc.tv := cv
-			ELSE	(* render initial joint *)
-				Gfx.GetNormVector(idu, idv, rc.devWidth, idu, idv);
-				Gfx.GetHalfAxisVector(idu, idv, odu, odv, hu, hv);
-				IF (hu = 0) & (hv = 0) THEN
-					rc.tu := cu; rc.tv := cv
-				ELSE
-					TrimJoinLength(cu, cv, 0.5*(cu + u), 0.5*(cv + v), odu, odv, hu, hv, rc.tu, rc.tv);
-					StrokeHalfJoin(rc, cu, cv, rc.tu, rc.tv, odu, odv, hu, hv, 0)
-				END
-			END
-		ELSE
-			UpdateClipState(rc, SHORT(ENTIER(cu)), SHORT(ENTIER(cv)));
-			Gfx.GetHalfAxisVector(idu, idv, odu, odv, hu, hv);
-			IF (hu = 0) & (hv = 0) THEN
-				tu := cu; tv := cv
-			ELSE
-				TrimJoinLength(cu, cv, rc.tu, rc.tv, -idu, -idv, hu, hv, tu, tv)
-			END;
-			IF (tu - rc.tu) * idu + (tv - rc.tv) * idv > 0 THEN
-				ThickLine(rc, rc.tu, rc.tv, tu, tv, idu, idv)
-			END;
-			IF (hu = 0) & (hv = 0) THEN
-				rc.tu := cu; rc.tv := cv
-			ELSE
-				TrimJoinLength(cu, cv, 0.5*(cu + u), 0.5*(cv + v), odu, odv, hu, hv, rc.tu, rc.tv);
-				StrokeFullJoin(rc, tu, tv, cu, cv, rc.tu, rc.tv, idu, idv, odu, odv, hu, hv)
-			END
-		END;
-		rc.su := cu; rc.sv := cv;
-		rc.u := u; rc.v := v;
-		rc.du := odu; rc.dv := odv
-	END ThickLineTo;
-
-	PROCEDURE ThickExit (rc: Context; odu, odv: REAL);
-		VAR cu, cv, idu, idv, hu, hv, tu, tv: REAL;
-	BEGIN
-		cu := rc.u; cv := rc.v;
-		IF (cu # rc.su) OR (cv # rc.sv) THEN	(* at least one thick line was rendered *)
-			idu := rc.du; idv := rc.dv;
-			UpdateClipState(rc, SHORT(ENTIER(cu)), SHORT(ENTIER(cv)));
-			IF (odu = 0) & (odv = 0) THEN
-				ThickLine(rc, rc.tu, rc.tv, cu, cv, idu, idv);
-				StrokeCap(rc, cu, cv, -idu, -idv)
-			ELSE
-				Gfx.GetNormVector(odu, odv, rc.devWidth, odu, odv);
-				Gfx.GetHalfAxisVector(idu, idv, odu, odv, hu, hv);
-				IF (hu = 0) & (hv = 0) THEN
-					tu := cu; tv := cv
-				ELSE
-					TrimJoinLength(cu, cv, rc.tu, rc.tv, -idu, -idv, hu, hv, tu, tv)
-				END;
-				IF (tu - rc.tu) * idu + (tv - rc.tv) * idv > 0 THEN
-					ThickLine(rc, rc.tu, rc.tv, tu, tv, idu, idv)
-				END;
-				IF (hu # 0) OR (hv # 0) THEN
-					StrokeHalfJoin(rc, cu, cv, tu, tv, idu, idv, hu, hv, 1)
-				END
-			END;
-			IF rc.deferred THEN	(* render cap at start pos *)
-				InitClipState(rc, SHORT(ENTIER(rc.u0)), SHORT(ENTIER(rc.v0)));
-				Gfx.GetNormVector(rc.u1 - rc.u0, rc.v1 - rc.v0, rc.devWidth, odu, odv);
-				StrokeCap(rc, rc.u0, rc.v0, odu, odv)
-			END
-		END
-	END ThickExit;
-
-	PROCEDURE ThickClose (rc: Context);
-		VAR cu, cv, idu, idv, odu, odv, hu, hv, tu, tv, eu, ev: REAL;
-	BEGIN
-		cu := rc.u; cv := rc.v; idu := rc.du; idv := rc.dv;
-		UpdateClipState(rc, SHORT(ENTIER(cu)), SHORT(ENTIER(cv)));
-		Gfx.GetNormVector(rc.u1 - rc.u0, rc.v1 - rc.v0, rc.devWidth, odu, odv);
-		Gfx.GetHalfAxisVector(idu, idv, odu, odv, hu, hv);
-		IF (hu = 0) & (hv = 0) THEN
-			tu := cu; tv := cv
-		ELSE
-			TrimJoinLength(cu, cv, rc.tu, rc.tv, -idu, -idv, hu, hv, tu, tv)
-		END;
-		IF (tu - rc.tu) * idu + (tv - rc.tv) * idv > 0 THEN
-			ThickLine(rc, rc.tu, rc.tv, tu, tv, idu, idv)
-		END;
-		IF (hu # 0) OR (hv # 0) THEN
-			TrimJoinLength(cu, cv, 0.5*(cu + rc.u1), 0.5*(cv + rc.v1), odu, odv, hu, hv, eu, ev);
-			StrokeFullJoin(rc, tu, tv, cu, cv, eu, ev, idu, idv, odu, odv, hu, hv)
-		END
-	END ThickClose;
-
-
-	(*--- Dashed Lines ---*)
-
-	PROCEDURE DashEnter (rc: Context; su, sv, idu, idv: REAL);
-		VAR beg, end, next: REAL; index: LONGINT;
-	BEGIN
-		rc.offset := 0;
-		rc.GetDashOffsets(0, beg, end, next, index);
-		IF end > 0 THEN
-			IF rc.devWidth <= 0.75 THEN HairLineEnter(rc, su, sv)
-			ELSE ThickEnter(rc, su, sv, idu, idv)
-			END
-		ELSE
-			rc.u := su; rc.v := sv
-		END
-	END DashEnter;
-
-	PROCEDURE DashLineTo (rc: Context; u, v: REAL);
-		VAR
-			cu, cv, du, dv, len, cos, sin, wdu, wdv, offset, beg, end, next, dash, tu, tv, u1, v1: REAL;
-			index: LONGINT; deferred: BOOLEAN;
-	BEGIN
-		cu := rc.u; cv := rc.v;
-		du := u - cu; dv := v - cv;
-		len := Math.sqrt(du * du + dv * dv);
-		cos := du/len; sin := dv/len;
-		wdu := rc.devWidth * cos; wdv := rc.devWidth * sin;
-		offset := rc.offset; rc.offset := offset + len;
-		rc.GetDashOffsets(offset, beg, end, next, index);
-		IF offset < end THEN	(* inside dash *)
-			IF end <= rc.offset THEN	(* finish current dash *)
-				len := end - offset;
-				IF rc.devWidth <= 0.75 THEN
-					HairLineTo(rc, cu + len * cos, cv + len * sin)
-				ELSE
-					ThickLineTo(rc, cu + len * cos, cv + len * sin);	(* sets u1/v1 if first line in subpath *)
-					deferred := rc.deferred; rc.deferred := FALSE;
-					ThickExit(rc, 0, 0);
-					rc.deferred := deferred
-				END
-			ELSIF rc.devWidth <= 0.75 THEN
-				HairLineTo(rc, u, v)
-			ELSE
-				ThickLineTo(rc, u, v)
-			END
-		END;
-		beg := offset;
-		LOOP
-			len := next - beg;
-			cu := cu + len * cos; cv := cv + len * sin;
-			beg := next;
-			GfxMatrix.ApplyToDist(rc.cam, rc.dashPatOn[index], dash);
-			end := beg + dash;
-			GfxMatrix.ApplyToDist(rc.cam, rc.dashPatOff[index], dash);
-			next := end + dash;
-			index := (index+1) MOD rc.dashPatLen;
-			IF end > rc.offset THEN EXIT END;
-			len := end - beg;
-			IF rc.devWidth <= 0.75 THEN
-				HairLineEnter(rc, cu, cv);
-				HairLineTo(rc, cu + len * cos, cv + len * sin)
-			ELSE
-				StrokeCap(rc, cu, cv, wdu, wdv);
-				InitClipState(rc, SHORT(ENTIER(cu)), SHORT(ENTIER(cv)));
-				tu := cu + len * cos; tv := cv + len * sin;
-				UpdateClipState(rc, SHORT(ENTIER(tu)), SHORT(ENTIER(tv)));
-				ThickLine(rc, cu, cv, tu, tv, wdu, wdv);
-				StrokeCap(rc, tu, tv, -wdu, -wdv)
-			END
-		END;
-		IF beg <= rc.offset THEN	(* begin next dash *)
-			IF rc.devWidth <= 0.75 THEN
-				HairLineEnter(rc, cu, cv);
-				HairLineTo(rc, u, v)
-			ELSE
-				u1 := rc.u1; v1 := rc.v1;
-				StrokeCap(rc, cu, cv, wdu, wdv);
-				ThickEnter(rc, cu, cv, 0, 0);
-				ThickLineTo(rc, u, v);
-				rc.u1 := u1; rc.v1 := v1	(* restore original point *)
-			END
-		ELSE
-			rc.u := u; rc.v := v
-		END
-	END DashLineTo;
-
-	PROCEDURE DashExit (rc: Context; odu, odv: REAL);
-		VAR beg, end, next: REAL; index: LONGINT;
-	BEGIN
-		IF rc.devWidth > 0.75 THEN
-			rc.GetDashOffsets(rc.offset, beg, end, next, index);
-			IF (beg < rc.offset) & (rc.offset < end) THEN
-				ThickExit(rc, odu, odv)
-			END
-		END
-	END DashExit;
-
-	PROCEDURE DashClose (rc: Context);
-		VAR beg, end, next: REAL; index: LONGINT;
-	BEGIN
-		IF rc.deferred & (rc.devWidth > 0.75) THEN
-			rc.GetDashOffsets(rc.offset, beg, end, next, index);
-			IF (beg < rc.offset) & (rc.offset < end) THEN
-				ThickClose(rc)
-			END
-		END
-	END DashClose;
-
-
-	(*--- Stroking ---*)
-
-	PROCEDURE StrokePrepare (rc: Context);
-	BEGIN
-		GfxMatrix.ApplyToDist(rc.cam, 0.5*rc.lineWidth, rc.devWidth);
-		IF rc.devWidth <= 0.75 THEN
-			rc.border := 1
-		ELSE
-			rc.border := -SHORT(ENTIER(-rc.devWidth * rc.styleLimit));
-		END;
-		rc.SetColPat(rc.strokeCol, rc.strokePat)
-	END StrokePrepare;
-
-	PROCEDURE StrokeEnter (rc: Context; u, v, du, dv: REAL);
-	BEGIN
-		IF rc.devWidth > 0.75 THEN
-			rc.pathReg.Clear()
-		END;
-		IF rc.dashPatLen > 0 THEN
-			DashEnter(rc, u, v, du, dv)
-		ELSIF rc.devWidth <= 0.75 THEN
-			HairLineEnter(rc, u, v)
-		ELSE
-			ThickEnter(rc, u, v, du, dv)
-		END
-	END StrokeEnter;
-
-	PROCEDURE StrokeLineTo (rc: Context; u, v: REAL);
-	BEGIN
-		IF rc.dashPatLen > 0 THEN
-			DashLineTo(rc, u, v)
-		ELSIF rc.devWidth <= 0.75 THEN
-			HairLineTo(rc, u, v)
-		ELSE
-			ThickLineTo(rc, u, v)
-		END
-	END StrokeLineTo;
-
-	PROCEDURE StrokeExit (rc: Context; du, dv: REAL);
-	BEGIN
-		IF rc.dashPatLen > 0 THEN
-			DashExit(rc, du, dv)
-		ELSIF rc.devWidth > 0.75 THEN
-			ThickExit(rc, du, dv)
-		END;
-		IF rc.devWidth > 0.75 THEN
-			rc.pathReg.Intersect(rc.clipReg);
-			IF ~rc.pathReg.Empty() THEN
-				rc.clipState := In;
-				FillRegion(rc)
-			END
-		END
-	END StrokeExit;
-
-	PROCEDURE StrokeClose (rc: Context);
-	BEGIN
-		IF rc.dashPatLen > 0 THEN
-			DashClose(rc)
-		ELSIF rc.devWidth > 0.75 THEN
-			ThickClose(rc)
-		END;
-		IF rc.devWidth > 0.75 THEN
-			rc.pathReg.Intersect(rc.clipReg);
-			IF ~rc.pathReg.Empty() THEN
-				rc.clipState := In;
-				FillRegion(rc)
-			END
-		END
-	END StrokeClose;
-
-	PROCEDURE StrokePathElem (VAR data: GfxPaths.EnumData);
-		VAR rc: Context; x, y: REAL;
-	BEGIN
-		rc := data(PathData).context;
-		CASE data.elem OF
-		| GfxPaths.Enter:
-			StrokeEnter(rc, data.x, data.y, data.dx, data.dy)
-		| GfxPaths.Line:
-			IF (data.x # rc.u) OR (data.y # rc.v) THEN
-				StrokeLineTo(rc, data.x, data.y)
-			END
-		| GfxPaths.Arc:
-			IF (rc.dashPatLen = 0) & (rc.devWidth <= 0.75) &
-				IsEllipse(data.x0, data.y0, data.x1, data.y1, data.x2, data.y2, data.x, data.y, rc.u, rc.v, rc.flatness, x, y)
-			THEN
-				IF ABS(ABS(x) - ABS(y)) < rc.flatness THEN
-					IF x * y > 0 THEN
-						HairCircle(rc, ENTIER(data.x0), ENTIER(data.y0), ENTIER(ABS(x) + 0.5))
-					ELSE
-						HairCircle(rc, ENTIER(data.x0), ENTIER(data.y0), -ENTIER(ABS(x) + 0.5))
-					END
-				ELSE
-					HairEllipse(rc, ENTIER(data.x0), ENTIER(data.y0), ENTIER(ABS(x) + 0.5), ENTIER(ABS(y) + 0.5))
-				END
-			ELSE
-				x := data.x; y := data.y;
-				data.x := rc.u; data.y := rc.v;
-				GfxPaths.EnumArc(data.x0, data.y0, data.x1, data.y1, data.x2, data.y2, x, y, rc.flatness, StrokePathElem, data)
-			END
-		| GfxPaths.Bezier:
-			x := data.x; y := data.y;
-			data.x := rc.u; data.y := rc.v;
-			GfxPaths.EnumBezier(data.x1, data.y1, data.x2, data.y2, x, y, rc.flatness, StrokePathElem, data)
-		| GfxPaths.Exit:
-			StrokeExit(rc, data.dx, data.dy)
-		END
-	END StrokePathElem;
-
-
-	(**--- Raster Contexts ---**)
-
-	(** initialize raster context **)
-	PROCEDURE InitContext* (rc: Context);
-	BEGIN
-		rc.InitRaster();
-	END InitContext;
-
-END GfxRaster.

+ 0 - 1356
source/AGfxRegions.Mod

@@ -1,1356 +0,0 @@
-(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
-Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
-
-MODULE GfxRegions; (** portable *)	(* eos  *)
-(** AUTHOR "eos"; PURPOSE "Arbitrarily shaped two_dimensional regions"; *)
-
-	(*
-		17.2.97 - eliminated rectangle type, added shift offsets, made enumerator extensible
-		2.5.97 - prevent dropouts when validating
-		17.7.97 - fixed bug in Validate (trying to copy filler spans if data was reallocated)
-		17.7.97 - eliminated size field
-		12.3.98 - eliminated shifted operations, fixed Shift to treat boundary cases correctly
-		5.5.98 - fixed bug in Intersect (wrong index into arg region)
-	*)
-
-	CONST
-		(** mode for reducing regions to non_overlapping areas **)
-		Winding* = 0;	(** non_zero winding rule **)
-		EvenOdd* = 1;	(** exclusive_or rule **)
-
-		(** interval of valid region coordinates (UBound - LBound is still representable within INTEGER **)
-		UBound* = MAX(INTEGER) DIV 2;
-		LBound* = MIN(INTEGER) DIV 2;
-
-		BlockSize = 512;	(* size increment for region data blocks *)
-		Enter = 1; Exit = -1;	(* direction of bounding curve at scanline intersection *)
-		FirstSlice = 2;	(* index of first slice *)
-		Bottom = MIN(INTEGER); Top = MAX(INTEGER);	(* sentinel values *)
-
-
-	TYPE
-		RegionData = POINTER TO ARRAY OF LONGINT;
-
-		(** regions of arbitrary shape **)
-		(*
-			Implementation notes:
-
-			Regions are managed by slicing them horizontally. For each scanline y, a set of spans on the scanline defines which
-			parts of the scanline are part of the region. The spans are defined through the x_coordinates of their end points.
-			Every point on a scanline has a direction attribute, which specifies whether the point starts a span (Enter) or ends
-			one (Exit), allowing spans to nest or overlap.
-
-			The x_ and y_coordinates of a point along with its direction are encoded into a LONGINT. The chosen encoding
-			weights the y_coordinate most, followed by the x_coordinate and the direction of an intersection. Visiting all
-			encoded points in ascending order therefore traverses all spans of the region from the bottom left corner to the
-			top right corner.
-
-			In order to save space, identical slices adjacent to each other are stored only once. The bottommost scanline of
-			an identical sequence of scanlines serves as a representant for the whole sequence; all others are eliminated.
-			This means that if no points exist for a certain y_coordinate, the spans of the corresponding scanline are identical
-			to those of the one below it. As a consequence, scanlines that are completely outside the region need an empty
-			filler span to distinguish them from eliminated scanlines. A filler span consists of two points located at UBound,
-			one entering the region and the other leaving it.
-
-			Most operations modifying regions append new points in ascending order to the sequence of existing points and
-			then merge the two sequences again. If points cannot be appended in order, the whole set of points has to be
-			sorted before any other operation can be executed. Doing this immediately after the sequence of points has been
-			invalidated can decrease performance significantly if a lot of invalidating operations are issued in sequence, as is
-			typically the case with AddPoint. This is why regions have a valid flag, indicating whether encoded points are sorted
-			or not. Invalidating operations only have to set valid to FALSE, other operations will eventually validate the region
-			again, at the same time eliminating multiple points and overlapping spans.
-		*)
-		
-		
-		Region* = OBJECT
-		VAR
-			llx*, lly*, urx*, ury*: INTEGER;	(** bounding box **)
-			mode*: INTEGER;	(** mode for reducing region to non_overlapping areas (Winding/EvenOdd) **)
-			valid: BOOLEAN;	(* flag if points in data array are consistent (i.e. sorted & compacted) *)
-			data: RegionData;	(* points defining region boundary *)
-			points: LONGINT;	(* number of data points actually used *)
-
-			
-	(**--- Region Construction ---**)
-
-			(** make region empty **)
-			PROCEDURE Clear* ();
-			BEGIN
-				SELF.llx := UBound; SELF.lly := UBound;
-				SELF.urx := LBound; SELF.ury := LBound;
-				SELF.valid := TRUE;
-				SELF.points := 0
-			END Clear;
-
-			(** set region mode **)
-			PROCEDURE SetMode* (mode: INTEGER);
-			BEGIN
-				SELF.mode := mode
-			END SetMode;
-
-			(** initialize region **)
-			PROCEDURE Init* (mode: INTEGER);
-			BEGIN
-				SELF.mode := mode;
-				(* SELF.data := NIL; *)
-				SELF.Clear()
-			END Init;
-
-			(** make region rectangular **)
-			PROCEDURE SetToRect* (llx, lly, urx, ury: INTEGER);
-			BEGIN
-				IF RectEmpty(llx, lly, urx, ury) THEN
-					SELF.Clear()
-				ELSE
-					ClipRect(llx, lly, urx, ury, LBound, LBound, UBound, UBound);
-					SELF.llx := llx; SELF.lly := lly; SELF.urx := urx; SELF.ury := ury;
-					SELF.valid := TRUE;
-					SELF.points := 0
-				END
-			END SetToRect;
-
-			(** shift region **)
-			PROCEDURE Shift* (dx, dy: INTEGER);
-				VAR rdata: RegionData; rn: LONGINT; ru, rv, rdir: INTEGER;
-			BEGIN
-				IF (dx # 0) OR (dy # 0) THEN
-					INC(SELF.llx, dx); INC(SELF.lly, dy); INC(SELF.urx, dx); INC(SELF.ury, dy);
-					IF SELF.points > 0 THEN
-						rdata := SELF.data; rn := FirstSlice;
-						Decode(rdata[rn], ru, rv, rdir);
-						WHILE rv < Top DO
-							IF (ru <= LBound) OR (ru + dx <= LBound) THEN ru := LBound
-							ELSIF (ru >= UBound) OR (ru + dx >= UBound) THEN ru := UBound
-							ELSE INC(ru, dx)
-							END;
-							IF (dy < 0) & (rv < Bottom - dy) THEN rv := Bottom
-							ELSIF (dy > 0) & (rv > Top - dy) THEN rv := Top
-							ELSE INC(rv, dy)
-							END;
-							Encode(rdata[rn], ru, rv, rdir);
-							INC(rn);
-							Decode(rdata[rn], ru, rv, rdir)
-						END
-					END
-				END
-			END Shift;
-
-			(** copy region **)
-			PROCEDURE Copy* (to: Region);
-			BEGIN
-				to.mode := SELF.mode;
-				SELF.CopyData(to)
-			END Copy;
-
-			(** add second region to first **)
-			PROCEDURE Add* (arg: Region);
-				VAR rdata, adata: RegionData; points, aslice, an, rn, rslice: LONGINT; au, av, adir, ru, rv, rdir, top, ry, ay, y: INTEGER;
-			BEGIN
-				IF ~RectEmpty(arg.llx, arg.lly, arg.urx, arg.ury) THEN
-					IF RectEmpty(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
-						arg.CopyData(SELF)
-					ELSIF arg.IsRect() & SELF.RectInside(arg.llx, arg.lly, arg.urx, arg.ury) THEN
-						(* do nothing *)
-					ELSIF SELF.IsRect() & arg.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
-						arg.CopyData(SELF)
-					ELSE
-						SELF.Validate(); arg.Validate();
-						SELF.MakeData(); arg.MakeData();
-						rdata := SELF.data; adata := arg.data;
-						points := SELF.points;
-
-						IF arg.lly < SELF.lly THEN
-							(* copy scanlines below SELF *)
-							arg.FindUpper(SELF.lly, aslice);
-							an := FirstSlice;
-							WHILE an < aslice DO
-								Decode(adata[an], au, av, adir);
-								SELF.Append(au, av, adir);
-								INC(an)
-							END;
-							rn := FirstSlice;
-							arg.FindLower(SELF.lly, an)
-						ELSE
-							SELF.FindLower(arg.lly, rn);
-							an := FirstSlice
-						END;
-
-						Decode(rdata[rn], ru, rv, rdir);
-						Decode(adata[an], au, av, adir);
-						rslice := rn; aslice := an;
-						top := MIN(SELF.ury, arg.ury);
-
-						WHILE (av < top) OR (rv < top) DO
-							(* merge slices *)
-							ry := rv; ay := av; y := MAX(ry, ay);
-							REPEAT
-								IF (av > ay) OR (rv = ry) & (ru <= au) THEN
-									IF rv # y THEN	(* do not duplicate points *)
-										SELF.Append(ru, y, rdir)
-									END;
-									INC(rn);
-									Decode(rdata[rn], ru, rv, rdir)
-								ELSE
-									SELF.Append(au, y, adir);
-									INC(an);
-									Decode(adata[an], au, av, adir)
-								END
-							UNTIL (rv > ry) & (av > ay);
-
-							(* advance to next slice *)
-							IF rv < av THEN
-								an := aslice; rslice := rn;
-								Decode(adata[an], au, av, adir)
-							ELSIF av < rv THEN
-								rn := rslice; aslice := an;
-								Decode(rdata[rn], ru, rv, rdir)
-							ELSE
-								rslice := rn; aslice := an
-							END
-						END;
-
-						(* copy slices above SELF *)
-						IF arg.ury > SELF.ury THEN
-							REPEAT
-								SELF.Append(au, av, adir);
-								INC(an);
-								Decode(adata[an], au, av, adir)
-							UNTIL av = Top
-						END;
-
-						SELF.Merge(points);
-						IncludeRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury)
-					END
-				END
-			END Add;
-
-			(** add rectangle to region **)
-			PROCEDURE AddRect* (llx, lly, urx, ury: INTEGER);
-				VAR reg: Region;
-			BEGIN{EXCLUSIVE}
-				reg.SetToRect(llx, lly, urx, ury);
-				SELF.Add(reg)
-			END AddRect;
-
-			(** subtract second region from first **)
-			PROCEDURE Subtract* (arg: Region);
-				VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, top, ry, ay, y: INTEGER;
-			BEGIN
-				IF ~RectEmpty(arg.llx, arg.lly, arg.urx, arg.ury) THEN
-					IF RectEmpty(SELF.llx, SELF.lly, SELF.urx, SELF.ury) OR SELF.RegionInside(arg) THEN
-						SELF.Clear()
-					ELSIF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
-						SELF.Validate(); arg.Validate();
-						SELF.MakeData(); arg.MakeData();
-						rdata := SELF.data; adata := arg.data;
-						points := SELF.points;
-						IF SELF.lly <= arg.lly THEN
-							SELF.FindLower(arg.lly, rn);
-							an := FirstSlice
-						ELSE
-							rn := FirstSlice;
-							arg.FindLower(SELF.lly, an)
-						END;
-						Decode(rdata[rn], ru, rv, rdir);
-						Decode(adata[an], au, av, adir);
-
-						rslice := rn; aslice := an;
-						top := MIN(SELF.ury, arg.ury);
-						WHILE (rv < top) OR (av < top) DO
-							(* merge slices *)
-							ry := rv; ay := av; y := MAX(ry, ay);
-							REPEAT
-								IF (av > ay) OR (rv = ry) & (ru <= au) THEN
-									IF rv # y THEN	(* do not duplicate points *)
-										SELF.Append(ru, y, rdir)
-									END;
-									INC(rn);
-									Decode(rdata[rn], ru, rv, rdir)
-								ELSE
-									SELF.Append(au, y, -adir);
-									INC(an);
-									Decode(adata[an], au, av, adir)
-								END
-							UNTIL (rv > ry) & (av > ay);
-
-							(* advance to next slice *)
-							IF rv < av THEN
-								an := aslice; rslice := rn;
-								Decode(adata[an], au, av, adir)
-							ELSIF av < rv THEN
-								rn := rslice; aslice := an;
-								Decode(rdata[rn], ru, rv, rdir)
-							ELSE
-								rslice := rn; aslice := an
-							END
-						END;
-
-						SELF.Merge(points);
-						SELF.CalcRect()
-					END
-				END
-			END Subtract;
-
-			(** subtract rectangle from region **)
-			PROCEDURE SubtractRect* (llx, lly, urx, ury: INTEGER);
-				VAR reg: Region;
-			BEGIN{EXCLUSIVE}
-				reg.SetToRect(llx, lly, urx, ury);
-				SELF.Subtract(reg)
-			END SubtractRect;
-
-		(** intersect first region with second region **)
-		PROCEDURE Intersect* (arg: Region);
-			VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, ry, ay, y: INTEGER;
-		BEGIN
-			IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
-				SELF.Clear()
-
-			ELSIF ~arg.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
-				SELF.Validate(); arg.Validate();
-				SELF.MakeData(); arg.MakeData();
-				rdata := SELF.data; adata := arg.data;
-				points := SELF.points;
-
-				(* cut off slices above arg *)
-				IF SELF.ury > arg.ury THEN
-					SELF.FindUpper(arg.ury, points);
-					Encode(rdata[points], UBound, arg.ury, Enter); INC(points);
-					Encode(rdata[points], UBound, arg.ury, Exit); INC(points);
-					Encode(rdata[points], UBound, Top, Exit); INC(points);
-					SELF.points := points
-				END;
-
-				(* delete slices below arg *)
-				IF SELF.lly < arg.lly THEN
-					SELF.FindLower(arg.lly, rn);
-					IF rn > FirstSlice THEN
-						points := FirstSlice;
-						WHILE rn < SELF.points DO
-							rdata[points] := rdata[rn];
-							INC(points); INC(rn)
-						END;
-						SELF.points := points
-					END;
-
-					rn := FirstSlice;
-					Decode(rdata[rn], ru, rv, rdir);
-					ry := rv;
-					REPEAT
-						Encode(rdata[rn], ru, arg.lly, rdir);
-						INC(rn);
-						Decode(rdata[rn], ru, rv, rdir)
-					UNTIL rv > ry;
-
-					rn := FirstSlice; an := FirstSlice
-				ELSE
-					rn := FirstSlice;
-					arg.FindLower(SELF.lly, an)
-				END;
-
-				Decode(rdata[rn], ru, rv, rdir);
-				Decode(adata[an], au, av, adir);
-				rslice := rn; aslice := an;
-
-				WHILE rv < SELF.ury DO
-					(* merge intersecting slices *)
-					ry := rv; ay := av; y := MAX(ry, ay);
-					SELF.Append(LBound, y, Exit);
-					REPEAT
-						IF (av > ay) OR (rv = ry) & (ru <= au) THEN
-							IF rv # y THEN	(* do not duplicate existing points *)
-								SELF.Append(ru, y, rdir)
-							END;
-							INC(rn);
-							Decode(rdata[rn], ru, rv, rdir)
-						ELSE
-							SELF.Append(au, y, adir);
-							INC(an);
-							Decode(adata[an], au, av, adir)
-						END
-					UNTIL (rv > ry) & (av > ay);
-					SELF.Append(UBound, y, Enter);
-
-					(* advance to next slice *)
-					IF rv < av THEN
-						an := aslice; rslice := rn;
-						Decode(adata[an], au, av, adir)
-					ELSIF av < rv THEN
-						rn := rslice; aslice := an;
-						Decode(rdata[rn], ru, rv, rdir)
-					ELSE
-						rslice := rn; aslice := an
-					END
-				END;
-
-				SELF.Merge(points);
-				SELF.CalcRect()
-			END
-		END Intersect;
-
-			(** intersect region with rectangle **)
-			PROCEDURE IntersectRect* (llx, lly, urx, ury: INTEGER);
-				VAR reg: Region;
-			BEGIN{EXCLUSIVE}
-				reg.SetToRect(llx, lly, urx, ury);
-				SELF.Intersect(reg)
-			END IntersectRect;
-
-			(** invert region **)
-			PROCEDURE Invert* ();
-				VAR data: RegionData; points, n: LONGINT; u, v, dir, y: INTEGER;
-			BEGIN
-				IF RectEmpty(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
-					SELF.SetToRect(LBound, LBound, UBound, UBound)
-				ELSE
-					SELF.Validate();
-					SELF.MakeData();
-					data := SELF.data;
-					points := SELF.points;
-					n := FirstSlice;
-					Decode(data[n], u, v, dir);
-
-					IF SELF.lly > LBound THEN
-						SELF.Append(LBound, LBound, Enter);
-						SELF.Append(UBound, LBound, Exit)
-					END;
-
-					REPEAT
-						y := v;
-						SELF.Append(LBound, y, Enter);
-						REPEAT
-							Encode(data[n], u, y, -dir);
-							INC(n);
-							Decode(data[n], u, y, dir)
-						UNTIL v > y;
-						SELF.Append(UBound, y, Exit)
-					UNTIL v >= UBound;
-
-					IF y < UBound THEN
-						SELF.Append(LBound, y, Enter);
-						SELF.Append(UBound, y, Exit)
-					END;
-
-					SELF.Merge(points);
-					SELF.CalcRect()
-				END
-			END Invert;
-
-			(**
-				In addition to creating rectangular regions and using Boolean operations to combine several regions, a region
-				can also be built by tracing its outline with AddPoint. In order to allow the correct handling of self_intersecting
-				contours, a direction parameter is needed which indicates whether the curve is going up (dy = 1) or down
-				(dy = -1) at the given point.
-
-				When performing Boolean operations upon regions or when building regions from self_intersecting contours,
-				it is possible that some areas in the resulting region get "covered" more than once. Since most query operations
-				reduce regions to non_overlapping areas, a rule which decides whether a point is inside a region or not is needed.
-				Imagine a ray originating at such a point and counting every intersection of the ray with the boundary curve of the
-				region as +1 if the curve crosses the ray from right to left and as -1 otherwise.
-					- for mode Winding (the default), a point is inside the region if the resulting sum is non_zero
-					- for mode EvenOdd, a point is inside the region if the resulting sum is odd
-
-				Behaviour of all region queries and region operations is undefined for contours which are not closed.
-			**)
-
-			(** add a scanline intersection to a region **)
-			PROCEDURE AddPoint* (x, y, dy: INTEGER);
-			BEGIN
-				IF (dy # 0) & (y >= LBound) & (y <= UBound) THEN
-					IF x < LBound THEN x := LBound
-					ELSIF x > UBound THEN x := UBound
-					END;
-					SELF.MakeData();
-					IncludePoint(SELF.llx, SELF.lly, SELF.urx, SELF.ury, x, y);
-					SELF.Append(x, y + (-dy) DIV 2, dy);	(* dy = -1 => y, dy = 1 => y - 1 *)
-					SELF.valid := FALSE
-				END
-			END AddPoint;
-			
-		(**--- Region Queries ---**)
-
-			(** return whether region is empty **)
-			PROCEDURE Empty* (): BOOLEAN;
-			BEGIN
-				RETURN (SELF.llx >= SELF.urx) OR (SELF.lly >= SELF.ury)
-			END Empty;
-
-			(** return whether (non_empty) region is rectangular **)
-			PROCEDURE IsRect* (): BOOLEAN;
-			BEGIN
-				SELF.Validate();
-				RETURN SELF.points = 0
-			END IsRect;
-
-			(** return whether point is inside (non_empty) region **)
-			PROCEDURE PointInside* (x, y: INTEGER): BOOLEAN;
-				VAR data: RegionData; n: LONGINT; u, v, dir: INTEGER;
-			BEGIN
-				IF ~PointInRect(x, y, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN	(* point not even within region rectangle *)
-					RETURN FALSE
-				ELSIF SELF.IsRect() THEN	(* region is rectangular *)
-					RETURN TRUE
-				END;
-
-				(* find span containing point *)
-				data := SELF.data;
-				SELF.FindLower(y, n);
-				Decode(data[n], u, v, dir);
-				WHILE u < x DO
-					INC(n);
-					Decode(data[n], u, v, dir)
-				END;
-				RETURN (u = x) & (dir = Enter) OR (u > x) & (dir = Exit)
-			END PointInside;
-
-			(** return whether (non_empty) rectangle is completely inside (non_empty) region **)
-			PROCEDURE RectInside* (llx, lly, urx, ury: INTEGER): BOOLEAN;
-				VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
-			BEGIN
-				IF ~RectInRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN	(* not even within bounding rectangle *)
-					RETURN FALSE
-				ELSIF SELF.IsRect() THEN	(* region is rectangular *)
-					RETURN TRUE
-				END;
-
-				data := SELF.data;
-				SELF.FindLower(lly, n);
-				Decode(data[n], u, v, dir);
-				REPEAT
-					y := v;
-					WHILE (v = y) & (u <= llx) DO
-						INC(n);
-						Decode(data[n], u, v, dir)
-					END;
-					IF (v > y) OR (u < urx) OR (dir = Enter) THEN	(* rectangle not covered by span *)
-						RETURN FALSE
-					END;
-
-					(* skip to next line *)
-					WHILE v = y DO
-						INC(n);
-						Decode(data[n], u, v, dir)
-					END
-				UNTIL v >= ury;
-
-				RETURN TRUE	(* rectangle is fully covered by spans *)
-			END RectInside;
-
-			(** return whether (non_empty) rectangle overlaps (non_empty) region **)
-			PROCEDURE RectOverlaps* (llx, lly, urx, ury: INTEGER): BOOLEAN;
-				VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
-			BEGIN
-				IF ~RectsIntersect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
-					RETURN FALSE	(* rect does not even intersect region rectangle *)
-				ELSIF SELF.IsRect() THEN	(* region is rectangular *)
-					RETURN TRUE
-				END;
-
-				ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
-				data := SELF.data;
-				SELF.FindLower(lly, n);
-				Decode(data[n], u, v, dir);
-				REPEAT
-					y := v;
-					WHILE (v = y) & (u <= llx) DO
-						INC(n);
-						Decode(data[n], u, v, dir)
-					END;
-					IF (v = y) & ((u < urx) OR (dir = Exit)) THEN
-						RETURN TRUE
-					END;
-
-					(* skip to next line *)
-					WHILE v = y DO
-						INC(n);
-						Decode(data[n], u, v, dir)
-					END
-				UNTIL v >= ury;
-
-				RETURN FALSE	(* rectangle does not intersect any span *)
-			END RectOverlaps;
-
-			(** return whether region is completely within another region **)
-			PROCEDURE RegionInside* (outer: Region): BOOLEAN;
-				VAR idata, odata: RegionData; in, on, is, os: LONGINT; iu, iv, idir, ou, ov, odir, iy, oy: INTEGER;
-			BEGIN
-				IF ~RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, outer.llx, outer.lly, outer.urx, outer.ury) THEN
-					RETURN FALSE	(* SELF rect not even within outer rect *)
-				ELSIF outer.IsRect() THEN
-					RETURN TRUE	(* outer region fully covers SELF region *)
-				ELSIF SELF.IsRect() THEN
-					RETURN outer.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
-				END;
-
-				idata := SELF.data; odata := outer.data;
-				in := FirstSlice;
-				outer.FindLower(SELF.lly, on);
-				Decode(idata[in], iu, iv, idir);
-				Decode(odata[on], ou, ov, odir);
-				is := in; os := on;
-				REPEAT
-					iy := iv; oy := ov;
-
-					(* skip empty slices *)
-					WHILE (iv = iy) & (iu = UBound) DO
-						INC(in);
-						Decode(idata[in], iu, iv, idir)
-					END;
-
-					(* compare slices *)
-					WHILE (iv = iy) OR (ov = oy) DO
-						IF (ov > oy) OR (iv = iy) & (idir = Exit) & (odir = Enter) THEN
-							RETURN FALSE
-						END;
-						IF (iv > iy) OR (ou <= iu) THEN
-							INC(on);
-							Decode(odata[on], ou, ov, odir)
-						ELSE
-							INC(in);
-							Decode(idata[in], iu, iv, idir)
-						END
-					END;
-
-					(* reset to begin of slice if not on same line *)
-					IF iv > ov THEN
-						in := is; os := on;
-						Decode(idata[in], iu, iv, idir)
-					ELSIF ov > iv THEN
-						on := os; is := in;
-						Decode(odata[on], ou, ov, odir)
-					ELSE
-						is := in; os := on
-					END
-				UNTIL iv = SELF.ury;
-
-				RETURN TRUE	(* all spans were covered by enclosing region *)
-			END RegionInside;
-
-			(** return whether two regions intersect each other **)
-			PROCEDURE RegionOverlaps* (arg: Region): BOOLEAN;
-				VAR rdata, adata: RegionData; bot, top, ru, rv, rdir, au, av, adir, ry, ay: INTEGER; rn, an, rs, as: LONGINT;
-			BEGIN
-				IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
-					RETURN FALSE	(* rect does not even intersect arg's bounding box *)
-				ELSIF SELF.IsRect() THEN
-					RETURN arg.RectOverlaps(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
-				ELSIF arg.IsRect() THEN
-					RETURN SELF.RectOverlaps(arg.llx, arg.lly, arg.urx, arg.ury)
-				END;
-
-				rdata := SELF.data; adata := arg.data;
-				bot := MAX(SELF.lly, arg.lly);
-				top := MIN(SELF.ury, arg.ury);
-				SELF.FindLower(bot, rn);
-				arg.FindLower(bot, an);
-				Decode(rdata[rn], ru, rv, rdir);
-				Decode(adata[an], au, av, adir);
-				rs := rn; as := an;
-				REPEAT
-					ry := rv; ay := av;
-
-					(* compare slices *)
-					WHILE (rv = ry) OR (av = ay) DO
-						IF (rv = ry) & (av = ay) & (rdir = Exit) & (adir = Exit) THEN
-							RETURN TRUE
-						END;
-						IF (av > ay) OR (rv = ry) & (ru <= au) THEN
-							INC(rn);
-							Decode(rdata[rn], ru, rv, rdir)
-						ELSE
-							INC(an);
-							Decode(adata[an], au, av, adir)
-						END
-					END;
-
-					(* reset to begin of line if not on same line *)
-					IF rv > av THEN
-						rn := rs; as := an;
-						Decode(rdata[rn], ru, rv, rdir)
-					ELSIF av > rv THEN
-						an := as; rs := rn;
-						Decode(adata[an], au, av, adir)
-					ELSE
-						rs := rn; as := an
-					END
-				UNTIL (rv = top) OR (av = top);
-
-				RETURN FALSE	(* no pair of spans intersected *)
-			END RegionOverlaps;
-
-			(** enumerate region within rectangle **)
-			PROCEDURE Enumerate* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
-			BEGIN
-				IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
-					ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
-					IF ~RectEmpty(llx, lly, urx, ury) THEN
-						IF SELF.IsRect() THEN
-							enum(llx, lly, urx, ury, edata)
-						ELSE
-							SELF.Enum(llx, lly, urx, ury, enum, edata, Enter)
-						END
-					END
-				END
-			END Enumerate;
-
-			(** enumerate parts of rectangle not within region **)
-			PROCEDURE EnumerateInv* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
-			BEGIN
-				IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
-					IF SELF.IsRect() & RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
-						IF lly < SELF.lly THEN enum(llx, lly, urx, SELF.lly, edata) END;
-						IF llx < SELF.llx THEN enum(llx, SELF.lly, SELF.llx, SELF.ury, edata) END;
-						IF urx > SELF.urx THEN enum(SELF.urx, SELF.lly, urx, SELF.ury, edata) END;
-						IF ury > SELF.ury THEN enum(llx, SELF.ury, urx, ury, edata) END
-					ELSE
-						SELF.Enum(llx, lly, urx, ury, enum, edata, Exit)
-					END
-				ELSE
-					enum(llx, lly, urx, ury, edata)
-				END
-			END EnumerateInv;
-	
-		(*--- Auxiliary Routines For Managing Regions ---*)			
-
-			(* append point to region data *)
-			PROCEDURE Append (u, v, dir: INTEGER);
-				VAR size: LONGINT; data: RegionData;
-			BEGIN
-				IF SELF.data = NIL THEN
-					NEW(SELF.data, BlockSize)
-				ELSIF SELF.points >= LEN(SELF.data^) THEN	(* grow data array *)
-					size := LEN(SELF.data^) + BlockSize;
-					NEW(data, size);
-					CopyPoints(SELF.data, data, SELF.points);
-					SELF.data := data
-				END;
-				Encode(SELF.data[SELF.points], u, v, dir);
-				INC(SELF.points)
-			END Append;
-
-			(* copy region data *)
-			PROCEDURE CopyData (dst: Region);
-				VAR size: LONGINT;
-			BEGIN
-				IF SELF.points > 0 THEN
-					IF (dst.data = NIL) OR (LEN(dst.data^) < SELF.points) THEN
-						size := SELF.points + (-SELF.points) MOD BlockSize;	(* round up to multiple of BlockSize *)
-						NEW(dst.data, size)
-					END;
-					CopyPoints(SELF.data, dst.data, SELF.points)
-				END;
-				dst.points := SELF.points;
-				dst.llx := SELF.llx; dst.lly := SELF.lly;
-				dst.urx := SELF.urx; dst.ury := SELF.ury;
-				dst.valid := SELF.valid
-			END CopyData;
-
-			(* re_calculate bounding box of (valid!) region *)
-			PROCEDURE CalcRect ();
-				VAR data: RegionData; n: LONGINT; u, v, dir, x: INTEGER;
-			BEGIN
-				ASSERT(SELF.valid);
-				IF SELF.points > 0 THEN
-					data := SELF.data;
-					n := FirstSlice;
-					Decode(data[n], u, v, dir);
-					SELF.llx := u; SELF.urx := u; SELF.lly := v;
-					REPEAT
-						SELF.ury := v; x := u;
-						REPEAT
-							IF (dir = Enter) & (u < SELF.llx) THEN
-								SELF.llx := u; x := u
-							ELSIF (dir = Exit) & (u > SELF.urx) & (u > x) THEN	(* last term excludes filler spans *)
-								SELF.urx := u
-							END;
-							INC(n);
-							Decode(data[n], u, v, dir)
-						UNTIL v > SELF.ury;
-					UNTIL v = Top
-				END
-			END CalcRect;
-
-			(* eliminate duplicate slices *)
-			PROCEDURE Compact (src: RegionData);
-				VAR rslice, dslice, sn, rn, dn: LONGINT; dst: RegionData; su, sv, sdir, ru, rv, rdir, sy, ry: INTEGER;
-			BEGIN
-				rslice := 0;	(* start of current reference slice is the bottom sentinel slice *)
-				dslice := FirstSlice;	(* start of current destination slice *)
-				sn := FirstSlice;	(* current reading position *)
-				dst := SELF.data;
-				Decode(src[sn], su, sv, sdir);
-
-				REPEAT
-					(* compare next source slice to current reference slice *)
-					rn := rslice; dn := dslice;
-					Decode(dst[rn], ru, rv, rdir);
-					sy := sv; ry := rv;
-					WHILE (sv = sy) & (rv = ry) & (su = ru) & (sdir = rdir) DO	(* copy while slices are equal *)
-						dst[dn] := src[sn];
-						INC(dn); INC(sn); INC(rn);
-						Decode(src[sn], su, sv, sdir);
-						Decode(dst[rn], ru, rv, rdir)
-					END;
-
-					IF (sv = sy) OR (rv = ry) THEN	(* slices are different => copy rest of source slice to destination *)
-						WHILE sv = sy DO
-							dst[dn] := src[sn];
-							INC(dn); INC(sn);
-							Decode(src[sn], su, sv, sdir)
-						END;
-
-						(* the slice just written becomes the new reference slice *)
-						rslice := dslice;
-						dslice := dn
-					END
-				UNTIL sv = Top;
-
-				IF dn = 6 THEN	(* region contains only one rectangle *)
-					Decode(dst[FirstSlice], SELF.llx, SELF.lly, rdir);
-					Decode(dst[FirstSlice + 1], SELF.urx, SELF.lly, rdir);
-					Decode(dst[FirstSlice + 2], ru, SELF.ury, rdir);
-					SELF.points := 0
-				ELSE
-					Encode(dst[dn], UBound, Top, Exit);
-					SELF.points := dn + 1
-				END
-			END Compact;
-
-			(* merge two runs of data points *)
-			PROCEDURE Merge (split: LONGINT);
-				VAR data: RegionData; n, N, m, M, p, tmp: LONGINT; nu, nv, ndir, mu, mv, mdir, sum, u, v, inc, nsum: INTEGER;
-					Data: RegionData;	(* temporary region data for merging *)
-					DataSize: LONGINT;	(* number of points allocated for Data *)				
-			BEGIN{EXCLUSIVE}
-				data := SELF.data;
-				n := 0; N := split;
-				Decode(data[n], nu, nv, ndir);
-				m := split; M := SELF.points;
-				Decode(data[m], mu, mv, mdir);
-				p := 0;
-				SELF.Append(UBound, Top, Exit);	(* sentinel for upper part *)
-
-				IF DataSize <= M THEN	(* reallocate temporary buffer *)
-					DataSize := M - M MOD BlockSize + BlockSize;
-					NEW(Data, DataSize)
-				END;
-
-				WHILE (n < N) & (m < M) DO
-					tmp := p;
-					v := MIN(nv, mv);
-
-					(* eliminate overlapping spans before copying them *)
-					sum := 0;
-					REPEAT
-						(* get next point *)
-						IF (nv < mv) OR (nv = mv) & (nu <= mu) THEN
-							u := nu; inc := ndir;
-							INC(n);
-							Decode(data[n], nu, nv, ndir)
-						ELSE
-							u := mu; inc := mdir;
-							INC(m);
-							Decode(data[m], mu, mv, mdir)
-						END;
-
-						(* accumulate directions of coincident points *)
-						WHILE (nv = v) & (nu = u) DO
-							INC(inc, ndir); INC(n);
-							Decode(data[n], nu, nv, ndir)
-						END;
-						WHILE (mv = v) & (mu = u) DO
-							INC(inc, mdir); INC(m);
-							Decode(data[m], mu, mv, mdir)
-						END;
-
-						IF inc # 0 THEN	(* append point to merged data *)
-							nsum := sum + inc;
-							IF SELF.mode = Winding THEN
-								IF (sum <= 0) & (nsum > 0) THEN
-									Encode(Data[p], u, v, Enter); INC(p)
-								ELSIF (sum > 0) & (nsum <= 0) THEN
-									Encode(Data[p], u, v, Exit); INC(p)
-								END
-							ELSIF (SELF.mode = EvenOdd) & ((sum > 0) & ODD(sum) # (nsum > 0) & ODD(nsum)) THEN
-								IF ODD(sum) THEN
-									Encode(Data[p], u, v, Exit)
-								ELSE
-									Encode(Data[p], u, v, Enter)
-								END;
-								INC(p)
-							END;
-							sum := nsum
-						END
-					UNTIL (nv > v) & (mv > v);
-
-					IF p = tmp THEN	(* line is empty => append filler slice *)
-						Encode(Data[p], UBound, v, Enter); INC(p);
-						Encode(Data[p], UBound, v, Exit); INC(p)
-					END
-				END;
-
-				(* copy remaining points *)
-				WHILE n < N DO
-					Data[p] := data[n];
-					INC(p); INC(n)
-				END;
-				WHILE m < M DO
-					Data[p] := data[m];
-					INC(p); INC(m)
-				END;
-
-				(* copy merged data back and eliminate duplicate scanlines *)
-				SELF.Compact(Data)
-			END Merge;
-
-			(* bring region data into consistent state *)
-			PROCEDURE Validate ();
-				VAR data: RegionData; points, rn, wn, tmp: LONGINT; u, v, dir, y, sum, x, inc: INTEGER;
-			BEGIN
-				IF ~SELF.valid THEN
-					data := SELF.data;
-					SafeQuickSort(data^,0,SELF.points-1);
-					points := SELF.points;
-					rn := FirstSlice; wn := FirstSlice;	(* read and write position *)
-					Decode(data[rn], u, v, dir);
-
-					REPEAT
-						tmp := wn;
-						y := v;
-						sum := 0;
-						REPEAT
-							(* accumulate directions of coincident points *)
-							x := u; inc := 0;
-							REPEAT
-								INC(inc, dir); INC(rn);
-								Decode(data[rn], u, v, dir)
-							UNTIL (v > y) OR (u > x);
-
-							IF x < UBound THEN
-								IF SELF.mode = Winding THEN
-									IF sum = 0 THEN
-										Encode(data[wn], x, y, Enter); INC(wn);
-										INC(x)	(* prevent dropouts *)
-									END;
-									INC(sum, inc);
-									IF sum = 0 THEN
-										Encode(data[wn], x, y, Exit); INC(wn)
-									END
-								ELSIF SELF.mode = EvenOdd THEN
-									IF ~ODD(sum) THEN
-										Encode(data[wn], x, y, Enter); INC(wn);
-										INC(x)	(* prevent dropouts *)
-									END;
-									INC(sum, inc);
-									IF ~ODD(sum) THEN
-										Encode(data[wn], x, y, Exit); INC(wn)
-									END
-								END
-							END
-						UNTIL v > y;
-
-						IF wn = tmp THEN	(* insert filler span if all slices have been eliminated *)
-							Encode(data[wn], UBound, y, Enter); INC(wn);
-							Encode(data[wn], UBound, y, Exit); INC(wn)
-						ELSIF v > y + 1 THEN	(* add filler slice for disconnected regions *)
-							INC(y);
-							SELF.Append(UBound, y, Enter);
-							SELF.Append(UBound, y, Exit)
-						END
-					UNTIL v = Top;
-
-					Encode(data[wn], UBound, Top, Exit); INC(wn);
-					IF SELF.points > points THEN	(* added filler slices => must merge *)
-						IF wn < points THEN	(* some points have been discarded => move filler slices *)
-							rn := points; points := wn;
-							REPEAT
-								IF (wn < LEN(data)) & (rn < LEN(SELF.data)) THEN
-									data[wn] := SELF.data[rn];	(* data may have been reallocated! *)
-								END;
-								INC(wn); INC(rn)
-							UNTIL rn = SELF.points;
-							SELF.data := data;
-							SELF.points := wn
-						END;
-						SELF.Merge(points)
-					ELSE	(* points are still sorted *)
-						SELF.points := wn;
-						SELF.Compact(SELF.data)
-					END;
-					SELF.valid := TRUE
-				END
-			END Validate;
-
-			(* find first point on line y or higher *)
-			PROCEDURE FindUpper (y: INTEGER; VAR n: LONGINT);
-				VAR item, i, j, m: LONGINT;
-			BEGIN
-				item := ASH(LONG(y), 16);	(* leftmost possible point on line y *)
-				i := 0; j := SELF.points;
-				WHILE i + 1 < j DO
-					m := (i + j) DIV 2;
-					IF SELF.data[m] < item THEN
-						i := m
-					ELSE
-						j := m
-					END
-				END;
-				n := j
-			END FindUpper;
-
-			(* find first point on line y or lower *)
-			PROCEDURE FindLower (y: INTEGER; VAR n: LONGINT);
-				VAR v: INTEGER;
-			BEGIN
-				SELF.FindUpper(y, n);
-				v := INTEGER(ASH(SELF.data[n], -16));
-				IF v > y THEN	(* => find leftmost point on lower slice *)
-					DEC(n);
-					y := INTEGER(ASH(SELF.data[n], -16));
-					REPEAT
-						DEC(n)
-					UNTIL (n < 0) OR (ASH(SELF.data[n], -16) < y);
-					INC(n)
-				END
-			END FindLower;
-
-			(* enumerate (inverted) region within rectangle *)
-			PROCEDURE Enum (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData; enter: INTEGER);
-				VAR data: RegionData; n, lo, hi: LONGINT; u, v, dir, y, top, x: INTEGER;
-			BEGIN
-				SELF.Validate();
-				ClipRect(llx, lly, urx, ury, LBound, LBound, UBound, UBound);
-				data := SELF.data;
-				SELF.FindLower(lly, n);
-				Decode(data[n], u, v, dir);
-				y := lly;
-
-				REPEAT
-					(* calculate height of slice *)
-					lo := n;
-					REPEAT
-						INC(n);
-						IF u < llx THEN
-							lo := n
-						END;
-						Decode(data[n], u, v, dir)
-					UNTIL v > y;
-					hi := n;
-					top := MIN(v, ury);
-
-					(* enumerate spans of current slice *)
-					n := lo;
-					Decode(data[n], u, v, dir);
-					x := llx;
-					WHILE (v <= y) & ((u < urx) OR (dir # enter)) DO
-						IF u > x THEN
-							IF dir = enter THEN
-								x := u
-							ELSE
-								enum(x, y, MIN(u, urx), top, edata)
-							END
-						END;
-						INC(n);
-						Decode(data[n], u, v, dir)
-					END;
-
-					IF n < hi THEN
-						n := hi;
-						Decode(data[n], u, v, dir)
-					END;
-					y := v
-				UNTIL v >= ury
-			END Enum;
-
-			(* create data points for rectangular region *)
-			PROCEDURE MakeData ();
-			BEGIN
-				IF SELF.points = 0 THEN
-					SELF.Append(UBound, Bottom, Enter);
-					SELF.Append(UBound, Bottom, Exit);
-					IF (SELF.llx <= SELF.urx) & (SELF.lly <= SELF.ury) THEN
-						SELF.Append(SELF.llx, SELF.lly, Enter);
-						SELF.Append(SELF.urx, SELF.lly, Exit);
-						SELF.Append(UBound, SELF.ury, Enter);
-						SELF.Append(UBound, SELF.ury, Exit)
-					END;
-					SELF.Append(UBound, Top, Enter)
-				END
-			END MakeData;
-
-		END Region;
-
-		(** region enumeration **)
-		EnumData* = RECORD END;
-		Enumerator* = PROCEDURE (llx, lly, urx, ury: INTEGER; VAR edata: EnumData);
-
-	(**--- Rectangles ---**)
-
-	(** make rectangle large enough to include a point **)
-	PROCEDURE IncludePoint* (VAR llx, lly, urx, ury: INTEGER; x, y: INTEGER);
-	BEGIN
-		IF x < llx THEN llx := x END;
-		IF x > urx THEN urx := x END;
-		IF y < lly THEN lly := y END;
-		IF y > ury THEN ury := y END
-	END IncludePoint;
-
-	(** make rectangle large enough to include other rectangle **)
-	PROCEDURE IncludeRect* (VAR llx, lly, urx, ury: INTEGER; illx, illy, iurx, iury: INTEGER);
-	BEGIN
-		IF illx < llx THEN llx := illx END;
-		IF iurx > urx THEN urx := iurx END;
-		IF illy < lly THEN lly := illy END;
-		IF iury > ury THEN ury := iury END
-	END IncludeRect;
-
-	(** shrink rectangle to area within other rectangle **)
-	PROCEDURE ClipRect* (VAR llx, lly, urx, ury: INTEGER; cllx, clly, curx, cury: INTEGER);
-	BEGIN
-		IF cllx > llx THEN llx := cllx END;
-		IF curx < urx THEN urx := curx END;
-		IF clly > lly THEN lly := clly END;
-		IF cury < ury THEN ury := cury END
-	END ClipRect;
-
-	(** return whether rectangle is empty **)
-	PROCEDURE RectEmpty* (llx, lly, urx, ury: INTEGER): BOOLEAN;
-	BEGIN
-		RETURN (llx >= urx) OR (lly >= ury)
-	END RectEmpty;
-
-	(** return whether (non_empty) rectangle is completely inside other rectangle **)
-	PROCEDURE RectInRect* (llx, lly, urx, ury, illx, illy, iurx, iury: INTEGER): BOOLEAN;
-	BEGIN
-		RETURN (llx >= illx) & (urx <= iurx) & (lly >= illy) & (ury <= iury)
-	END RectInRect;
-
-	(** return whether (non_empty) rectangle intersects other rectangle **)
-	PROCEDURE RectsIntersect* (llx, lly, urx, ury, illx, illy, iurx, iury: INTEGER): BOOLEAN;
-	BEGIN
-		RETURN (llx < iurx) & (urx > illx) & (lly < iury) & (ury > illy)
-	END RectsIntersect;
-
-	(** return whether rectangle contains point **)
-	PROCEDURE PointInRect* (x, y: INTEGER; llx, lly, urx, ury: INTEGER): BOOLEAN;
-	BEGIN
-		RETURN (x >= llx) & (x < urx) & (y >= lly) & (y < ury)
-	END PointInRect;
-
-
-	(*--- Auxiliary Routines For Managing Regions ---*)
-
-	(* encode point coordinates and curve direction into a LONGINT *)
-	PROCEDURE Encode (VAR item: LONGINT; u, v, dir: LONGINT);
-	BEGIN
-		item := ASH(v, 16) + ASH((u + 4000H) MOD 8000H, 1) + ASH(1 + dir, -1)
-	END Encode;
-
-	(* restore point coordinates and curve direction from an encoded LONGINT *)
-	PROCEDURE Decode (item: LONGINT; VAR u, v, dir: INTEGER);
-	BEGIN
-		v := INTEGER(ASH(item, -16));
-		u := INTEGER(ASH(item, -1) MOD 8000H - 4000H);
-		dir := INTEGER(ASH(item MOD 2, 1) - 1)
-	END Decode;
-
-	(* copy points between region data blocks *)
-	PROCEDURE CopyPoints (src, dst: RegionData; points: LONGINT);
-		VAR i: LONGINT;
-	BEGIN
-		i := 0;
-		WHILE (i < points) & (i < LEN(dst)) & (i < LEN(src)) DO
-			dst[i] := src[i];
-			INC(i)
-		END
-	END CopyPoints;
-
-	(* quick sort with limited recursion guarantee *)
-	PROCEDURE SafeQuickSort(VAR data: ARRAY OF LONGINT; lo, hi: LONGINT);
-	CONST limit = 8;
-	VAR i, x, j, t, shortLo, shortHi, longLo, longHi: LONGINT;
-	BEGIN
-		WHILE (hi > lo) DO
-			IF hi - lo < limit THEN	(* use straight insertion for less than limit entries... *)
-				i := lo + 1;
-				WHILE i <= hi DO
-					x := data[i];
-					j := i;
-					WHILE (j > lo) & (x < data[j - 1]) DO
-						data[j] := data[j - 1];
-						DEC(j)
-					END;
-					data[j] := x;
-					INC(i)
-				END;
-				hi := lo; (* termination! *)
-			ELSE
-				i := lo; j := hi;
-				x := data[(lo + hi) DIV 2];
-				REPEAT
-					WHILE data[i] < x DO INC(i) END;
-					WHILE data[j] > x DO DEC(j) END;
-					IF i <= j THEN
-						t := data[i]; data[i] := data[j]; data[j] := t;
-						INC(i); DEC(j)
-					END
-				UNTIL i > j;
-				IF (j - lo) < (hi - i) THEN (* generalized interval [lo,j] is smaller than interval [hi, i] *)
-					shortLo := lo; shortHi := j;
-					longLo := i; longHi := hi;
-				ELSE
-					longLo := lo; longHi := j;
-					shortLo := i; shortHi := hi;
-				END;
-				IF (shortLo < shortHi) THEN 
-					SafeQuickSort(data, shortLo, shortHi);
-				END;
-				(* now: tail recursion, call of: SortRange(data, longLo, longHi) *)
-				lo := longLo; hi := longHi;
-			END
-		END;
-	END SafeQuickSort;
-
-	(**--- Region Queries ---**)
-
-	(** return whether region is empty **)
-	PROCEDURE Empty* (reg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.Empty();
-	END Empty;
-
-	(** return whether (non_empty) region is rectangular **)
-	PROCEDURE IsRect* (reg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.IsRect();
-	END IsRect;
-
-	(** return whether point is inside (non_empty) region **)
-	PROCEDURE PointInside* (x, y: INTEGER; reg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.PointInside(x, y);
-	END PointInside;
-
-	(** return whether (non_empty) rectangle is completely inside (non_empty) region **)
-	PROCEDURE RectInside* (llx, lly, urx, ury: INTEGER; reg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.RectInside(llx, lly, urx, ury);
-	END RectInside;
-
-	(** return whether (non_empty) rectangle overlaps (non_empty) region **)
-	PROCEDURE RectOverlaps* (llx, lly, urx, ury: INTEGER; reg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.RectOverlaps(llx, lly, urx, ury);
-	END RectOverlaps;
-
-	(** return whether region is completely within another region **)
-	PROCEDURE RegionInside* (inner, outer: Region): BOOLEAN;
-	BEGIN
-		RETURN inner.RegionInside(outer);
-	END RegionInside;
-
-	(** return whether two regions intersect each other **)
-	PROCEDURE RegionOverlaps* (reg, arg: Region): BOOLEAN;
-	BEGIN
-		RETURN reg.RegionOverlaps(arg);
-	END RegionOverlaps;
-
-	(** enumerate region within rectangle **)
-	PROCEDURE Enumerate* (reg: Region; llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
-	BEGIN
-		reg.Enumerate(llx, lly, urx, ury, enum, edata);
-	END Enumerate;
-
-	(** enumerate parts of rectangle not within region **)
-	PROCEDURE EnumerateInv* (reg: Region; llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
-	BEGIN
-		reg.EnumerateInv(llx, lly, urx, ury, enum, edata);
-	END EnumerateInv;
-
-	(**--- Region Construction ---**)
-
-	(** make region empty **)
-	PROCEDURE Clear* (reg: Region);
-	BEGIN
-		reg.Clear();
-	END Clear;
-
-	(** set region mode **)
-	PROCEDURE SetMode* (reg: Region; mode: INTEGER);
-	BEGIN
-		reg.SetMode(mode);
-	END SetMode;
-
-	(** initialize region **)
-	PROCEDURE Init* (reg: Region; mode: INTEGER);
-	BEGIN
-		reg.Init(mode);
-	END Init;
-
-	(** make region rectangular **)
-	PROCEDURE SetToRect* (reg: Region; llx, lly, urx, ury: INTEGER);
-	BEGIN
-		reg.SetToRect(llx, lly, urx, ury);
-	END SetToRect;
-
-	(** shift region **)
-	PROCEDURE Shift* (reg: Region; dx, dy: INTEGER);
-	BEGIN
-		reg.Shift(dx, dy);
-	END Shift;
-
-	(** copy region **)
-	PROCEDURE Copy* (from, to: Region);
-	BEGIN
-		from.Copy(to);
-	END Copy;
-
-	(** add second region to first **)
-	PROCEDURE Add* (reg, arg: Region);
-	BEGIN
-		reg.Add(arg);
-	END Add;
-
-	(** add rectangle to region **)
-	PROCEDURE AddRect* (reg: Region; llx, lly, urx, ury: INTEGER);
-	BEGIN
-		reg.AddRect(llx, lly, urx, ury);
-	END AddRect;
-
-	(** subtract second region from first **)
-	PROCEDURE Subtract* (reg, arg: Region);
-	BEGIN
-		reg.Subtract(arg);
-	END Subtract;
-
-	(** subtract rectangle from region **)
-	PROCEDURE SubtractRect* (reg: Region; llx, lly, urx, ury: INTEGER);
-	BEGIN
-		reg.SubtractRect(llx, lly, urx, ury);
-	END SubtractRect;
-
-	(** intersect first region with second region **)
-	PROCEDURE Intersect* (reg, arg: Region);
-	BEGIN
-		reg.Intersect(arg);
-	END Intersect;
-
-	(** intersect region with rectangle **)
-	PROCEDURE IntersectRect* (reg: Region; llx, lly, urx, ury: INTEGER);
-	BEGIN
-		reg.IntersectRect(llx, lly, urx, ury);
-	END IntersectRect;
-
-	(** invert region **)
-	PROCEDURE Invert* (reg: Region);
-	BEGIN
-		reg.Invert();
-	END Invert;
-
-	(** add a scanline intersection to a region **)
-	PROCEDURE AddPoint* (reg: Region; x, y, dy: INTEGER);
-	BEGIN
-		reg.AddPoint(x, y, dy);
-	END AddPoint;
-
-END GfxRegions.