AGfx.Mod 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595
  1. (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  2. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  3. MODULE Gfx; (** portable *) (* eos *)
  4. (** AUTHOR "eos"; PURPOSE "High-level, device independent 2D-graphics"; *)
  5. (*
  6. 11.2.98 - changed behaviour of GetOutline if current line width is zero: calculates dashed path instead of outline (eos)
  7. 11.2.98 - eliminated offset parameter from subpath begin since it can be simulated by modifying the dash phase
  8. 16.2.98 - DrawPath now accepts current path
  9. 17.2.98 - ...but only if Record is not included in mode
  10. 17.2.98 - added RenderPath
  11. 19.2.98 - simplified cap and join styles to procedures
  12. 19.2.98 - eliminated clip path (was not correct, anyway), introduced GetClipRect instead
  13. 6.3.98 - fixed bug in GetDashOutline (started last dash twice even if fully drawn)
  14. 18.9.98 - several changes: renaming, added stroke pattern and rect/ellipse methods, text is now part of path model,
  15. standard colors
  16. 9.12.98 - adaptation to new GfxMaps
  17. 10.3.99 - separate dash pattern into on/off arrays
  18. 13.5.99 - remodeled cap and join styles
  19. 13.5.99 - eliminated 'erase'
  20. 2.6.99 - bugfix in GetStrokeOutline and GetDashOutline (forgot to consume GfxPaths.Exit after subpath)
  21. 8.6.99 - made GetPolyOutline automatically close open subpaths
  22. 25.8.99 - use GfxImages instead of GfxMaps
  23. 6.10.99 - save and restore graphics state, MoveTo and Close
  24. 13.02.2000 - added ClipArea, replaced SaveClip/RestoreClip by GetClip/SetClip, fine-grained SaveState semantics
  25. 26.02.2000 - allow font change within path
  26. 27.02.2000 - added DrawArc
  27. 29.03.2000 - made default flatness 1/2
  28. 25.05.2000 - no longer imports Texts and Oberon
  29. 12.06.2001 - made tmpPath and dashPath ctxt fields to allow concurrency
  30. *)
  31. IMPORT
  32. Math, GfxMatrix, GfxImages, GfxPaths, GfxFonts;
  33. CONST
  34. Version* = "Gfx 2.0/eos 25.05.2000";
  35. Record* = 0; Fill* = 1; Clip* = 2; Stroke* = 3; EvenOdd* = 4; (** drawing mode elements **)
  36. InPath* = 5; InSubpath* = 6; (** context state **)
  37. MaxDashPatSize* = 8; (** maximal number of dash entries **)
  38. NoJoin* = 0; MiterJoin* = 1; BevelJoin* = 2; RoundJoin* = 3; (** join styles **)
  39. NoCap* = 0; ButtCap* = 1; SquareCap* = 2; RoundCap* = 3; (** cap styles **)
  40. (** state elements **)
  41. fillColPat* = 0; strokeColPat* = 1; lineWidth* = 2; dashPat* = 3; capStyle* = 4; joinStyle* = 5; styleLimit* = 6;
  42. flatness* = 7; font* = 8; ctm* = 9; clip* = 10;
  43. strokeAttr* = {strokeColPat..styleLimit};
  44. attr* = {fillColPat..font}; all* = attr + {ctm, clip};
  45. TYPE
  46. (** color type **)
  47. Color* = RECORD
  48. r*, g*, b*, a*: INTEGER;
  49. END;
  50. (** fill patterns **)
  51. Pattern* = POINTER TO PatternDesc;
  52. PatternDesc* = RECORD
  53. img*: GfxImages.Image; (** replicated image map **)
  54. px*, py*: REAL; (** pinpoint coordinates **)
  55. END;
  56. (** line join and cap styles **)
  57. JoinStyle* = SHORTINT;
  58. CapStyle* = SHORTINT;
  59. (** abstract clip areas **)
  60. ClipArea* = POINTER TO ClipAreaDesc;
  61. ClipAreaDesc* = RECORD END;
  62. (** graphics context **)
  63. Context* = OBJECT { ABSTRACT }
  64. VAR
  65. mode*: SET; (** current drawing mode **)
  66. path*: GfxPaths.Path; (** current path in device coordinates (updated only if mode contains the 'Record' flag) **)
  67. cpx*, cpy*: REAL; (** current point in user coordinates **)
  68. ctm*: GfxMatrix.Matrix; (** current transformation matrix **)
  69. cam*: GfxMatrix.Matrix; (** current attribute matrix (frozen ctm while inside path) **)
  70. strokeCol*, fillCol*: Color; (** current stroke and fill color **)
  71. strokePat*, fillPat*: Pattern; (** current stroke and fill pattern **)
  72. lineWidth*: REAL; (** current line width **)
  73. dashPatOn*, dashPatOff*: ARRAY MaxDashPatSize OF REAL; (** line dash array **)
  74. dashPatLen*: LONGINT; (** number of valid elements in dash arrays **)
  75. dashPhase*: REAL; (** offset for first dash **)
  76. dashPeriod*: REAL; (** sum of dash element lengths **)
  77. capStyle*: CapStyle; (** line cap style **)
  78. joinStyle*: JoinStyle; (** line join style **)
  79. styleLimit*: REAL; (** determines area that may be rendered to by styles **)
  80. flatness*: REAL; (** current flatness tolerance (in device coordinates) **)
  81. font*: GfxFonts.Font; (** current font **)
  82. dashPath: GfxPaths.Path; (* path for temporarily storing dashes *)
  83. tmpPath: GfxPaths.Path;
  84. (** initialize context values to defaults **)
  85. PROCEDURE InitContext* ();
  86. BEGIN
  87. SELF.ctm := GfxMatrix.Identity; SELF.cam := SELF.ctm;
  88. SELF.strokeCol := Black; SELF.strokePat := NIL;
  89. SELF.fillCol := Black; SELF.fillPat := NIL;
  90. SELF.lineWidth := 1;
  91. SELF.dashPatLen := 0; SELF.dashPhase := 0; SELF.dashPeriod := 0;
  92. SELF.capStyle := DefaultCap; SELF.joinStyle := DefaultJoin; SELF.styleLimit := 5;
  93. SELF.mode := {};
  94. SELF.path := NIL;
  95. SELF.cpx := 0; SELF.cpy := 0;
  96. SELF.flatness := 0.5;
  97. SELF.font := GfxFonts.Default;
  98. IF SELF.tmpPath = NIL THEN
  99. NEW(SELF.tmpPath);
  100. END;
  101. IF SELF.dashPath = NIL THEN
  102. NEW(SELF.dashPath)
  103. END;
  104. END InitContext;
  105. (** reset context to default values **)
  106. PROCEDURE Reset*();
  107. BEGIN
  108. SELF.InitContext();
  109. SELF.ResetClip();
  110. SELF.ResetCTM()
  111. END Reset;
  112. (**--- Coordinate System ---**)
  113. (** reset current transformation matrix **)
  114. PROCEDURE{ABSTRACT} ResetCTM*(); END ResetCTM;
  115. (** set current transformation matrix **)
  116. PROCEDURE SetCTM*(VAR mat: GfxMatrix.Matrix);
  117. BEGIN
  118. SELF.ctm := mat;
  119. END SetCTM;
  120. (** translate coordinate system **)
  121. PROCEDURE Translate*(dx, dy: REAL);
  122. BEGIN
  123. GfxMatrix.Translate(SELF.ctm, dx, dy, SELF.ctm);
  124. END Translate;
  125. (** scale coordinate system at origin **)
  126. PROCEDURE Scale*(sx, sy: REAL);
  127. BEGIN
  128. GfxMatrix.Scale(SELF.ctm, sx, sy, SELF.ctm);
  129. END Scale;
  130. (** scale coordinate system at specified point **)
  131. PROCEDURE ScaleAt* (sx, sy, x, y: REAL);
  132. BEGIN
  133. SELF.Translate(x, y);
  134. SELF.Scale(sx, sy);
  135. SELF.Translate(-x, -y)
  136. END ScaleAt;
  137. (** rotate coordinate system at origin **)
  138. PROCEDURE Rotate*(sin, cos: REAL);
  139. BEGIN
  140. GfxMatrix.Rotate(SELF.ctm, sin, cos, SELF.ctm);
  141. END Rotate;
  142. (** rotate coordinate system at specified point **)
  143. PROCEDURE RotateAt* (sin, cos, x, y: REAL);
  144. BEGIN
  145. SELF.Translate(x, y);
  146. SELF.Rotate(sin, cos);
  147. SELF.Translate(-x, -y)
  148. END RotateAt;
  149. (** concat transformation matrix to CTM **)
  150. PROCEDURE Concat*(VAR mat: GfxMatrix.Matrix);
  151. BEGIN
  152. GfxMatrix.Concat(mat, SELF.ctm, SELF.ctm);
  153. END Concat;
  154. (**--- Clipping ---**)
  155. (** reset clip path **)
  156. PROCEDURE{ABSTRACT} ResetClip*(); END ResetClip;
  157. (** get bounding box of clipping path in user coordinates **)
  158. PROCEDURE{ABSTRACT} GetClipRect*(VAR llx, lly, urx, ury: REAL); END GetClipRect;
  159. (** get current clipping area **)
  160. PROCEDURE{ABSTRACT} GetClip*(): ClipArea; END GetClip;
  161. (** restore saved clipping path **)
  162. PROCEDURE{ABSTRACT} SetClip*(clip: ClipArea); END SetClip;
  163. (**--- Graphics State ---**)
  164. (** set stroke color **)
  165. PROCEDURE SetStrokeColor*(color: Color);
  166. BEGIN
  167. ASSERT(~(InPath IN SELF.mode), 100);
  168. SELF.strokeCol := color;
  169. END SetStrokeColor;
  170. (** set stroke pattern (NIL = solid) **)
  171. PROCEDURE SetStrokePattern*(pat: Pattern);
  172. BEGIN
  173. ASSERT(~(InPath IN SELF.mode), 100);
  174. SELF.strokePat := pat
  175. END SetStrokePattern;
  176. (** set fill color **)
  177. PROCEDURE SetFillColor*(color: Color);
  178. BEGIN
  179. ASSERT(~(InPath IN SELF.mode), 100);
  180. SELF.fillCol := color;
  181. END SetFillColor;
  182. (** set fill pattern (NIL = solid) **)
  183. PROCEDURE SetFillPattern*(pat: Pattern);
  184. BEGIN
  185. ASSERT(~(InPath IN SELF.mode), 100);
  186. SELF.fillPat := pat
  187. END SetFillPattern;
  188. (** set line width **)
  189. PROCEDURE SetLineWidth*(width: REAL);
  190. BEGIN
  191. ASSERT(~(InPath IN SELF.mode), 100);
  192. ASSERT(width >= 0.0, 101);
  193. SELF.lineWidth := width
  194. END SetLineWidth;
  195. (** set dash pattern **)
  196. PROCEDURE SetDashPattern*(VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
  197. BEGIN
  198. ASSERT(~(InPath IN SELF.mode), 100);
  199. ASSERT((len <= LEN(on)) & (len <= LEN(off)), 101);
  200. SELF.SetDashArray(on, off, len);
  201. SELF.dashPhase := phase
  202. END SetDashPattern;
  203. (** copy values from parameter, and calculate dash period **)
  204. PROCEDURE SetDashArray* (VAR on, off: ARRAY OF REAL; len: LONGINT);
  205. BEGIN
  206. SELF.dashPatLen := len;
  207. SELF.dashPeriod := 0;
  208. IF len > 0 THEN
  209. REPEAT
  210. DEC(len);
  211. SELF.dashPatOn[len] := on[len]; SELF.dashPatOff[len] := off[len];
  212. SELF.dashPeriod := SELF.dashPeriod + on[len] + off[len]
  213. UNTIL len = 0
  214. END;
  215. ASSERT((SELF.dashPatLen = 0) OR (SELF.dashPeriod # 0), 120)
  216. END SetDashArray;
  217. (** set line cap style **)
  218. PROCEDURE SetCapStyle*(style: CapStyle);
  219. BEGIN
  220. ASSERT(~(InPath IN SELF.mode), 100);
  221. ASSERT((NoCap <= style) & (style <= RoundCap), 101);
  222. SELF.capStyle := style
  223. END SetCapStyle;
  224. (** set line join style **)
  225. PROCEDURE SetJoinStyle*(style: JoinStyle);
  226. BEGIN
  227. ASSERT(~(InPath IN SELF.mode), 100);
  228. ASSERT((NoJoin <= style) & (style <= RoundJoin), 101);
  229. SELF.joinStyle := style
  230. END SetJoinStyle;
  231. (** set style border factor **)
  232. PROCEDURE SetStyleLimit*(limit: REAL);
  233. BEGIN
  234. ASSERT(~(InPath IN SELF.mode), 100);
  235. SELF.styleLimit := limit
  236. END SetStyleLimit;
  237. (** set flatness parameter **)
  238. PROCEDURE SetFlatness*(flatness: REAL);
  239. BEGIN
  240. SELF.flatness := flatness
  241. END SetFlatness;
  242. (** set current font **)
  243. PROCEDURE SetFont*(font: GfxFonts.Font);
  244. BEGIN
  245. ASSERT(font # NIL, 100);
  246. SELF.font := font
  247. END SetFont;
  248. (** set current font using name and size **)
  249. PROCEDURE SetFontName* (fontname: ARRAY OF CHAR; size: INTEGER);
  250. VAR font: GfxFonts.Font;
  251. BEGIN
  252. font := GfxFonts.OpenSize(fontname, size);
  253. IF font = NIL THEN font := GfxFonts.Default END;
  254. SELF.SetFont(font)
  255. END SetFontName;
  256. (** calculate distance that current point would move if given string were rendered **)
  257. PROCEDURE GetStringWidth*(VAR str: ARRAY OF CHAR; VAR dx, dy: REAL);
  258. BEGIN
  259. GfxFonts.GetStringWidth(SELF.font, str, dx, dy)
  260. END GetStringWidth;
  261. (**--- Current Path ---**)
  262. (** start new path **)
  263. PROCEDURE Begin* (mode: SET);
  264. BEGIN
  265. ASSERT(~(InPath IN SELF.mode), 100);
  266. SELF.DoBegin(mode);
  267. INCL(SELF.mode, InPath)
  268. END Begin;
  269. (** exit current subpath (if open) and end current path **)
  270. PROCEDURE End* ();
  271. BEGIN
  272. ASSERT(InPath IN SELF.mode, 100);
  273. IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
  274. SELF.DoEnd();
  275. EXCL(SELF.mode, InPath)
  276. END End;
  277. (** end current subpath (if open) and begin new subpath **)
  278. PROCEDURE MoveTo* (x, y: REAL);
  279. BEGIN
  280. ASSERT(InPath IN SELF.mode, 100);
  281. IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
  282. SELF.DoEnter(x, y, 0, 0);
  283. INCL(SELF.mode, InSubpath)
  284. END MoveTo;
  285. (** start subpath at inner point **)
  286. PROCEDURE Enter* (x, y, dx, dy: REAL);
  287. BEGIN
  288. ASSERT(InPath IN SELF.mode, 100);
  289. IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
  290. SELF.DoEnter(x, y, dx, dy);
  291. INCL(SELF.mode, InSubpath)
  292. END Enter;
  293. (** end subpath at inner point **)
  294. PROCEDURE Exit* (dx, dy: REAL);
  295. BEGIN
  296. ASSERT(InSubpath IN SELF.mode, 100);
  297. SELF.DoExit(dx, dy);
  298. EXCL(SELF.mode, InSubpath)
  299. END Exit;
  300. (** close current subpath **)
  301. PROCEDURE Close* ();
  302. BEGIN
  303. ASSERT(InSubpath IN SELF.mode, 100);
  304. SELF.DoClose();
  305. EXCL(SELF.mode, InSubpath)
  306. END Close;
  307. (** append line to current path **)
  308. PROCEDURE LineTo* (x, y: REAL);
  309. BEGIN
  310. ASSERT(InSubpath IN SELF.mode, 100);
  311. SELF.DoLine(x, y)
  312. END LineTo;
  313. (** append arc to current path **)
  314. PROCEDURE ArcTo* (x, y, x0, y0, x1, y1, x2, y2: REAL);
  315. BEGIN
  316. ASSERT(InSubpath IN SELF.mode, 100);
  317. SELF.DoArc(x, y, x0, y0, x1, y1, x2, y2)
  318. END ArcTo;
  319. (** append cubic bezier to current path **)
  320. PROCEDURE BezierTo* (x, y, x1, y1, x2, y2: REAL);
  321. BEGIN
  322. ASSERT(InSubpath IN SELF.mode, 100);
  323. SELF.DoBezier(x, y, x1, y1, x2, y2)
  324. END BezierTo;
  325. (** append character outlines to current path at given point; advance current point to position after last character **)
  326. PROCEDURE ShowAt* (x, y: REAL; str: ARRAY OF CHAR);
  327. BEGIN
  328. ASSERT(InPath IN SELF.mode, 100);
  329. IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
  330. SELF.DoShow(x, y, str)
  331. END ShowAt;
  332. (** append character outlines to current path at current point; advance current point to position after last character **)
  333. PROCEDURE Show* (str: ARRAY OF CHAR);
  334. BEGIN
  335. ASSERT(InPath IN SELF.mode, 100);
  336. IF InSubpath IN SELF.mode THEN SELF.DoExit(0, 0) END;
  337. SELF.DoShow(SELF.cpx, SELF.cpy, str)
  338. END Show;
  339. (**--- Path Flattening ---**)
  340. (** replace arcs and beziers in current path by approximation using straight lines **)
  341. PROCEDURE Flatten*();
  342. BEGIN
  343. ASSERT(~(InPath IN SELF.mode), 100);
  344. SELF.GetFlattenedPath(SELF.tmpPath);
  345. SELF.tmpPath.CopyTo(SELF.path);
  346. SELF.tmpPath.Clear()
  347. END Flatten;
  348. (** store flattened current path in given path **)
  349. PROCEDURE GetFlattenedPath* (path: GfxPaths.Path);
  350. VAR data: PathData;
  351. BEGIN
  352. ASSERT(SELF.path # path, 100);
  353. path.Clear();
  354. data.path := path;
  355. SELF.path.EnumFlattened(SELF.flatness, EnumPathElem, data)
  356. END GetFlattenedPath;
  357. (**--- Cap Styles ---**)
  358. PROCEDURE EnterCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
  359. BEGIN
  360. IF SELF.capStyle = ButtCap THEN
  361. path.AddEnter(x, y, dy, -dx);
  362. path.AddLine(x + dy, y - dx)
  363. ELSIF SELF.capStyle = SquareCap THEN
  364. path.AddEnter(x - dx, y - dy, dy, -dx);
  365. path.AddLine(x - dx + dy, y - dy - dx);
  366. path.AddLine(x + dy, y - dx)
  367. ELSIF SELF.capStyle = RoundCap THEN
  368. path.AddEnter(x - dx, y - dy, dy, -dx);
  369. path.AddArc(x + dy, y - dx, x, y, x - dx, y - dy, x + dy, y - dx)
  370. ELSE
  371. path.AddEnter(x + dy, y - dx, 0, 0)
  372. END
  373. END EnterCapStyle;
  374. PROCEDURE AddCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
  375. BEGIN
  376. IF SELF.capStyle = ButtCap THEN
  377. path.AddLine(x + dy, y - dx)
  378. ELSIF SELF.capStyle = SquareCap THEN
  379. path.AddLine(x - dx - dy, y - dy + dx);
  380. path.AddLine(x - dx + dy, y - dy - dx);
  381. path.AddLine(x + dy, y - dx)
  382. ELSIF SELF.capStyle = RoundCap THEN
  383. path.AddArc(x + dy, y - dx, x, y, x - dy, y + dx, x - dx, y - dy)
  384. ELSE
  385. path.AddExit(0, 0);
  386. path.AddEnter(x + dy, y - dx, 0, 0)
  387. END
  388. END AddCapStyle;
  389. PROCEDURE ExitCapStyle* (x, y, dx, dy: REAL; path: GfxPaths.Path);
  390. BEGIN
  391. IF SELF.capStyle = ButtCap THEN
  392. path.AddLine(x, y);
  393. path.AddExit(dy, -dx)
  394. ELSIF SELF.capStyle = SquareCap THEN
  395. path.AddLine(x - dx - dy, y - dy + dx);
  396. path.AddLine(x - dx, y - dy);
  397. path.AddExit(dy, -dx)
  398. ELSIF SELF.capStyle = RoundCap THEN
  399. path.AddArc(x - dx, y - dy, x, y, x - dy, y + dx, x - dx, y - dy);
  400. path.AddExit(dy, -dx)
  401. ELSE
  402. path.AddExit(0, 0)
  403. END
  404. END ExitCapStyle;
  405. (**--- Join Styles ---**)
  406. (** return if half axis vector (in device coordinates) exceeds style limit **)
  407. PROCEDURE ExceedsLimit* (hx, hy: REAL): BOOLEAN;
  408. VAR limit: REAL;
  409. BEGIN
  410. GfxMatrix.ApplyToDist(SELF.cam, 0.5*SELF.lineWidth * SELF.styleLimit, limit);
  411. RETURN hx * hx + hy * hy > limit * limit
  412. END ExceedsLimit;
  413. PROCEDURE EnterJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  414. VAR ix, iy, t: REAL;
  415. BEGIN
  416. IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
  417. GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
  418. path.AddEnter(ix, iy, -hy, hx);
  419. path.AddLine(x + ody, y - odx)
  420. ELSIF SELF.joinStyle = MiterJoin THEN
  421. path.AddEnter(x + hx, y + hy, idx, idy);
  422. path.AddLine(x + ody, y - odx)
  423. ELSIF SELF.joinStyle = RoundJoin THEN
  424. t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
  425. path.AddEnter(x + t * hx, y + t * hy, -hy, hx);
  426. path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
  427. ELSE
  428. path.AddEnter(x + ody, y - odx, 0, 0)
  429. END
  430. END EnterJoinStyle;
  431. PROCEDURE AddJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  432. BEGIN
  433. IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
  434. path.AddLine(x + ody, y - odx)
  435. ELSIF SELF.joinStyle = MiterJoin THEN
  436. path.AddLine(x + hx, y + hy);
  437. path.AddLine(x + ody, y - odx)
  438. ELSIF SELF.joinStyle = RoundJoin THEN
  439. path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
  440. ELSE
  441. path.AddExit(0, 0);
  442. path.AddEnter(x + ody, y - odx, 0, 0)
  443. END
  444. END AddJoinStyle;
  445. PROCEDURE ExitJoinStyle* (x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  446. VAR ix, iy, t: REAL;
  447. BEGIN
  448. IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
  449. GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
  450. path.AddLine(ix, iy);
  451. path.AddExit(-hy, hx)
  452. ELSIF SELF.joinStyle = MiterJoin THEN
  453. path.AddLine(x + hx, y + hy);
  454. path.AddExit(odx, ody)
  455. ELSIF SELF.joinStyle = RoundJoin THEN
  456. t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
  457. path.AddArc(x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx);
  458. path.AddExit(-hy, hx)
  459. ELSE
  460. path.AddExit(0, 0)
  461. END
  462. END ExitJoinStyle;
  463. (**--- Path Outline ---**)
  464. (** replace current path by outline of area which would be drawn to if the path were stroked **)
  465. PROCEDURE Outline*();
  466. BEGIN
  467. ASSERT(~(InPath IN SELF.mode), 100);
  468. SELF.GetOutline(SELF.tmpPath);
  469. SELF.tmpPath.CopyTo(SELF.path);
  470. SELF.tmpPath.Clear()
  471. END Outline;
  472. PROCEDURE AddEnterJoinStyle (x, y, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  473. VAR ix, iy, t: REAL;
  474. BEGIN
  475. IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
  476. GfxPaths.IntersectLines(x, y, hx, hy, x + ody, y - odx, -hy, hx, ix, iy);
  477. path.AddLine(ix, iy); path.AddLine(x + ody, y - odx)
  478. ELSIF SELF.joinStyle = MiterJoin THEN
  479. path.AddLine(x + hx, y + hy); path.AddLine(x + ody, y - odx)
  480. ELSIF SELF.joinStyle = RoundJoin THEN
  481. t := Math.sqrt((odx * odx + ody * ody)/(hx * hx + hy * hy));
  482. path.AddLine(x + t * hx, y + t * hy);
  483. path.AddArc(x + ody, y - odx, x, y, x - odx, y - ody, x + ody, y - odx)
  484. ELSE
  485. path.AddLine(x + ody, y - odx)
  486. END
  487. END AddEnterJoinStyle;
  488. PROCEDURE AddExitJoinStyle (x, y, idx, idy, hx, hy: REAL; path: GfxPaths.Path);
  489. VAR ix, iy, t: REAL;
  490. BEGIN
  491. IF (SELF.joinStyle = BevelJoin) OR (SELF.joinStyle = MiterJoin) & SELF.ExceedsLimit(hx, hy) THEN
  492. GfxPaths.IntersectLines(x, y, hx, hy, x + idy, y - idx, -hy, hx, ix, iy);
  493. path.AddLine(ix, iy)
  494. ELSIF SELF.joinStyle = MiterJoin THEN
  495. path.AddLine(x + hx, y + hy)
  496. ELSIF SELF.joinStyle = RoundJoin THEN
  497. t := Math.sqrt((idx * idx + idy * idy)/(hx * hx + hy * hy));
  498. path.AddArc(x + t * hx, y + t * hy, x, y, x - idx, y - idy, x + idy, y - idx)
  499. END;
  500. path.AddLine(x - hx, y - hy)
  501. END AddExitJoinStyle;
  502. PROCEDURE GetPolyOutline (VAR x, y: ARRAY OF REAL; n: LONGINT; dxi, dyi, dxo, dyo: REAL; dst: GfxPaths.Path);
  503. VAR closed: BOOLEAN; width, odx, ody, idx, idy, hx, hy: REAL; i, j: LONGINT;
  504. BEGIN
  505. closed := (x[n] = x[0]) & (y[n] = y[0]);
  506. GfxMatrix.ApplyToDist(SELF.cam, 0.5*SELF.lineWidth, width);
  507. GetNormVector(x[1] - x[0], y[1] - y[0], width, odx, ody);
  508. IF (dxi = 0) & (dyi = 0) THEN
  509. SELF.EnterCapStyle(x[0], y[0], odx, ody, dst)
  510. ELSE
  511. GetNormVector(dxi, dyi, width, idx, idy);
  512. GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
  513. IF (hx = 0) & (hy = 0) THEN (* collinear vectors *)
  514. IF closed THEN
  515. dst.AddEnter(x[0] + ody, y[0] - odx, dxi, dyi)
  516. ELSE
  517. dst.AddEnter(x[0] - ody, y[0] + odx, ody, -odx);
  518. dst.AddLine(x[0] + ody, y[0] - odx)
  519. END
  520. ELSIF idx * ody > idy * odx THEN (* starts with left turn *)
  521. IF closed THEN
  522. SELF.EnterJoinStyle(x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
  523. ELSE
  524. dst.AddEnter(x[0] - hx, y[0] - hy, ody, -odx);
  525. SELF.AddEnterJoinStyle(x[0], y[0], hx, hy, odx, ody, dst)
  526. END
  527. ELSE
  528. IF closed THEN
  529. dst.AddEnter(x[0] - hx, y[0] - hy, dxi, dyi)
  530. ELSE
  531. dst.AddEnter(x[0] + hx, y[0] + hy, -hx, -hy);
  532. dst.AddLine(x[0] - hx, y[0] - hy)
  533. END
  534. END
  535. END;
  536. i := 1; j := 2;
  537. WHILE j <= n DO
  538. idx := odx; idy := ody;
  539. GetNormVector(x[j] - x[i], y[j] - y[i], width, odx, ody);
  540. GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
  541. IF (hx = 0) & (hy = 0) THEN (* collinear vectors *)
  542. dst.AddLine(x[i] + idy, y[i] - idx)
  543. ELSIF idx * ody > idy * odx THEN (* left turn => outer join *)
  544. dst.AddLine(x[i] + idy, y[i] - idx);
  545. SELF.AddJoinStyle(x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
  546. ELSE (* right turn => inner join *)
  547. dst.AddLine(x[i] - hx, y[i] - hy)
  548. END;
  549. i := j; INC(j)
  550. END;
  551. idx := odx; idy := ody;
  552. IF (dxo = 0) & (dyo = 0) THEN
  553. dst.AddLine(x[n] + ody, y[n] - odx);
  554. SELF.AddCapStyle(x[n], y[n], -odx, -ody, dst)
  555. ELSE
  556. dst.AddLine(x[n] + idy, y[n] - idx);
  557. GetNormVector(dxo, dyo, width, odx, ody);
  558. GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
  559. IF (hx = 0) & (hy = 0) THEN (* collinear vectors *)
  560. IF closed THEN
  561. dst.AddExit(odx, ody);
  562. dst.AddEnter(x[n] - idy, y[n] + idx, -dxo, -dyo)
  563. ELSE
  564. dst.AddLine(x[n] - idy, y[n] + idx)
  565. END
  566. ELSIF idx * ody > idy * odx THEN (* ends in left turn *)
  567. IF closed THEN
  568. SELF.ExitJoinStyle(x[n], y[n], idx, idy, hx, hy, odx, ody, dst);
  569. dst.AddEnter(x[n] - hx, y[n] - hy, -dxo, -dyo)
  570. ELSE
  571. SELF.AddExitJoinStyle(x[n], y[n], idx, idy, hx, hy, dst)
  572. END
  573. ELSE
  574. dst.AddLine(x[n] - hx, y[n] - hy);
  575. IF closed THEN
  576. dst.AddExit(dxo, dyo);
  577. SELF.EnterJoinStyle(x[n], y[n], -odx, -ody, -hx, -hy, -idx, -idy, dst)
  578. ELSE
  579. SELF.AddEnterJoinStyle(x[n], y[n], -hx, -hy, -idx, -idy, dst)
  580. END
  581. END
  582. END;
  583. odx := -idx; ody := -idy;
  584. i := n-1; j := n-2;
  585. WHILE j >= 0 DO
  586. idx := odx; idy := ody;
  587. GetNormVector(x[j] - x[i], y[j] - y[i], width, odx, ody);
  588. GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
  589. IF (hx = 0) & (hy = 0) THEN (* collinear vectors *)
  590. dst.AddLine(x[i] + idy, y[i] - idx)
  591. ELSIF idx * ody > idy * odx THEN (* left turn => outer join *)
  592. dst.AddLine(x[i] + idy, y[i] - idx);
  593. SELF.AddJoinStyle(x[i], y[i], idx, idy, hx, hy, odx, ody, dst)
  594. ELSE (* right turn => inner join *)
  595. dst.AddLine(x[i] - hx, y[i] - hy)
  596. END;
  597. i := j; DEC(j)
  598. END;
  599. IF (dxi = 0) & (dyi = 0) THEN
  600. dst.AddLine(x[0] + ody, y[0] - odx);
  601. SELF.ExitCapStyle(x[0], y[0], -odx, -ody, dst)
  602. ELSE
  603. idx := odx; idy := ody;
  604. GetNormVector(-dxi, -dyi, width, odx, ody);
  605. GetHalfAxisVector(idx, idy, odx, ody, hx, hy);
  606. dst.AddLine(x[0] + idy, y[0] - idx);
  607. IF (hx = 0) & (hy = 0) THEN (* collinear vectors *)
  608. IF closed THEN
  609. dst.AddExit(-dxi, -dyi)
  610. ELSE
  611. dst.AddExit(-idy, idx)
  612. END
  613. ELSIF idx * ody > idy * odx THEN (* left turn *)
  614. IF closed THEN
  615. SELF.ExitJoinStyle(x[0], y[0], idx, idy, hx, hy, odx, ody, dst)
  616. ELSE
  617. SELF.AddExitJoinStyle(x[0], y[0], idx, idy, hx, hy, dst);
  618. dst.AddExit(-idx, -idy)
  619. END
  620. ELSE
  621. dst.AddLine(x[0] - hx, y[0] - hy);
  622. IF closed THEN
  623. dst.AddExit(-dxi, -dyi)
  624. ELSE
  625. dst.AddExit(hx, hy)
  626. END
  627. END
  628. END
  629. END GetPolyOutline;
  630. PROCEDURE GetStrokeOutline (VAR scan: GfxPaths.Scanner; dst: GfxPaths.Path);
  631. CONST last = 127;
  632. VAR x, y: ARRAY last+1 OF REAL; dxi, dyi, dxo, dyo: REAL; n: LONGINT;
  633. BEGIN
  634. ASSERT(scan.elem = GfxPaths.Enter);
  635. x[0] := scan.x; y[0] := scan.y; dxi := scan.dx; dyi := scan.dy;
  636. scan.Scan(); n := 0;
  637. WHILE scan.elem = GfxPaths.Line DO
  638. IF n < last THEN
  639. INC(n); x[n] := scan.x; y[n] := scan.y
  640. ELSE
  641. dxo := scan.x - x[n]; dyo := scan.y - y[n];
  642. SELF.GetPolyOutline(x, y, n, dxi, dyi, dxo, dyo, dst);
  643. dxi := x[n] - x[n-1]; dyi := y[n] - y[n-1];
  644. x[0] := x[n]; y[0] := y[n];
  645. x[1] := scan.x; y[1] := scan.y;
  646. n := 1
  647. END;
  648. scan.Scan()
  649. END;
  650. IF n > 0 THEN
  651. SELF.GetPolyOutline(x, y, n, dxi, dyi, scan.dx, scan.dy, dst)
  652. END;
  653. scan.Scan()
  654. END GetStrokeOutline;
  655. (** get offset values and pattern index of visible and invisible dash part at start of subpath (in device space) **)
  656. PROCEDURE GetDashOffsets* (offset: REAL; VAR beg, end, next: REAL; VAR idx: LONGINT);
  657. VAR phase, period, len: REAL;
  658. BEGIN
  659. idx := 0;
  660. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPhase, phase);
  661. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPeriod, period);
  662. beg := ENTIER((phase + offset)/period) * period - phase; (* offset - period < beg <= offset *)
  663. LOOP
  664. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOn[idx], len);
  665. end := beg + len;
  666. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOff[idx], len);
  667. next := end + len;
  668. idx := (idx+1) MOD SELF.dashPatLen;
  669. IF next > offset THEN EXIT END;
  670. beg := next
  671. END
  672. END GetDashOffsets;
  673. PROCEDURE GetDashOutline (VAR scan: GfxPaths.Scanner; dst: GfxPaths.Path);
  674. VAR
  675. width, cx, cy, dx, dy, beg, end, next, offset, len, cos, sin, wdx, wdy, endOff, dash, nx, ny: REAL;
  676. index: LONGINT; dscan: GfxPaths.Scanner;
  677. BEGIN
  678. GfxMatrix.ApplyToDist(SELF.cam, 0.5*SELF.lineWidth, width);
  679. ASSERT(scan.elem = GfxPaths.Enter);
  680. cx := scan.x; cy := scan.y; dx := scan.dx; dy := scan.dy;
  681. scan.Scan();
  682. SELF.GetDashOffsets(0, beg, end, next, index);
  683. IF 0 < end THEN (* starts within dash *)
  684. IF width = 0 THEN
  685. dst.AddEnter(cx, cy, dx, dy)
  686. ELSE
  687. SELF.dashPath.Clear();
  688. SELF.dashPath.AddEnter(cx, cy, dx, dy)
  689. END
  690. END;
  691. offset := 0;
  692. WHILE scan.elem = GfxPaths.Line DO
  693. dx := scan.x - cx; dy := scan.y - cy;
  694. len := Math.sqrt(dx * dx + dy * dy);
  695. cos := dx/len; sin := dy/len;
  696. endOff := offset + len;
  697. IF offset < end THEN (* begin of line is within dash *)
  698. IF end <= endOff THEN (* end of current dash comes before end of line => finish current dash *)
  699. len := end - offset;
  700. IF width = 0 THEN
  701. dst.AddLine(cx + len * cos, cy + len * sin);
  702. dst.AddExit(0, 0)
  703. ELSE
  704. SELF.dashPath.AddLine(cx + len * cos, cy + len * sin);
  705. SELF.dashPath.AddExit(0, 0);
  706. dscan.Open(SELF.dashPath, 0);
  707. SELF.GetStrokeOutline(dscan, dst)
  708. END
  709. ELSIF width = 0 THEN (* continue current dash to end of line *)
  710. dst.AddLine(scan.x, scan.y)
  711. ELSE
  712. SELF.dashPath.AddLine(scan.x, scan.y)
  713. END
  714. END;
  715. IF next < endOff THEN (* next dash starts before end of line => draw complete dashes *)
  716. wdx := width * cos; wdy := width * sin;
  717. beg := offset;
  718. REPEAT
  719. len := next - beg;
  720. cx := cx + len * cos; cy := cy + len * sin;
  721. beg := next;
  722. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOn[index], dash);
  723. end := beg + dash;
  724. GfxMatrix.ApplyToDist(SELF.cam, SELF.dashPatOff[index], dash);
  725. next := end + dash;
  726. index := (index+1) MOD SELF.dashPatLen;
  727. IF end <= endOff THEN (* next dash can be fully drawn *)
  728. len := end - beg;
  729. nx := cx + len * cos; ny := cy + len * sin;
  730. IF width = 0 THEN
  731. dst.AddEnter(cx, cy, 0, 0);
  732. dst.AddLine(nx, ny);
  733. dst.AddExit(0, 0)
  734. ELSE
  735. SELF.EnterCapStyle(cx, cy, wdx, wdy, dst);
  736. dst.AddLine(nx + wdy, ny - wdx);
  737. SELF.AddCapStyle(nx, ny, -wdx, -wdy, dst);
  738. dst.AddLine(cx - wdy, cy + wdx);
  739. SELF.ExitCapStyle(cx, cy, wdx, wdy, dst)
  740. END
  741. END
  742. UNTIL next >= endOff;
  743. IF endOff < end THEN (* next dash not complete => hasn't been started yet *)
  744. IF width = 0 THEN
  745. dst.AddEnter(cx, cy, 0, 0);
  746. dst.AddLine(scan.x, scan.y)
  747. ELSE
  748. SELF.dashPath.Clear();
  749. SELF.dashPath.AddEnter(cx, cy, 0, 0);
  750. SELF.dashPath.AddLine(scan.x, scan.y)
  751. END
  752. END
  753. END;
  754. cx := scan.x; cy := scan.y; offset := endOff;
  755. scan.Scan()
  756. END;
  757. ASSERT(scan.elem = GfxPaths.Exit);
  758. IF offset < end THEN (* currently within dash => end properly *)
  759. IF width = 0 THEN
  760. dst.AddExit(scan.dx, scan.dy)
  761. ELSE
  762. SELF.dashPath.AddExit(scan.dx, scan.dy);
  763. dscan.Open(SELF.dashPath, 0);
  764. SELF.GetStrokeOutline(dscan, dst)
  765. END
  766. END;
  767. scan.Scan()
  768. END GetDashOutline;
  769. (** store outline/dashes of current path in specified path **)
  770. PROCEDURE GetOutline* (dst: GfxPaths.Path);
  771. VAR scan: GfxPaths.Scanner;
  772. BEGIN
  773. ASSERT(dst # SELF.path, 100);
  774. SELF.Flatten();
  775. dst.Clear();
  776. scan.Open(SELF.path, 0);
  777. WHILE scan.elem = GfxPaths.Enter DO
  778. IF SELF.dashPatLen > 0 THEN
  779. SELF.GetDashOutline(scan, dst)
  780. ELSE
  781. SELF.GetStrokeOutline(scan, dst)
  782. END
  783. END
  784. END GetOutline;
  785. (**--- Drawing Operations ---**)
  786. (** draw current path in requested mode **)
  787. PROCEDURE Render* (mode: SET);
  788. BEGIN
  789. ASSERT(~(InPath IN SELF.mode), 100);
  790. EXCL(mode, Record);
  791. IF mode # {} THEN
  792. SELF.DoRender(mode)
  793. END
  794. END Render;
  795. (** draw given path in requested mode **)
  796. PROCEDURE DrawPath* (path: GfxPaths.Path; mode: SET);
  797. VAR scan: GfxPaths.Scanner;
  798. BEGIN
  799. ASSERT(~(InPath IN SELF.mode), 100);
  800. IF path = SELF.path THEN
  801. EXCL(mode, Record);
  802. IF mode # {} THEN
  803. SELF.DoRender(mode)
  804. END;
  805. ELSE
  806. SELF.DoBegin(mode);
  807. scan.Open(path, 0);
  808. WHILE scan.elem # GfxPaths.Stop DO
  809. CASE scan.elem OF
  810. | GfxPaths.Enter: SELF.DoEnter(scan.x, scan.y, scan.dx, scan.dy)
  811. | GfxPaths.Line: SELF.DoLine(scan.x, scan.y);
  812. | GfxPaths.Arc: SELF.DoArc(scan.x, scan.y, scan.x0, scan.y0, scan.x1, scan.y1, scan.x2, scan.y2)
  813. | GfxPaths.Bezier: SELF.DoBezier(scan.x, scan.y, scan.x1, scan.y1, scan.x2, scan.y2)
  814. | GfxPaths.Exit: SELF.DoExit(scan.dx, scan.dy)
  815. END;
  816. scan.Scan();
  817. END;
  818. SELF.DoEnd()
  819. END
  820. END DrawPath;
  821. (** draw line in requested mode **)
  822. PROCEDURE DrawLine* (x0, y0, x1, y1: REAL; mode: SET);
  823. BEGIN
  824. IF (x0=x1)&(y0=y1) THEN RETURN END; (*optimization PH 2012*)
  825. ASSERT(~(InPath IN SELF.mode), 100);
  826. ASSERT(mode * {Fill, Clip, EvenOdd} = {}, 101);
  827. SELF.DoBegin(mode);
  828. SELF.DoEnter(x0, y0, 0, 0);
  829. SELF.DoLine(x1, y1);
  830. SELF.DoExit(0, 0);
  831. SELF.DoEnd()
  832. END DrawLine;
  833. (** draw arc in requested mode (start and end angle in radians; negative radius for clockwise arc) **)
  834. PROCEDURE DrawArc* (x, y, r, start, end: REAL; mode: SET);
  835. VAR x1, y1, x2, y2: REAL;
  836. BEGIN
  837. ASSERT(~(InPath IN SELF.mode), 100);
  838. ASSERT(mode * {Fill, Clip, EvenOdd} = {}, 101);
  839. IF r > 0 THEN x1 := x + r; y1 := y; x2 := x; y2 := y + r
  840. ELSIF r < 0 THEN r := -r; x1 := x; y1 := y + r; x2 := x + r; y2 := y
  841. ELSE RETURN
  842. END;
  843. SELF.DoBegin(mode);
  844. SELF.DoEnter(x + r * Math.cos(start), y + r * Math.sin(start), 0, 0);
  845. SELF.DoArc(x + r * Math.cos(end), y + r * Math.sin(end), x, y, x1, y1, x2, y2);
  846. SELF.DoEnd()
  847. END DrawArc;
  848. (** draw rectangle in requested mode **)
  849. PROCEDURE DrawRect* (x0, y0, x1, y1: REAL; mode: SET);
  850. BEGIN
  851. ASSERT(~(InPath IN SELF.mode), 100);
  852. SELF.DoBegin(mode);
  853. SELF.DoRect(x0, y0, x1, y1);
  854. SELF.DoEnd()
  855. END DrawRect;
  856. (** draw circle in requested mode (clockwise if r > 0, counterclockwise if r < 0) **)
  857. PROCEDURE DrawCircle* (x, y, r: REAL; mode: SET);
  858. BEGIN
  859. ASSERT(~(InPath IN SELF.mode), 100);
  860. SELF.DoBegin(mode);
  861. SELF.DoEllipse(x, y, r, ABS(r));
  862. SELF.DoEnd()
  863. END DrawCircle;
  864. (** draw ellipse in requested mode (clockwise if rx*ry > 0, counterclockwise if rx*ry < 0) **)
  865. PROCEDURE DrawEllipse* (x, y, rx, ry: REAL; mode: SET);
  866. BEGIN
  867. ASSERT(~(InPath IN SELF.mode), 100);
  868. SELF.DoBegin(mode);
  869. SELF.DoEllipse(x, y, rx, ry);
  870. SELF.DoEnd()
  871. END DrawEllipse;
  872. (** draw string at given coordinates and move current point to string end **)
  873. PROCEDURE DrawStringAt* (x, y: REAL; str: ARRAY OF CHAR);
  874. BEGIN
  875. ASSERT(~(InPath IN SELF.mode), 100);
  876. SELF.DoBegin({Fill});
  877. SELF.DoShow(x, y, str);
  878. SELF.DoEnd()
  879. END DrawStringAt;
  880. (** draw string at current point and move current point to string end **)
  881. PROCEDURE DrawString* (str: ARRAY OF CHAR);
  882. BEGIN
  883. ASSERT(~(InPath IN SELF.mode), 100);
  884. SELF.DoBegin({Fill});
  885. SELF.DoShow(SELF.cpx, SELF.cpy, str);
  886. SELF.DoEnd()
  887. END DrawString;
  888. (** images and patterns **)
  889. (** draw image at given point **)
  890. PROCEDURE DrawImageAt* (x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
  891. BEGIN
  892. SELF.DrawImage(x, y, img, filter)
  893. END DrawImageAt;
  894. PROCEDURE{ABSTRACT} DrawImage*(x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter); END DrawImage;
  895. PROCEDURE NewPattern*(img: GfxImages.Image; px, py: REAL): Pattern;
  896. VAR pat: Pattern;
  897. BEGIN
  898. NEW(pat); pat.img := img; pat.px := px; pat.py := py;
  899. RETURN pat
  900. END NewPattern;
  901. (**--- Implementation ---**)
  902. (** draw current path in requested mode **)
  903. PROCEDURE{ABSTRACT} DoRender*(mode: SET); END DoRender;
  904. (** start new path **)
  905. PROCEDURE{ABSTRACT} DoBegin*(mode: SET); END DoBegin;
  906. (** exit current subpath (if open) and end current path **)
  907. PROCEDURE{ABSTRACT} DoEnd*(); END DoEnd;
  908. (** start subpath at inner point **)
  909. PROCEDURE{ABSTRACT} DoEnter*(x, y, dx, dy: REAL); END DoEnter;
  910. (** end subpath at inner point **)
  911. PROCEDURE{ABSTRACT} DoExit*(dx, dy: REAL); END DoExit;
  912. (** close current subpath **)
  913. PROCEDURE{ABSTRACT} DoClose*(); END DoClose;
  914. (** append line to current path **)
  915. PROCEDURE{ABSTRACT} DoLine*(x, y: REAL); END DoLine;
  916. (** append arc to current path **)
  917. PROCEDURE{ABSTRACT} DoArc*(x, y, x0, y0, x1, y1, x2, y2: REAL); END DoArc;
  918. (** append cubic bezier to current path **)
  919. PROCEDURE{ABSTRACT} DoBezier*(x, y, x1, y1, x2, y2: REAL); END DoBezier;
  920. (** append character outlines to current path at current point; advance current point to position after last character **)
  921. PROCEDURE{ABSTRACT} DoShow*(x, y: REAL; VAR str: ARRAY OF CHAR); END DoShow;
  922. (** painting operators (potential for optimization) **)
  923. PROCEDURE{ABSTRACT} DoRect*(x0, y0, x1, y1: REAL); (* default implementation *)
  924. BEGIN
  925. SELF.DoEnter(x0, y0, 0, y0 - y1);
  926. SELF.DoLine(x1, y0); SELF.DoLine(x1, y1); SELF.DoLine(x0, y1); SELF.DoLine(x0, y0);
  927. SELF.DoExit(x1 - x0, 0)
  928. END DoRect;
  929. PROCEDURE{ABSTRACT} DoEllipse*(x, y, rx, ry: REAL); (* default implementation *)
  930. VAR xr: REAL;
  931. BEGIN
  932. xr := x + rx;
  933. IF xr # x THEN
  934. SELF.DoEnter(xr, y, 0, ry);
  935. SELF.DoArc(xr, y, x, y, xr, y, x, y + ry);
  936. SELF.DoExit(0, ry)
  937. END
  938. END DoEllipse;
  939. END Context;
  940. (** graphics state **)
  941. State* = RECORD
  942. saved: SET;
  943. strokeCol, fillCol: Color; strokePat, fillPat: Pattern;
  944. lineWidth: REAL;
  945. dashPatOn, dashPatOff: ARRAY MaxDashPatSize OF REAL;
  946. dashPatLen: LONGINT; dashPhase: REAL;
  947. capStyle: CapStyle; joinStyle: JoinStyle; styleLimit: REAL;
  948. flatness: REAL;
  949. font: GfxFonts.Font;
  950. ctm: GfxMatrix.Matrix;
  951. clip: ClipArea;
  952. END;
  953. PathData = RECORD (GfxPaths.EnumData)
  954. path: GfxPaths.Path;
  955. END;
  956. VAR
  957. Black*, White*, Red*, Green*, Blue*, Cyan*, Magenta*, Yellow*, LGrey*, MGrey*, DGrey*: Color; (** standard colors **)
  958. DefaultCap*: CapStyle; (** default line cap style (initially butt caps) **)
  959. DefaultJoin*: JoinStyle; (** default line join style (initially miter joins) **)
  960. (**--- Contexts ---**)
  961. (** reset context to default values **)
  962. PROCEDURE Reset* (ctxt: Context);
  963. BEGIN
  964. ctxt.Reset()
  965. END Reset;
  966. (** initialize context values to defaults **)
  967. PROCEDURE Init* (ctxt: Context);
  968. BEGIN
  969. ctxt.InitContext();
  970. END Init;
  971. (** save and restore graphics state **)
  972. PROCEDURE Save* (ctxt: Context; elems: SET; VAR state: State);
  973. VAR i: LONGINT;
  974. BEGIN
  975. state.saved := elems;
  976. state.strokeCol := ctxt.strokeCol; state.strokePat := ctxt.strokePat;
  977. state.fillCol := ctxt.fillCol; state.fillPat := ctxt.fillPat;
  978. state.lineWidth := ctxt.lineWidth;
  979. IF dashPat IN elems THEN
  980. state.dashPatLen := ctxt.dashPatLen; state.dashPhase := ctxt.dashPhase;
  981. i := 0;
  982. WHILE i < ctxt.dashPatLen DO
  983. state.dashPatOn[i] := ctxt.dashPatOn[i]; state.dashPatOff[i] := ctxt.dashPatOff[i]; INC(i)
  984. END
  985. END;
  986. state.capStyle := ctxt.capStyle; state.joinStyle := ctxt.joinStyle; state.styleLimit := ctxt.styleLimit;
  987. state.flatness := ctxt.flatness;
  988. state.font := ctxt.font;
  989. IF ctm IN elems THEN
  990. state.ctm := ctxt.ctm
  991. END;
  992. IF clip IN elems THEN
  993. state.clip := ctxt.GetClip()
  994. END
  995. END Save;
  996. PROCEDURE Restore* (ctxt: Context; state: State);
  997. BEGIN
  998. ASSERT(~(InPath IN ctxt.mode), 100);
  999. IF strokeColPat IN state.saved THEN
  1000. ctxt.SetStrokeColor(state.strokeCol);
  1001. ctxt.SetStrokePattern(state.strokePat)
  1002. END;
  1003. IF fillColPat IN state.saved THEN
  1004. ctxt.SetFillColor(state.fillCol);
  1005. ctxt.SetFillPattern(state.fillPat)
  1006. END;
  1007. IF lineWidth IN state.saved THEN
  1008. ctxt.SetLineWidth(state.lineWidth)
  1009. END;
  1010. IF dashPat IN state.saved THEN
  1011. ctxt.SetDashPattern(state.dashPatOn, state.dashPatOff, state.dashPatLen, state.dashPhase)
  1012. END;
  1013. IF capStyle IN state.saved THEN
  1014. ctxt.SetCapStyle(state.capStyle)
  1015. END;
  1016. IF joinStyle IN state.saved THEN
  1017. ctxt.SetJoinStyle(state.joinStyle)
  1018. END;
  1019. IF styleLimit IN state.saved THEN
  1020. ctxt.SetStyleLimit(state.styleLimit)
  1021. END;
  1022. IF flatness IN state.saved THEN
  1023. ctxt.SetFlatness(state.flatness)
  1024. END;
  1025. IF font IN state.saved THEN
  1026. ctxt.SetFont(state.font)
  1027. END;
  1028. IF ctm IN state.saved THEN
  1029. ctxt.SetCTM(state.ctm)
  1030. END;
  1031. IF clip IN state.saved THEN
  1032. ctxt.SetClip(state.clip)
  1033. END
  1034. END Restore;
  1035. (**--- Coordinate System ---**)
  1036. (** reset current transformation matrix **)
  1037. PROCEDURE ResetCTM* (ctxt: Context);
  1038. BEGIN
  1039. ctxt.ResetCTM()
  1040. END ResetCTM;
  1041. (** set current transformation matrix **)
  1042. PROCEDURE SetCTM* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
  1043. BEGIN
  1044. ctxt.SetCTM(mat)
  1045. END SetCTM;
  1046. (** translate coordinate system **)
  1047. PROCEDURE Translate* (ctxt: Context; dx, dy: REAL);
  1048. BEGIN
  1049. ctxt.Translate(dx, dy)
  1050. END Translate;
  1051. (** scale coordinate system at origin **)
  1052. PROCEDURE Scale* (ctxt: Context; sx, sy: REAL);
  1053. BEGIN
  1054. ctxt.Scale(sx, sy)
  1055. END Scale;
  1056. (** scale coordinate system at specified point **)
  1057. PROCEDURE ScaleAt* (ctxt: Context; sx, sy, x, y: REAL);
  1058. BEGIN
  1059. ctxt.ScaleAt(sx, sy, x, y);
  1060. END ScaleAt;
  1061. (** rotate coordinate system at origin **)
  1062. PROCEDURE Rotate* (ctxt: Context; sin, cos: REAL);
  1063. BEGIN
  1064. ctxt.Rotate(sin, cos)
  1065. END Rotate;
  1066. (** rotate coordinate system at specified point **)
  1067. PROCEDURE RotateAt* (ctxt: Context; sin, cos, x, y: REAL);
  1068. BEGIN
  1069. ctxt.RotateAt(sin, cos, x, y);
  1070. END RotateAt;
  1071. (** concat transformation matrix to CTM **)
  1072. PROCEDURE Concat* (ctxt: Context; VAR mat: GfxMatrix.Matrix);
  1073. BEGIN
  1074. ctxt.Concat(mat)
  1075. END Concat;
  1076. (**--- Clipping ---**)
  1077. (** reset clip path **)
  1078. PROCEDURE ResetClip* (ctxt: Context);
  1079. BEGIN
  1080. ctxt.ResetClip()
  1081. END ResetClip;
  1082. (** get bounding box of clipping path in user coordinates **)
  1083. PROCEDURE GetClipRect* (ctxt: Context; VAR llx, lly, urx, ury: REAL);
  1084. BEGIN
  1085. ctxt.GetClipRect(llx, lly, urx, ury)
  1086. END GetClipRect;
  1087. (** get current clipping area **)
  1088. PROCEDURE GetClip* (ctxt: Context): ClipArea;
  1089. BEGIN
  1090. RETURN ctxt.GetClip()
  1091. END GetClip;
  1092. (** restore saved clipping path **)
  1093. PROCEDURE SetClip* (ctxt: Context; clip: ClipArea);
  1094. BEGIN
  1095. ctxt.SetClip(clip)
  1096. END SetClip;
  1097. (**--- Graphics State ---**)
  1098. (** set stroke color **)
  1099. PROCEDURE SetStrokeColor* (ctxt: Context; color: Color);
  1100. BEGIN
  1101. ctxt.SetStrokeColor(color)
  1102. END SetStrokeColor;
  1103. (** set stroke pattern (NIL = solid) **)
  1104. PROCEDURE SetStrokePattern* (ctxt: Context; pat: Pattern);
  1105. BEGIN
  1106. ctxt.SetStrokePattern(pat)
  1107. END SetStrokePattern;
  1108. (** set fill color **)
  1109. PROCEDURE SetFillColor* (ctxt: Context; color: Color);
  1110. BEGIN
  1111. ctxt.SetFillColor(color)
  1112. END SetFillColor;
  1113. (** set fill pattern (NIL = solid) **)
  1114. PROCEDURE SetFillPattern* (ctxt: Context; pat: Pattern);
  1115. BEGIN
  1116. ctxt.SetFillPattern(pat)
  1117. END SetFillPattern;
  1118. (** set line width **)
  1119. PROCEDURE SetLineWidth* (ctxt: Context; width: REAL);
  1120. BEGIN
  1121. ctxt.SetLineWidth(width)
  1122. END SetLineWidth;
  1123. (** set dash pattern **)
  1124. PROCEDURE SetDashPattern* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT; phase: REAL);
  1125. BEGIN
  1126. ctxt.SetDashPattern(on, off, len, phase)
  1127. END SetDashPattern;
  1128. (** copy values from parameter, and calculate dash period **)
  1129. PROCEDURE SetDashArray* (ctxt: Context; VAR on, off: ARRAY OF REAL; len: LONGINT);
  1130. BEGIN
  1131. ctxt.SetDashArray(on, off, len);
  1132. END SetDashArray;
  1133. (** set line cap style **)
  1134. PROCEDURE SetCapStyle* (ctxt: Context; style: CapStyle);
  1135. BEGIN
  1136. ctxt.SetCapStyle(style)
  1137. END SetCapStyle;
  1138. (** set line join style **)
  1139. PROCEDURE SetJoinStyle* (ctxt: Context; style: JoinStyle);
  1140. BEGIN
  1141. ctxt.SetJoinStyle(style)
  1142. END SetJoinStyle;
  1143. (** set style border factor **)
  1144. PROCEDURE SetStyleLimit* (ctxt: Context; limit: REAL);
  1145. BEGIN
  1146. ctxt.SetStyleLimit(limit)
  1147. END SetStyleLimit;
  1148. (** set flatness parameter **)
  1149. PROCEDURE SetFlatness* (ctxt: Context; flatness: REAL);
  1150. BEGIN
  1151. ctxt.SetFlatness(flatness)
  1152. END SetFlatness;
  1153. (** set current font **)
  1154. PROCEDURE SetFont* (ctxt: Context; font: GfxFonts.Font);
  1155. BEGIN
  1156. ctxt.SetFont(font)
  1157. END SetFont;
  1158. (** set current font using name and size **)
  1159. PROCEDURE SetFontName* (ctxt: Context; fontname: ARRAY OF CHAR; size: INTEGER);
  1160. BEGIN
  1161. ctxt.SetFontName(fontname, size);
  1162. END SetFontName;
  1163. (** calculate distance that current point would move if given string were rendered **)
  1164. PROCEDURE GetStringWidth* (ctxt: Context; str: ARRAY OF CHAR; VAR dx, dy: REAL);
  1165. BEGIN
  1166. ctxt.GetStringWidth(str, dx, dy)
  1167. END GetStringWidth;
  1168. (**--- Current Path ---**)
  1169. (** start new path **)
  1170. PROCEDURE Begin* (ctxt: Context; mode: SET);
  1171. BEGIN
  1172. ctxt.Begin(mode);
  1173. END Begin;
  1174. (** exit current subpath (if open) and end current path **)
  1175. PROCEDURE End* (ctxt: Context);
  1176. BEGIN
  1177. ctxt.End();
  1178. END End;
  1179. (** end current subpath (if open) and begin new subpath **)
  1180. PROCEDURE MoveTo* (ctxt: Context; x, y: REAL);
  1181. BEGIN
  1182. ctxt.MoveTo(x, y);
  1183. END MoveTo;
  1184. (** start subpath at inner point **)
  1185. PROCEDURE Enter* (ctxt: Context; x, y, dx, dy: REAL);
  1186. BEGIN
  1187. ctxt.Enter(x, y, dx, dy);
  1188. END Enter;
  1189. (** end subpath at inner point **)
  1190. PROCEDURE Exit* (ctxt: Context; dx, dy: REAL);
  1191. BEGIN
  1192. ctxt.Exit(dx, dy);
  1193. END Exit;
  1194. (** close current subpath **)
  1195. PROCEDURE Close* (ctxt: Context);
  1196. BEGIN
  1197. ctxt.Close();
  1198. END Close;
  1199. (** append line to current path **)
  1200. PROCEDURE LineTo* (ctxt: Context; x, y: REAL);
  1201. BEGIN
  1202. ctxt.LineTo(x, y);
  1203. END LineTo;
  1204. (** append arc to current path **)
  1205. PROCEDURE ArcTo* (ctxt: Context; x, y, x0, y0, x1, y1, x2, y2: REAL);
  1206. BEGIN
  1207. ctxt.ArcTo(x, y, x0, y0, x1, y1, x2, y2);
  1208. END ArcTo;
  1209. (** append cubic bezier to current path **)
  1210. PROCEDURE BezierTo* (ctxt: Context; x, y, x1, y1, x2, y2: REAL);
  1211. BEGIN
  1212. ctxt.BezierTo(x, y, x1, y1, x2, y2);
  1213. END BezierTo;
  1214. (** append character outlines to current path at given point; advance current point to position after last character **)
  1215. PROCEDURE ShowAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
  1216. BEGIN
  1217. ctxt.ShowAt(x, y, str);
  1218. END ShowAt;
  1219. (** append character outlines to current path at current point; advance current point to position after last character **)
  1220. PROCEDURE Show* (ctxt: Context; str: ARRAY OF CHAR);
  1221. BEGIN
  1222. ctxt.Show(str);
  1223. END Show;
  1224. (**--- Path Flattening ---**)
  1225. (** replace arcs and beziers in current path by approximation using straight lines **)
  1226. PROCEDURE Flatten* (ctxt: Context);
  1227. BEGIN
  1228. ctxt.Flatten()
  1229. END Flatten;
  1230. PROCEDURE EnumPathElem (VAR data: GfxPaths.EnumData);
  1231. BEGIN
  1232. WITH data: PathData DO
  1233. CASE data.elem OF
  1234. | GfxPaths.Enter: data.path.AddEnter(data.x, data.y, data.dx, data.dy)
  1235. | GfxPaths.Line: data.path.AddLine(data.x, data.y)
  1236. | GfxPaths.Exit: data.path.AddExit(data.dx, data.dy)
  1237. END
  1238. END
  1239. END EnumPathElem;
  1240. (** store flattened current path in given path **)
  1241. PROCEDURE GetFlattenedPath* (ctxt: Context; path: GfxPaths.Path);
  1242. BEGIN
  1243. ctxt.GetFlattenedPath(path);
  1244. END GetFlattenedPath;
  1245. (**--- Cap Styles ---**)
  1246. PROCEDURE EnterCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
  1247. BEGIN
  1248. ctxt.EnterCapStyle(x, y, dx, dy, path);
  1249. END EnterCapStyle;
  1250. PROCEDURE AddCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
  1251. BEGIN
  1252. ctxt.AddCapStyle(x, y, dx, dy, path);
  1253. END AddCapStyle;
  1254. PROCEDURE ExitCapStyle* (ctxt: Context; x, y, dx, dy: REAL; path: GfxPaths.Path);
  1255. BEGIN
  1256. ctxt.ExitCapStyle(x, y, dx, dy, path);
  1257. END ExitCapStyle;
  1258. (**--- Join Styles ---**)
  1259. (** return if half axis vector (in device coordinates) exceeds style limit **)
  1260. PROCEDURE ExceedsLimit* (ctxt: Context; hx, hy: REAL): BOOLEAN;
  1261. BEGIN
  1262. RETURN ctxt.ExceedsLimit(hx, hy);
  1263. END ExceedsLimit;
  1264. PROCEDURE EnterJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  1265. BEGIN
  1266. ctxt.EnterJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
  1267. END EnterJoinStyle;
  1268. PROCEDURE AddJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  1269. BEGIN
  1270. ctxt.AddJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
  1271. END AddJoinStyle;
  1272. PROCEDURE ExitJoinStyle* (ctxt: Context; x, y, idx, idy, hx, hy, odx, ody: REAL; path: GfxPaths.Path);
  1273. BEGIN
  1274. ctxt.ExitJoinStyle(x, y, idx, idy, hx, hy, odx, ody, path);
  1275. END ExitJoinStyle;
  1276. (**--- Path Outline ---**)
  1277. (** replace current path by outline of area which would be drawn to if the path were stroked **)
  1278. PROCEDURE Outline* (ctxt: Context);
  1279. BEGIN
  1280. ctxt.Outline()
  1281. END Outline;
  1282. (** return vector scaled to given length **)
  1283. PROCEDURE GetNormVector* (x, y, len: REAL; VAR nx, ny: REAL);
  1284. VAR t: REAL;
  1285. BEGIN
  1286. t := len/Math.sqrt(x * x + y * y);
  1287. nx := t * x; ny := t * y
  1288. END GetNormVector;
  1289. (** return vector to outer corner of two joining vectors whose lengths correspond to line width **)
  1290. PROCEDURE GetHalfAxisVector* (idx, idy, odx, ody: REAL; VAR hx, hy: REAL);
  1291. VAR cprod, t: REAL;
  1292. BEGIN
  1293. cprod := idx * ody - idy * odx;
  1294. IF ABS(cprod) < 1.0E-3 THEN
  1295. hx := 0; hy := 0
  1296. ELSE (* intersect outer border lines to find half axis vector *)
  1297. t := ((idy - ody) * ody + (idx - odx) * odx)/cprod;
  1298. IF cprod > 0 THEN (* left turn *)
  1299. hx := idy - t * idx; hy := -(idx + t * idy)
  1300. ELSE (* right turn *)
  1301. hx := t * idx - idy; hy := idx + t * idy
  1302. END
  1303. END
  1304. END GetHalfAxisVector;
  1305. (** get offset values and pattern index of visible and invisible dash part at start of subpath (in device space) **)
  1306. PROCEDURE GetDashOffsets* (ctxt: Context; offset: REAL; VAR beg, end, next: REAL; VAR idx: LONGINT);
  1307. BEGIN
  1308. ctxt.GetDashOffsets(offset, beg, end, next, idx);
  1309. END GetDashOffsets;
  1310. (** store outline/dashes of current path in specified path **)
  1311. PROCEDURE GetOutline* (ctxt: Context; dst: GfxPaths.Path);
  1312. BEGIN
  1313. ctxt.GetOutline(dst);
  1314. END GetOutline;
  1315. (**--- Drawing Operations ---**)
  1316. (** draw current path in requested mode **)
  1317. PROCEDURE Render* (ctxt: Context; mode: SET);
  1318. BEGIN
  1319. ASSERT(~(InPath IN ctxt.mode), 100);
  1320. EXCL(mode, Record);
  1321. IF mode # {} THEN
  1322. ctxt.DoRender(mode)
  1323. END
  1324. END Render;
  1325. (** draw given path in requested mode **)
  1326. PROCEDURE DrawPath* (ctxt: Context; path: GfxPaths.Path; mode: SET);
  1327. BEGIN
  1328. ctxt.DrawPath(path, mode);
  1329. END DrawPath;
  1330. (** draw line in requested mode **)
  1331. PROCEDURE DrawLine* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
  1332. BEGIN
  1333. ctxt.DrawLine(x0, y0, x1, y1, mode);
  1334. END DrawLine;
  1335. (** draw arc in requested mode (start and end angle in radians; negative radius for clockwise arc) **)
  1336. PROCEDURE DrawArc* (ctxt: Context; x, y, r, start, end: REAL; mode: SET);
  1337. BEGIN
  1338. ctxt.DrawArc(x, y, r, start, end, mode);
  1339. END DrawArc;
  1340. (** draw rectangle in requested mode **)
  1341. PROCEDURE DrawRect* (ctxt: Context; x0, y0, x1, y1: REAL; mode: SET);
  1342. BEGIN
  1343. ctxt.DrawRect(x0, y0, x1, y1, mode);
  1344. END DrawRect;
  1345. (** draw circle in requested mode (clockwise if r > 0, counterclockwise if r < 0) **)
  1346. PROCEDURE DrawCircle* (ctxt: Context; x, y, r: REAL; mode: SET);
  1347. BEGIN
  1348. ctxt.DrawCircle(x, y, r, mode);
  1349. END DrawCircle;
  1350. (** draw ellipse in requested mode (clockwise if rx*ry > 0, counterclockwise if rx*ry < 0) **)
  1351. PROCEDURE DrawEllipse* (ctxt: Context; x, y, rx, ry: REAL; mode: SET);
  1352. BEGIN
  1353. ctxt.DrawEllipse(x, y, rx, ry, mode);
  1354. END DrawEllipse;
  1355. (** draw string at given coordinates and move current point to string end **)
  1356. PROCEDURE DrawStringAt* (ctxt: Context; x, y: REAL; str: ARRAY OF CHAR);
  1357. BEGIN
  1358. ctxt.DrawStringAt(x, y, str);
  1359. END DrawStringAt;
  1360. (** draw string at current point and move current point to string end **)
  1361. PROCEDURE DrawString* (ctxt: Context; str: ARRAY OF CHAR);
  1362. BEGIN
  1363. ctxt.DrawString(str);
  1364. END DrawString;
  1365. (**--- Images and Patterns ---**)
  1366. (** draw image at given point **)
  1367. PROCEDURE DrawImageAt* (ctxt: Context; x, y: REAL; img: GfxImages.Image; VAR filter: GfxImages.Filter);
  1368. BEGIN
  1369. ctxt.DrawImage(x, y, img, filter)
  1370. END DrawImageAt;
  1371. (** return new pattern **)
  1372. PROCEDURE NewPattern* (ctxt: Context; img: GfxImages.Image; px, py: REAL): Pattern;
  1373. BEGIN
  1374. RETURN ctxt.NewPattern(img, px, py)
  1375. END NewPattern;
  1376. (*--- Initialization of Standard Colors ---*)
  1377. PROCEDURE InitColors;
  1378. PROCEDURE init (VAR col: Color; r, g, b: INTEGER);
  1379. BEGIN
  1380. col.r := r; col.g := g; col.b := b; col.a := 255
  1381. END init;
  1382. BEGIN
  1383. 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);
  1384. init(Cyan, 0, 255, 255); init(Magenta, 255, 0, 255); init(Yellow, 255, 255, 0);
  1385. init(LGrey, 192, 192, 192); init(MGrey, 160, 160, 160); init(DGrey, 128, 128, 128)
  1386. END InitColors;
  1387. BEGIN
  1388. InitColors;
  1389. DefaultCap := ButtCap; DefaultJoin := MiterJoin
  1390. END Gfx.