SVGRenderer.Mod 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. MODULE SVGRenderer;
  2. IMPORT SVG, SVGColors, SVGGradients, SVGFilters, SVGUtilities, XMLObjects,
  3. Gfx, GfxBuffer, GfxPaths, GfxImages, GfxMatrix, Math, Raster;
  4. TYPE
  5. RenderTargetPool=OBJECT
  6. VAR list: XMLObjects.ArrayCollection; (* container for pooled render targets *)
  7. PROCEDURE &New*;
  8. BEGIN
  9. NEW(list)
  10. END New;
  11. (* Allocate an unused or new document *)
  12. PROCEDURE Alloc*(VAR doc: SVG.Document; width, height: LONGINT);
  13. VAR p: ANY;
  14. BEGIN
  15. IF list.GetNumberOfElements()=0 THEN
  16. doc := SVG.NewDocument(width, height);
  17. ELSE
  18. p := list.GetElement(list.GetNumberOfElements()-1);
  19. doc := p(SVG.Document);
  20. list.Remove(doc);
  21. Raster.Clear(doc);
  22. END
  23. END Alloc;
  24. (* Add doc to pool *)
  25. PROCEDURE Free*(doc: SVG.Document);
  26. BEGIN
  27. list.Add(doc)
  28. END Free;
  29. END RenderTargetPool;
  30. Renderer*=OBJECT
  31. VAR
  32. gradients*: SVGGradients.GradientDict; (* container of all defined gradients *)
  33. filters*: SVGFilters.FilterDict; (* container of all defined filters *)
  34. ctxt: GfxBuffer.Context; (* context of the graphics library *)
  35. mode: SET; (* drawing mode for the graphics library (filled, stroked, ...) *)
  36. rasterMode: Raster.Mode; (* mode for the raster library (srcOverDst) *)
  37. filterStack: SVGFilters.FilterStack; (* stack of active filters *)
  38. renderTarget: SVG.Document; (* current render target *)
  39. renderTargetPool: RenderTargetPool; (* pool of unused render targets *)
  40. PROCEDURE &New*;
  41. BEGIN
  42. NEW(gradients);
  43. NEW(filters);
  44. NEW(filterStack);
  45. NEW(renderTargetPool);
  46. Raster.InitMode(rasterMode, Raster.srcOverDst);
  47. END New;
  48. (* Fill target white *)
  49. PROCEDURE FillWhite*(state: SVG.State);
  50. VAR
  51. white: Raster.Pixel;
  52. copyMode: Raster.Mode;
  53. BEGIN
  54. Raster.SetRGB(white,0FFH,0FFH,0FFH);
  55. Raster.InitMode(copyMode, Raster.srcCopy);
  56. Raster.Fill(state.target, 0, 0, state.target.width, state.target.height, white, copyMode)
  57. END FillWhite;
  58. (* Prepare for rendering *)
  59. PROCEDURE RenderBegin(state: SVG.State; recordPathMode: BOOLEAN);
  60. BEGIN
  61. NEW(ctxt);
  62. IF state.style.stroke.type = SVG.PaintURI THEN state.transparencyUsed := TRUE END;
  63. IF state.style.fill.type = SVG.PaintURI THEN state.transparencyUsed := TRUE END;
  64. IF state.transparencyUsed THEN
  65. renderTargetPool.Alloc(renderTarget, state.target.width, state.target.height);
  66. GfxBuffer.Init(ctxt, renderTarget);
  67. ELSE
  68. GfxBuffer.Init(ctxt, state.target);
  69. END;
  70. IF recordPathMode THEN
  71. mode := {Gfx.Record};
  72. ELSE
  73. mode := {};
  74. SetMatrix(state.userToWorldSpace,ctxt);
  75. END;
  76. END RenderBegin;
  77. (* Finalize the rendering *)
  78. PROCEDURE RenderEnd(state: SVG.State; recordPathMode: BOOLEAN);
  79. VAR pattern: Gfx.Pattern;
  80. worldBBox, objectBBox: SVG.Box;
  81. minx, miny, maxx, maxy: LONGINT;
  82. strokeWidth: SVG.Length;
  83. BEGIN
  84. IF recordPathMode THEN
  85. mode:={};
  86. (* Calculate bounding box of path *)
  87. GetBBoxes(ctxt.path, state.userToWorldSpace, worldBBox, objectBBox);
  88. (* Calculate and set stroke width *)
  89. strokeWidth := state.userToWorldSpace.TransformLength(state.style.strokeWidth);
  90. Gfx.SetLineWidth(ctxt, SHORT(strokeWidth));
  91. (* Enlarge bounding box for thick strokes *)
  92. worldBBox.x := worldBBox.x - strokeWidth;
  93. worldBBox.y := worldBBox.y - strokeWidth;
  94. worldBBox.width := worldBBox.width + 2*strokeWidth;
  95. worldBBox.height := worldBBox.height + 2*strokeWidth;
  96. (* Set the stroke style *)
  97. IF state.style.stroke.type = SVG.PaintColor THEN
  98. Gfx.SetStrokeColor(ctxt, GetColor(state.style.stroke.color));
  99. mode := mode + {Gfx.Stroke}
  100. ELSIF state.style.stroke.type = SVG.PaintURI THEN
  101. pattern := gradients.GetGradientAsPattern(ctxt, state.style.stroke.uri, worldBBox, objectBBox, state.userToWorldSpace, state.viewport);
  102. IF pattern#NIL THEN
  103. Gfx.SetFillPattern(ctxt, pattern);
  104. mode := mode + {Gfx.Stroke}
  105. END
  106. END;
  107. (* Set the fill style *)
  108. IF state.style.fill.type = SVG.PaintColor THEN
  109. Gfx.SetFillColor(ctxt, GetColor(state.style.fill.color));
  110. mode := mode + {Gfx.Fill}
  111. ELSIF state.style.fill.type = SVG.PaintURI THEN
  112. pattern := gradients.GetGradientAsPattern(ctxt, state.style.fill.uri, worldBBox, objectBBox, state.userToWorldSpace, state.viewport);
  113. IF pattern#NIL THEN
  114. Gfx.SetFillPattern(ctxt, pattern);
  115. mode := mode + {Gfx.Fill}
  116. END
  117. END;
  118. (* Render the recorded path *)
  119. Gfx.Render(ctxt, mode)
  120. ELSE
  121. (* Can not calculate bounding box of unrecorded path -> assume it is as large as the target *)
  122. worldBBox.x := 0;
  123. worldBBox.y := 0;
  124. worldBBox.width := state.target.width-1;
  125. worldBBox.height := state.target.height-1;
  126. END;
  127. IF state.transparencyUsed THEN
  128. minx := ENTIER(worldBBox.x);
  129. miny := ENTIER(worldBBox.y);
  130. maxx := ENTIER(worldBBox.x+worldBBox.width)+1;
  131. maxy := ENTIER(worldBBox.y+worldBBox.height)+1;
  132. IF minx<ENTIER(state.viewport.x) THEN minx := ENTIER(state.viewport.x) END;
  133. IF miny<ENTIER(state.viewport.y) THEN miny := ENTIER(state.viewport.y) END;
  134. IF maxx>ENTIER(state.viewport.x+state.viewport.width) THEN maxx := ENTIER(state.viewport.x+state.viewport.width) END;
  135. IF maxy>ENTIER(state.viewport.y+state.viewport.height) THEN maxy :=ENTIER(state. viewport.y+state.viewport.height) END;
  136. IF (minx<=maxx) & (miny<=maxy) THEN
  137. Raster.Copy(renderTarget, state.target, minx, miny, maxx, maxy, minx, miny, rasterMode)
  138. END;
  139. renderTargetPool.Free(renderTarget)
  140. END
  141. END RenderEnd;
  142. (* Prepare use of a filter *)
  143. PROCEDURE BeginFilter*(filter: SVGFilters.Filter; state: SVG.State);
  144. BEGIN
  145. filterStack.Push(filter);
  146. IF filter#NIL THEN
  147. state.Push();
  148. state.target := SVG.NewDocument(state.target.width, state.target.height);
  149. END
  150. END BeginFilter;
  151. (* Finalize use of a filter *)
  152. PROCEDURE EndFilter*(state: SVG.State);
  153. VAR filterTarget: SVG.Document;
  154. filter: SVGFilters.Filter;
  155. BEGIN
  156. filterStack.Pop(filter);
  157. IF filter#NIL THEN
  158. filterTarget := state.target;
  159. state.Pop();
  160. filter.Apply(filterTarget, state.target);
  161. END
  162. END EndFilter;
  163. (* Draw a cubic bezier curve *)
  164. PROCEDURE Bezier3To(current, bezier1, bezier2: SVG.Coordinate);
  165. BEGIN
  166. Gfx.BezierTo (ctxt,
  167. SHORT(current.x), SHORT(current.y),
  168. SHORT(bezier1.x), SHORT(bezier1.y),
  169. SHORT(bezier2.x), SHORT(bezier2.y));
  170. END Bezier3To;
  171. (* Draw a quadrativ bezier curve *)
  172. PROCEDURE Bezier2To(start, bezier, end: SVG.Coordinate);
  173. VAR bezier1, bezier2: SVG.Coordinate;
  174. BEGIN
  175. bezier1.x := (bezier.x-start.x)*2.0/3.0+start.x;
  176. bezier1.y := (bezier.y-start.y)*2.0/3.0+start.y;
  177. bezier2.x := end.x-(end.x-bezier.x)*2.0/3.0;
  178. bezier2.y := end.y-(end.y-bezier.y)*2.0/3.0;
  179. Bezier3To(end, bezier1, bezier2);
  180. END Bezier2To;
  181. (* Draw an arc *)
  182. PROCEDURE ArcTo(radius, flags, start, end: SVG.Coordinate; xrot: SVG.Length);
  183. VAR cos, sin, rx2, ry2, tmp: SVG.Length;
  184. diff0, diff, center0, center, d1, d2: SVG.Coordinate;
  185. BEGIN
  186. (* Interpret out-of-range parameters *)
  187. IF (start.x=end.x) & (start.y=end.y) THEN RETURN END;
  188. IF (radius.x=0) OR (radius.y=0) THEN
  189. Gfx.LineTo(ctxt, SHORT(end.x), SHORT(end.y));
  190. RETURN
  191. END;
  192. radius.x := ABS(radius.x);
  193. radius.y := ABS(radius.y);
  194. IF flags.x#0 THEN flags.x := 1.0 END;
  195. IF flags.y#0 THEN flags.y := 1.0 END;
  196. (* Calculate center *)
  197. cos := Math.cos(SHORT(xrot/180.0*Math.pi));
  198. sin := Math.sin(SHORT(xrot/180.0*Math.pi));
  199. diff.x := (start.x-end.x)/2;
  200. diff.y := (start.y-end.y)/2;
  201. diff0.x := cos*diff.x+sin*diff.y;
  202. diff0.y := -sin*diff.x+cos*diff.y;
  203. tmp := diff0.x*diff0.x/(radius.x*radius.x)+diff0.y*diff0.y/(radius.y*radius.y);
  204. IF tmp > 1 THEN
  205. tmp := Math.sqrt(SHORT(tmp));
  206. radius.x := tmp*radius.x;
  207. radius.y := tmp*radius.y;
  208. tmp := 0;
  209. ELSE
  210. rx2 := radius.x*radius.x;
  211. ry2 := radius.y*radius.y;
  212. tmp := rx2*diff0.y*diff0.y+ry2*diff0.x*diff0.x;
  213. tmp := (rx2*ry2-tmp)/tmp;
  214. IF tmp <= 0 THEN tmp := 0 END;
  215. tmp := Math.sqrt(SHORT(tmp));
  216. IF flags.x=flags.y THEN tmp := -tmp END;
  217. END;
  218. center0.x := tmp*radius.x*diff0.y/radius.y;
  219. center0.y := -tmp*radius.y*diff0.x/radius.x;
  220. center.x := cos*center0.x-sin*center0.y+(start.x+end.x)/2;
  221. center.y := sin*center0.x+cos*center0.y+(start.y+end.y)/2;
  222. (* Calculate conjugate diameter pair *)
  223. d1.x := center.x+radius.x*cos;
  224. d1.y := center.y+radius.x*sin;
  225. d2.x := center.x-radius.y*sin;
  226. d2.y := center.y+radius.y*cos;
  227. IF (flags.y = 0.0) = (d1.x*d2.y-d1.y*d2.x > 0.0) THEN
  228. tmp := d1.x;
  229. d1.x := d2.x;
  230. d2.x := tmp;
  231. tmp := d1.y;
  232. d1.y := d2.y;
  233. d2.y := tmp;
  234. END;
  235. (* Draw arc *)
  236. Gfx.ArcTo(ctxt,
  237. SHORT(end.x), SHORT(end.y),
  238. SHORT(center.x), SHORT(center.y),
  239. SHORT(d1.x), SHORT(d1.y),
  240. SHORT(d2.x), SHORT(d2.y))
  241. END ArcTo;
  242. (* Render an image *)
  243. PROCEDURE RenderImage*(x, y, width, height: SVG.Length; image: SVG.Document; state: SVG.State);
  244. VAR filter: GfxImages.Filter;
  245. BEGIN
  246. GfxImages.InitLinearFilter(filter);
  247. state.userToWorldSpace := state.userToWorldSpace.Translate(-x, -y);
  248. state.userToWorldSpace := state.userToWorldSpace.Scale(width / image.width, height / image.height);
  249. x := x*image.width / width;
  250. y := y*image.height / height;
  251. state.userToWorldSpace := state.userToWorldSpace.Translate(x, y);
  252. (* Gfx cannot record calls to DrawImageAt *)
  253. RenderBegin(state, FALSE);
  254. Gfx.DrawImageAt(ctxt, SHORT(x), SHORT(y), image, filter);
  255. RenderEnd(state, FALSE);
  256. END RenderImage;
  257. (* Render a rectangle*)
  258. PROCEDURE RenderRect*(x, y, width, height: SVG.Length; state: SVG.State);
  259. BEGIN
  260. RenderBegin(state,TRUE);
  261. Gfx.DrawRect(ctxt, SHORT(x), SHORT(y), SHORT(x+width), SHORT(y+height), mode);
  262. RenderEnd(state, TRUE);
  263. END RenderRect;
  264. (* Render a rounded rectangle *)
  265. PROCEDURE RenderRoundedRect*(x, y, width, height, rx, ry: SVG.Length; state: SVG.State);
  266. BEGIN
  267. RenderBegin(state, TRUE);
  268. IF rx>width/2 THEN rx := width/2 END;
  269. IF ry>height/2 THEN ry := height/2 END;
  270. Gfx.Begin(ctxt, mode);
  271. Gfx.MoveTo(ctxt, SHORT(x+width/2), SHORT(y));
  272. Gfx.LineTo(ctxt, SHORT(x+width-rx), SHORT(y));
  273. Gfx.ArcTo(ctxt,SHORT(x+width), SHORT(y+ry),
  274. SHORT(x+width-rx), SHORT(y+ry),
  275. SHORT(x+width-rx), SHORT(y),
  276. SHORT(x+width), SHORT(y+ry));
  277. Gfx.LineTo(ctxt, SHORT(x+width), SHORT(y+height-ry));
  278. Gfx.ArcTo(ctxt,SHORT(x+width-rx), SHORT(y+height),
  279. SHORT(x+width-rx), SHORT(y+height-ry),
  280. SHORT(x+width), SHORT(y+height-ry),
  281. SHORT(x+width-rx), SHORT(y+height));
  282. Gfx.LineTo(ctxt, SHORT(x+rx), SHORT(y+height));
  283. Gfx.ArcTo(ctxt,SHORT(x), SHORT(y+height-ry),
  284. SHORT(x+rx), SHORT(y+height-ry),
  285. SHORT(x+rx), SHORT(y+height),
  286. SHORT(x), SHORT(y+height-ry));
  287. Gfx.LineTo(ctxt, SHORT(x), SHORT(y+ry));
  288. Gfx.ArcTo(ctxt,SHORT(x+rx), SHORT(y),
  289. SHORT(x+rx), SHORT(y+ry),
  290. SHORT(x), SHORT(y+ry),
  291. SHORT(x+rx), SHORT(y));
  292. Gfx.Close(ctxt);
  293. Gfx.End(ctxt);
  294. RenderEnd(state, TRUE);
  295. END RenderRoundedRect;
  296. (* Render a circle *)
  297. PROCEDURE RenderCircle*(x, y, r: SVG.Length; state: SVG.State);
  298. BEGIN
  299. RenderBegin(state, TRUE);
  300. Gfx.DrawCircle(ctxt, SHORT(x), SHORT(y), SHORT(r), mode);
  301. RenderEnd(state, TRUE);
  302. END RenderCircle;
  303. (* Render an ellipse *)
  304. PROCEDURE RenderEllipse*(x, y, rx, ry: SVG.Length; state: SVG.State);
  305. BEGIN
  306. RenderBegin(state, TRUE);
  307. Gfx.DrawEllipse(ctxt, SHORT(x), SHORT(y), SHORT(rx), SHORT(ry), mode);
  308. RenderEnd(state, TRUE);
  309. END RenderEllipse;
  310. (* Render a line *)
  311. PROCEDURE RenderLine*(x1, y1, x2, y2: SVG.Length; state: SVG.State);
  312. BEGIN
  313. RenderBegin(state, TRUE);
  314. Gfx.DrawLine(ctxt, SHORT(x1), SHORT(y1), SHORT(x2), SHORT(y2), mode-{Gfx.Fill, Gfx.Clip, Gfx.EvenOdd});
  315. RenderEnd(state, TRUE);
  316. END RenderLine;
  317. (* Render an open polyline or a closed polygon *)
  318. PROCEDURE RenderPoly*(points: SVG.String; closed: BOOLEAN; state: SVG.State);
  319. VAR i: SIZE;
  320. current: SVG.Coordinate;
  321. BEGIN
  322. RenderBegin(state, TRUE);
  323. Gfx.Begin(ctxt, mode);
  324. i := 0;
  325. SVGUtilities.SkipWhiteSpace(i, points);
  326. SVG.ParseCoordinate(points, i, current, FALSE);
  327. SVGUtilities.SkipWhiteSpace(i, points);
  328. Gfx.MoveTo(ctxt, SHORT(current.x), SHORT(current.y));
  329. WHILE points[i] # 0X DO
  330. SVG.ParseCoordinate(points, i, current, FALSE);
  331. SVGUtilities.SkipWhiteSpace(i, points);
  332. Gfx.LineTo(ctxt, SHORT(current.x), SHORT(current.y));
  333. END;
  334. IF closed THEN
  335. Gfx.Close(ctxt);
  336. END;
  337. Gfx.End(ctxt);
  338. RenderEnd(state, TRUE);
  339. END RenderPoly;
  340. (* Render a general path element *)
  341. PROCEDURE RenderPath*(d: SVG.String; state: SVG.State);
  342. VAR i: SIZE;
  343. subPathStart, current, last: SVG.Coordinate;
  344. relative: BOOLEAN;
  345. command, prevCommand: CHAR;
  346. arcR, arcFlags: SVG.Coordinate;
  347. arcAngle: SVG.Length;
  348. bezier1, bezier2: SVG.Coordinate;
  349. BEGIN
  350. RenderBegin(state, TRUE);
  351. Gfx.Begin(ctxt, mode);
  352. current.x := 0;
  353. current.y := 0;
  354. command := 0X;
  355. i := 0;
  356. SVGUtilities.SkipWhiteSpace(i, d);
  357. IF (d[i] # 'm') & (d[i] # 'M') THEN
  358. SVG.Error("PathData error: missing moveto")
  359. END;
  360. WHILE d[i] # 0X DO
  361. SVGUtilities.SkipWhiteSpace(i, d);
  362. prevCommand := command;
  363. last := current;
  364. IF SVGUtilities.IsAlpha(d[i]) THEN
  365. relative := SVGUtilities.IsLowercase(d[i]);
  366. command := d[i];
  367. INC(i);
  368. SVGUtilities.SkipWhiteSpace(i, d);
  369. END;
  370. CASE command OF
  371. 'm', 'M':
  372. SVG.ParseCoordinate(d, i, current, relative);
  373. subPathStart := current;
  374. Gfx.MoveTo(ctxt, SHORT(current.x), SHORT(current.y));
  375. | 'z', 'Z':
  376. current := subPathStart;
  377. Gfx.Close(ctxt);
  378. | 'l', 'L':
  379. SVG.ParseCoordinate(d, i, current, relative);
  380. Gfx.LineTo(ctxt, SHORT(current.x), SHORT(current.y));
  381. | 'h', 'H':
  382. SVG.ParseCoordinate1(d, i, current.x, relative);
  383. Gfx.LineTo(ctxt, SHORT(current.x), SHORT(current.y));
  384. | 'v', 'V':
  385. SVG.ParseCoordinate1(d, i, current.y, relative);
  386. Gfx.LineTo(ctxt, SHORT(current.x), SHORT(current.y));
  387. | 'c', 'C':
  388. bezier1:=current;
  389. bezier2:=current;
  390. SVG.ParseCoordinate(d, i, bezier1, relative);
  391. SVG.ParseCoordinate(d, i, bezier2, relative);
  392. SVG.ParseCoordinate(d, i, current, relative);
  393. Bezier3To(current, bezier1, bezier2);
  394. | 's', 'S':
  395. CASE prevCommand OF
  396. 's','S','c','C':
  397. bezier1.x:=current.x-(bezier2.x-current.x);
  398. bezier1.y:=current.y-(bezier2.y-current.y);
  399. ELSE
  400. bezier1:=current;
  401. END;
  402. bezier2:=current;
  403. SVG.ParseCoordinate(d, i, bezier2, relative);
  404. SVG.ParseCoordinate(d, i, current, relative);
  405. Bezier3To(current, bezier1, bezier2);
  406. | 'q', 'Q':
  407. bezier1:=current;
  408. SVG.ParseCoordinate(d, i, bezier1, relative);
  409. SVG.ParseCoordinate(d, i, current, relative);
  410. Bezier2To(last, bezier1, current);
  411. | 't', 'T':
  412. CASE prevCommand OF
  413. 't','T','q','Q':
  414. bezier1.x:=current.x-(bezier1.x-current.x);
  415. bezier1.y:=current.y-(bezier1.y-current.y);
  416. ELSE
  417. bezier1:=current;
  418. END;
  419. SVG.ParseCoordinate(d, i, current, relative);
  420. Bezier2To(last, bezier1, current);
  421. | 'a', 'A':
  422. SVG.ParseCoordinate(d, i, arcR, FALSE);
  423. SVG.ParseCoordinate1(d, i, arcAngle, FALSE);
  424. SVG.ParseCoordinate(d, i, arcFlags, FALSE);
  425. SVG.ParseCoordinate(d, i, current, relative);
  426. ArcTo(arcR,arcFlags,last,current,arcAngle);
  427. ELSE
  428. SVG.Error("PathData error: unknown command");
  429. d[i] := 0X;
  430. END;
  431. SVGUtilities.SkipWhiteSpace(i, d)
  432. END;
  433. Gfx.End(ctxt);
  434. RenderEnd(state, TRUE);
  435. END RenderPath;
  436. END Renderer;
  437. (* Convert SVG.Color to Gfx.Color *)
  438. PROCEDURE GetColor(color: SVG.Color): Gfx.Color;
  439. VAR c: Gfx.Color;
  440. BEGIN
  441. SVGColors.Split(color, c.r, c.g, c.b, c.a);
  442. RETURN c
  443. END GetColor;
  444. (* Calculate the bounding box of a path in worldspace and in object space. Also transform the path to worldspace *)
  445. PROCEDURE GetBBoxes(path: GfxPaths.Path; objectToWorldSpace: SVG.Transform; VAR worldBBox, objectBBox: SVG.Box);
  446. VAR
  447. x1World, y1World, x2World, y2World: REAL;
  448. x1Object, y1Object, x2Object, y2Object: REAL;
  449. mat: GfxMatrix.Matrix;
  450. BEGIN
  451. GfxMatrix.Init(mat, SHORT(objectToWorldSpace.a), SHORT(objectToWorldSpace.b),
  452. SHORT(objectToWorldSpace.c), SHORT(objectToWorldSpace.d),
  453. SHORT(objectToWorldSpace.e), SHORT(objectToWorldSpace.f));
  454. GfxPaths.GetBox(path, x1Object, y1Object, x2Object, y2Object);
  455. objectBBox.x:=x1Object;
  456. objectBBox.y:=y1Object;
  457. objectBBox.width :=x2Object-x1Object;
  458. objectBBox.height :=y2Object-y1Object;
  459. GfxMatrix.ApplyToRect(mat, x1Object, y1Object, x2Object, y2Object, x1World, y1World, x2World, y2World);
  460. worldBBox.x := x1World;
  461. worldBBox.y := y1World;
  462. worldBBox.width := x2World-x1World;
  463. worldBBox.height := y2World-y1World;
  464. GfxPaths.Apply(path, mat);
  465. END GetBBoxes;
  466. (* Set the matrix to transform to world space. *)
  467. PROCEDURE SetMatrix(objectToWorldSpace: SVG.Transform; ctxt: GfxBuffer.Context);
  468. VAR mat: GfxMatrix.Matrix;
  469. BEGIN
  470. GfxMatrix.Init(mat, SHORT(objectToWorldSpace.a), SHORT(objectToWorldSpace.b),
  471. SHORT(objectToWorldSpace.c), SHORT(objectToWorldSpace.d),
  472. SHORT(objectToWorldSpace.e), SHORT(objectToWorldSpace.f));
  473. Gfx.SetCTM(ctxt, mat);
  474. END SetMatrix;
  475. END SVGRenderer.