PageRenderTime 93ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 1ms

/MagickCore/draw.c

https://gitlab.com/ImageMagick/ImageMagick
C | 7487 lines | 6298 code | 331 blank | 858 comment | 1382 complexity | 21ae1370ce1858972c291d10c68a9f34 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /*
  2. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  3. % %
  4. % %
  5. % %
  6. % DDDD RRRR AAA W W %
  7. % D D R R A A W W %
  8. % D D RRRR AAAAA W W W %
  9. % D D R RN A A WW WW %
  10. % DDDD R R A A W W %
  11. % %
  12. % %
  13. % MagickCore Image Drawing Methods %
  14. % %
  15. % %
  16. % Software Design %
  17. % Cristy %
  18. % July 1998 %
  19. % %
  20. % %
  21. % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
  22. % dedicated to making software imaging solutions freely available. %
  23. % %
  24. % You may not use this file except in compliance with the License. You may %
  25. % obtain a copy of the License at %
  26. % %
  27. % https://imagemagick.org/script/license.php %
  28. % %
  29. % Unless required by applicable law or agreed to in writing, software %
  30. % distributed under the License is distributed on an "AS IS" BASIS, %
  31. % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
  32. % See the License for the specific language governing permissions and %
  33. % limitations under the License. %
  34. % %
  35. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  36. %
  37. % Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon
  38. % rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion",
  39. % Graphics Gems, 1990. Leonard Rosenthal and David Harr of Appligent
  40. % (www.appligent.com) contributed the dash pattern, linecap stroking
  41. % algorithm, and minor rendering improvements.
  42. %
  43. */
  44. /*
  45. Include declarations.
  46. */
  47. #include "MagickCore/studio.h"
  48. #include "MagickCore/annotate.h"
  49. #include "MagickCore/artifact.h"
  50. #include "MagickCore/blob.h"
  51. #include "MagickCore/cache.h"
  52. #include "MagickCore/cache-private.h"
  53. #include "MagickCore/cache-view.h"
  54. #include "MagickCore/channel.h"
  55. #include "MagickCore/color.h"
  56. #include "MagickCore/colorspace-private.h"
  57. #include "MagickCore/composite.h"
  58. #include "MagickCore/composite-private.h"
  59. #include "MagickCore/constitute.h"
  60. #include "MagickCore/draw.h"
  61. #include "MagickCore/draw-private.h"
  62. #include "MagickCore/enhance.h"
  63. #include "MagickCore/exception.h"
  64. #include "MagickCore/exception-private.h"
  65. #include "MagickCore/gem.h"
  66. #include "MagickCore/geometry.h"
  67. #include "MagickCore/image-private.h"
  68. #include "MagickCore/list.h"
  69. #include "MagickCore/log.h"
  70. #include "MagickCore/memory-private.h"
  71. #include "MagickCore/monitor.h"
  72. #include "MagickCore/monitor-private.h"
  73. #include "MagickCore/option.h"
  74. #include "MagickCore/paint.h"
  75. #include "MagickCore/pixel-accessor.h"
  76. #include "MagickCore/pixel-private.h"
  77. #include "MagickCore/property.h"
  78. #include "MagickCore/resample.h"
  79. #include "MagickCore/resample-private.h"
  80. #include "MagickCore/resource_.h"
  81. #include "MagickCore/splay-tree.h"
  82. #include "MagickCore/string_.h"
  83. #include "MagickCore/string-private.h"
  84. #include "MagickCore/thread-private.h"
  85. #include "MagickCore/token.h"
  86. #include "MagickCore/transform-private.h"
  87. #include "MagickCore/utility.h"
  88. /*
  89. Define declarations.
  90. */
  91. #define BezierQuantum 200
  92. #define PrimitiveExtentPad 2048
  93. #define MaxBezierCoordinates 4194304
  94. #define ThrowPointExpectedException(token,exception) \
  95. { \
  96. (void) ThrowMagickException(exception,GetMagickModule(),DrawError, \
  97. "NonconformingDrawingPrimitiveDefinition","`%s'",token); \
  98. status=MagickFalse; \
  99. break; \
  100. }
  101. /*
  102. Typedef declarations.
  103. */
  104. typedef struct _EdgeInfo
  105. {
  106. SegmentInfo
  107. bounds;
  108. double
  109. scanline;
  110. PointInfo
  111. *points;
  112. size_t
  113. number_points;
  114. ssize_t
  115. direction;
  116. MagickBooleanType
  117. ghostline;
  118. size_t
  119. highwater;
  120. } EdgeInfo;
  121. typedef struct _ElementInfo
  122. {
  123. double
  124. cx,
  125. cy,
  126. major,
  127. minor,
  128. angle;
  129. } ElementInfo;
  130. typedef struct _MVGInfo
  131. {
  132. PrimitiveInfo
  133. **primitive_info;
  134. size_t
  135. *extent;
  136. ssize_t
  137. offset;
  138. PointInfo
  139. point;
  140. ExceptionInfo
  141. *exception;
  142. } MVGInfo;
  143. typedef struct _PolygonInfo
  144. {
  145. EdgeInfo
  146. *edges;
  147. size_t
  148. number_edges;
  149. } PolygonInfo;
  150. typedef enum
  151. {
  152. MoveToCode,
  153. OpenCode,
  154. GhostlineCode,
  155. LineToCode,
  156. EndCode
  157. } PathInfoCode;
  158. typedef struct _PathInfo
  159. {
  160. PointInfo
  161. point;
  162. PathInfoCode
  163. code;
  164. } PathInfo;
  165. /*
  166. Forward declarations.
  167. */
  168. static Image
  169. *DrawClippingMask(Image *,const DrawInfo *,const char *,const char *,
  170. ExceptionInfo *);
  171. static MagickBooleanType
  172. DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
  173. ExceptionInfo *),
  174. RenderMVGContent(Image *,const DrawInfo *,const size_t,ExceptionInfo *),
  175. TraceArc(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
  176. TraceArcPath(MVGInfo *,const PointInfo,const PointInfo,const PointInfo,
  177. const double,const MagickBooleanType,const MagickBooleanType),
  178. TraceBezier(MVGInfo *,const size_t),
  179. TraceCircle(MVGInfo *,const PointInfo,const PointInfo),
  180. TraceEllipse(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
  181. TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
  182. TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
  183. TraceRoundRectangle(MVGInfo *,const PointInfo,const PointInfo,PointInfo),
  184. TraceSquareLinecap(PrimitiveInfo *,const size_t,const double);
  185. static PrimitiveInfo
  186. *TraceStrokePolygon(const Image *,const DrawInfo *,const PrimitiveInfo *);
  187. static size_t
  188. TracePath(MVGInfo *,const char *,ExceptionInfo *);
  189. /*
  190. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  191. % %
  192. % %
  193. % %
  194. % A c q u i r e D r a w I n f o %
  195. % %
  196. % %
  197. % %
  198. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  199. %
  200. % AcquireDrawInfo() returns a DrawInfo structure properly initialized.
  201. %
  202. % The format of the AcquireDrawInfo method is:
  203. %
  204. % DrawInfo *AcquireDrawInfo(void)
  205. %
  206. */
  207. MagickExport DrawInfo *AcquireDrawInfo(void)
  208. {
  209. DrawInfo
  210. *draw_info;
  211. draw_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*draw_info));
  212. GetDrawInfo((ImageInfo *) NULL,draw_info);
  213. return(draw_info);
  214. }
  215. /*
  216. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  217. % %
  218. % %
  219. % %
  220. % C l o n e D r a w I n f o %
  221. % %
  222. % %
  223. % %
  224. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  225. %
  226. % CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
  227. % is specified, a new DrawInfo structure is created initialized to default
  228. % values.
  229. %
  230. % The format of the CloneDrawInfo method is:
  231. %
  232. % DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
  233. % const DrawInfo *draw_info)
  234. %
  235. % A description of each parameter follows:
  236. %
  237. % o image_info: the image info.
  238. %
  239. % o draw_info: the draw info.
  240. %
  241. */
  242. MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
  243. const DrawInfo *draw_info)
  244. {
  245. DrawInfo
  246. *clone_info;
  247. ExceptionInfo
  248. *exception;
  249. clone_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*clone_info));
  250. GetDrawInfo(image_info,clone_info);
  251. if (draw_info == (DrawInfo *) NULL)
  252. return(clone_info);
  253. exception=AcquireExceptionInfo();
  254. if (draw_info->primitive != (char *) NULL)
  255. (void) CloneString(&clone_info->primitive,draw_info->primitive);
  256. if (draw_info->geometry != (char *) NULL)
  257. (void) CloneString(&clone_info->geometry,draw_info->geometry);
  258. clone_info->compliance=draw_info->compliance;
  259. clone_info->viewbox=draw_info->viewbox;
  260. clone_info->affine=draw_info->affine;
  261. clone_info->gravity=draw_info->gravity;
  262. clone_info->fill=draw_info->fill;
  263. clone_info->stroke=draw_info->stroke;
  264. clone_info->stroke_width=draw_info->stroke_width;
  265. if (draw_info->fill_pattern != (Image *) NULL)
  266. clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
  267. exception);
  268. if (draw_info->stroke_pattern != (Image *) NULL)
  269. clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
  270. MagickTrue,exception);
  271. clone_info->stroke_antialias=draw_info->stroke_antialias;
  272. clone_info->text_antialias=draw_info->text_antialias;
  273. clone_info->fill_rule=draw_info->fill_rule;
  274. clone_info->linecap=draw_info->linecap;
  275. clone_info->linejoin=draw_info->linejoin;
  276. clone_info->miterlimit=draw_info->miterlimit;
  277. clone_info->dash_offset=draw_info->dash_offset;
  278. clone_info->decorate=draw_info->decorate;
  279. clone_info->compose=draw_info->compose;
  280. if (draw_info->text != (char *) NULL)
  281. (void) CloneString(&clone_info->text,draw_info->text);
  282. if (draw_info->font != (char *) NULL)
  283. (void) CloneString(&clone_info->font,draw_info->font);
  284. if (draw_info->metrics != (char *) NULL)
  285. (void) CloneString(&clone_info->metrics,draw_info->metrics);
  286. if (draw_info->family != (char *) NULL)
  287. (void) CloneString(&clone_info->family,draw_info->family);
  288. clone_info->style=draw_info->style;
  289. clone_info->stretch=draw_info->stretch;
  290. clone_info->weight=draw_info->weight;
  291. if (draw_info->encoding != (char *) NULL)
  292. (void) CloneString(&clone_info->encoding,draw_info->encoding);
  293. clone_info->pointsize=draw_info->pointsize;
  294. clone_info->kerning=draw_info->kerning;
  295. clone_info->interline_spacing=draw_info->interline_spacing;
  296. clone_info->interword_spacing=draw_info->interword_spacing;
  297. clone_info->direction=draw_info->direction;
  298. if (draw_info->density != (char *) NULL)
  299. (void) CloneString(&clone_info->density,draw_info->density);
  300. clone_info->align=draw_info->align;
  301. clone_info->undercolor=draw_info->undercolor;
  302. clone_info->border_color=draw_info->border_color;
  303. if (draw_info->server_name != (char *) NULL)
  304. (void) CloneString(&clone_info->server_name,draw_info->server_name);
  305. if (draw_info->dash_pattern != (double *) NULL)
  306. {
  307. register ssize_t
  308. x;
  309. for (x=0; fabs(draw_info->dash_pattern[x]) >= MagickEpsilon; x++) ;
  310. clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) (2*x+2),
  311. sizeof(*clone_info->dash_pattern));
  312. if (clone_info->dash_pattern == (double *) NULL)
  313. ThrowFatalException(ResourceLimitFatalError,
  314. "UnableToAllocateDashPattern");
  315. (void) memset(clone_info->dash_pattern,0,(size_t) (2*x+2)*
  316. sizeof(*clone_info->dash_pattern));
  317. (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(size_t)
  318. (x+1)*sizeof(*clone_info->dash_pattern));
  319. }
  320. clone_info->gradient=draw_info->gradient;
  321. if (draw_info->gradient.stops != (StopInfo *) NULL)
  322. {
  323. size_t
  324. number_stops;
  325. number_stops=clone_info->gradient.number_stops;
  326. clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
  327. number_stops,sizeof(*clone_info->gradient.stops));
  328. if (clone_info->gradient.stops == (StopInfo *) NULL)
  329. ThrowFatalException(ResourceLimitFatalError,
  330. "UnableToAllocateDashPattern");
  331. (void) memcpy(clone_info->gradient.stops,draw_info->gradient.stops,
  332. (size_t) number_stops*sizeof(*clone_info->gradient.stops));
  333. }
  334. clone_info->bounds=draw_info->bounds;
  335. clone_info->fill_alpha=draw_info->fill_alpha;
  336. clone_info->stroke_alpha=draw_info->stroke_alpha;
  337. clone_info->element_reference=draw_info->element_reference;
  338. clone_info->clip_path=draw_info->clip_path;
  339. clone_info->clip_units=draw_info->clip_units;
  340. if (draw_info->clip_mask != (char *) NULL)
  341. (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
  342. if (draw_info->clipping_mask != (Image *) NULL)
  343. clone_info->clipping_mask=CloneImage(draw_info->clipping_mask,0,0,
  344. MagickTrue,exception);
  345. if (draw_info->composite_mask != (Image *) NULL)
  346. clone_info->composite_mask=CloneImage(draw_info->composite_mask,0,0,
  347. MagickTrue,exception);
  348. clone_info->render=draw_info->render;
  349. clone_info->debug=IsEventLogging();
  350. exception=DestroyExceptionInfo(exception);
  351. return(clone_info);
  352. }
  353. /*
  354. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  355. % %
  356. % %
  357. % %
  358. + C o n v e r t P a t h T o P o l y g o n %
  359. % %
  360. % %
  361. % %
  362. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  363. %
  364. % ConvertPathToPolygon() converts a path to the more efficient sorted
  365. % rendering form.
  366. %
  367. % The format of the ConvertPathToPolygon method is:
  368. %
  369. % PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info)
  370. %
  371. % A description of each parameter follows:
  372. %
  373. % o Method ConvertPathToPolygon returns the path in a more efficient sorted
  374. % rendering form of type PolygonInfo.
  375. %
  376. % o draw_info: Specifies a pointer to an DrawInfo structure.
  377. %
  378. % o path_info: Specifies a pointer to an PathInfo structure.
  379. %
  380. %
  381. */
  382. #if defined(__cplusplus) || defined(c_plusplus)
  383. extern "C" {
  384. #endif
  385. static int DrawCompareEdges(const void *p_edge,const void *q_edge)
  386. {
  387. #define DrawCompareEdge(p,q) \
  388. { \
  389. if (((p)-(q)) < 0.0) \
  390. return(-1); \
  391. if (((p)-(q)) > 0.0) \
  392. return(1); \
  393. }
  394. register const PointInfo
  395. *p,
  396. *q;
  397. /*
  398. Edge sorting for right-handed coordinate system.
  399. */
  400. p=((const EdgeInfo *) p_edge)->points;
  401. q=((const EdgeInfo *) q_edge)->points;
  402. DrawCompareEdge(p[0].y,q[0].y);
  403. DrawCompareEdge(p[0].x,q[0].x);
  404. DrawCompareEdge((p[1].x-p[0].x)*(q[1].y-q[0].y),(p[1].y-p[0].y)*
  405. (q[1].x-q[0].x));
  406. DrawCompareEdge(p[1].y,q[1].y);
  407. DrawCompareEdge(p[1].x,q[1].x);
  408. return(0);
  409. }
  410. #if defined(__cplusplus) || defined(c_plusplus)
  411. }
  412. #endif
  413. static void LogPolygonInfo(const PolygonInfo *polygon_info)
  414. {
  415. register EdgeInfo
  416. *p;
  417. register ssize_t
  418. i,
  419. j;
  420. (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
  421. p=polygon_info->edges;
  422. for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
  423. {
  424. (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
  425. (double) i);
  426. (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
  427. p->direction != MagickFalse ? "down" : "up");
  428. (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
  429. p->ghostline != MagickFalse ? "transparent" : "opaque");
  430. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  431. " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
  432. p->bounds.x2,p->bounds.y2);
  433. for (j=0; j < (ssize_t) p->number_points; j++)
  434. (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g",
  435. p->points[j].x,p->points[j].y);
  436. p++;
  437. }
  438. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
  439. }
  440. static void ReversePoints(PointInfo *points,const size_t number_points)
  441. {
  442. PointInfo
  443. point;
  444. register ssize_t
  445. i;
  446. for (i=0; i < (ssize_t) (number_points >> 1); i++)
  447. {
  448. point=points[i];
  449. points[i]=points[number_points-(i+1)];
  450. points[number_points-(i+1)]=point;
  451. }
  452. }
  453. static PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info)
  454. {
  455. long
  456. direction,
  457. next_direction;
  458. PointInfo
  459. point,
  460. *points;
  461. PolygonInfo
  462. *polygon_info;
  463. SegmentInfo
  464. bounds;
  465. register ssize_t
  466. i,
  467. n;
  468. MagickBooleanType
  469. ghostline;
  470. size_t
  471. edge,
  472. number_edges,
  473. number_points;
  474. /*
  475. Convert a path to the more efficient sorted rendering form.
  476. */
  477. polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
  478. if (polygon_info == (PolygonInfo *) NULL)
  479. return((PolygonInfo *) NULL);
  480. number_edges=16;
  481. polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges,
  482. sizeof(*polygon_info->edges));
  483. if (polygon_info->edges == (EdgeInfo *) NULL)
  484. return((PolygonInfo *) NULL);
  485. (void) memset(polygon_info->edges,0,number_edges*
  486. sizeof(*polygon_info->edges));
  487. direction=0;
  488. edge=0;
  489. ghostline=MagickFalse;
  490. n=0;
  491. number_points=0;
  492. points=(PointInfo *) NULL;
  493. (void) memset(&point,0,sizeof(point));
  494. (void) memset(&bounds,0,sizeof(bounds));
  495. polygon_info->edges[edge].number_points=(size_t) n;
  496. polygon_info->edges[edge].scanline=0.0;
  497. polygon_info->edges[edge].highwater=0;
  498. polygon_info->edges[edge].ghostline=ghostline;
  499. polygon_info->edges[edge].direction=(ssize_t) direction;
  500. polygon_info->edges[edge].points=points;
  501. polygon_info->edges[edge].bounds=bounds;
  502. polygon_info->number_edges=0;
  503. for (i=0; path_info[i].code != EndCode; i++)
  504. {
  505. if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
  506. (path_info[i].code == GhostlineCode))
  507. {
  508. /*
  509. Move to.
  510. */
  511. if ((points != (PointInfo *) NULL) && (n >= 2))
  512. {
  513. if (edge == number_edges)
  514. {
  515. number_edges<<=1;
  516. polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
  517. polygon_info->edges,(size_t) number_edges,
  518. sizeof(*polygon_info->edges));
  519. if (polygon_info->edges == (EdgeInfo *) NULL)
  520. return((PolygonInfo *) NULL);
  521. }
  522. polygon_info->edges[edge].number_points=(size_t) n;
  523. polygon_info->edges[edge].scanline=(-1.0);
  524. polygon_info->edges[edge].highwater=0;
  525. polygon_info->edges[edge].ghostline=ghostline;
  526. polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
  527. if (direction < 0)
  528. ReversePoints(points,(size_t) n);
  529. polygon_info->edges[edge].points=points;
  530. polygon_info->edges[edge].bounds=bounds;
  531. polygon_info->edges[edge].bounds.y1=points[0].y;
  532. polygon_info->edges[edge].bounds.y2=points[n-1].y;
  533. points=(PointInfo *) NULL;
  534. ghostline=MagickFalse;
  535. edge++;
  536. }
  537. if (points == (PointInfo *) NULL)
  538. {
  539. number_points=16;
  540. points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
  541. sizeof(*points));
  542. if (points == (PointInfo *) NULL)
  543. return((PolygonInfo *) NULL);
  544. }
  545. ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
  546. point=path_info[i].point;
  547. points[0]=point;
  548. bounds.x1=point.x;
  549. bounds.x2=point.x;
  550. direction=0;
  551. n=1;
  552. continue;
  553. }
  554. /*
  555. Line to.
  556. */
  557. next_direction=((path_info[i].point.y > point.y) ||
  558. ((fabs(path_info[i].point.y-point.y) < MagickEpsilon) &&
  559. (path_info[i].point.x > point.x))) ? 1 : -1;
  560. if ((points != (PointInfo *) NULL) && (direction != 0) &&
  561. (direction != next_direction))
  562. {
  563. /*
  564. New edge.
  565. */
  566. point=points[n-1];
  567. if (edge == number_edges)
  568. {
  569. number_edges<<=1;
  570. polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
  571. polygon_info->edges,(size_t) number_edges,
  572. sizeof(*polygon_info->edges));
  573. if (polygon_info->edges == (EdgeInfo *) NULL)
  574. return((PolygonInfo *) NULL);
  575. }
  576. polygon_info->edges[edge].number_points=(size_t) n;
  577. polygon_info->edges[edge].scanline=(-1.0);
  578. polygon_info->edges[edge].highwater=0;
  579. polygon_info->edges[edge].ghostline=ghostline;
  580. polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
  581. if (direction < 0)
  582. ReversePoints(points,(size_t) n);
  583. polygon_info->edges[edge].points=points;
  584. polygon_info->edges[edge].bounds=bounds;
  585. polygon_info->edges[edge].bounds.y1=points[0].y;
  586. polygon_info->edges[edge].bounds.y2=points[n-1].y;
  587. number_points=16;
  588. points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
  589. sizeof(*points));
  590. if (points == (PointInfo *) NULL)
  591. return((PolygonInfo *) NULL);
  592. n=1;
  593. ghostline=MagickFalse;
  594. points[0]=point;
  595. bounds.x1=point.x;
  596. bounds.x2=point.x;
  597. edge++;
  598. }
  599. direction=next_direction;
  600. if (points == (PointInfo *) NULL)
  601. continue;
  602. if (n == (ssize_t) number_points)
  603. {
  604. number_points<<=1;
  605. points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
  606. sizeof(*points));
  607. if (points == (PointInfo *) NULL)
  608. return((PolygonInfo *) NULL);
  609. }
  610. point=path_info[i].point;
  611. points[n]=point;
  612. if (point.x < bounds.x1)
  613. bounds.x1=point.x;
  614. if (point.x > bounds.x2)
  615. bounds.x2=point.x;
  616. n++;
  617. }
  618. if (points != (PointInfo *) NULL)
  619. {
  620. if (n < 2)
  621. points=(PointInfo *) RelinquishMagickMemory(points);
  622. else
  623. {
  624. if (edge == number_edges)
  625. {
  626. number_edges<<=1;
  627. polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
  628. polygon_info->edges,(size_t) number_edges,
  629. sizeof(*polygon_info->edges));
  630. if (polygon_info->edges == (EdgeInfo *) NULL)
  631. return((PolygonInfo *) NULL);
  632. }
  633. polygon_info->edges[edge].number_points=(size_t) n;
  634. polygon_info->edges[edge].scanline=(-1.0);
  635. polygon_info->edges[edge].highwater=0;
  636. polygon_info->edges[edge].ghostline=ghostline;
  637. polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
  638. if (direction < 0)
  639. ReversePoints(points,(size_t) n);
  640. polygon_info->edges[edge].points=points;
  641. polygon_info->edges[edge].bounds=bounds;
  642. polygon_info->edges[edge].bounds.y1=points[0].y;
  643. polygon_info->edges[edge].bounds.y2=points[n-1].y;
  644. ghostline=MagickFalse;
  645. edge++;
  646. }
  647. }
  648. polygon_info->number_edges=edge;
  649. qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
  650. sizeof(*polygon_info->edges),DrawCompareEdges);
  651. if (IsEventLogging() != MagickFalse)
  652. LogPolygonInfo(polygon_info);
  653. return(polygon_info);
  654. }
  655. /*
  656. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  657. % %
  658. % %
  659. % %
  660. + C o n v e r t P r i m i t i v e T o P a t h %
  661. % %
  662. % %
  663. % %
  664. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  665. %
  666. % ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
  667. % path structure.
  668. %
  669. % The format of the ConvertPrimitiveToPath method is:
  670. %
  671. % PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
  672. % const PrimitiveInfo *primitive_info)
  673. %
  674. % A description of each parameter follows:
  675. %
  676. % o Method ConvertPrimitiveToPath returns a vector path structure of type
  677. % PathInfo.
  678. %
  679. % o draw_info: a structure of type DrawInfo.
  680. %
  681. % o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
  682. %
  683. %
  684. */
  685. static void LogPathInfo(const PathInfo *path_info)
  686. {
  687. register const PathInfo
  688. *p;
  689. (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
  690. for (p=path_info; p->code != EndCode; p++)
  691. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  692. " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
  693. "moveto ghostline" : p->code == OpenCode ? "moveto open" :
  694. p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
  695. "?");
  696. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
  697. }
  698. static PathInfo *ConvertPrimitiveToPath(const PrimitiveInfo *primitive_info)
  699. {
  700. MagickBooleanType
  701. closed_subpath;
  702. PathInfo
  703. *path_info;
  704. PathInfoCode
  705. code;
  706. PointInfo
  707. p,
  708. q;
  709. register ssize_t
  710. i,
  711. n;
  712. ssize_t
  713. coordinates,
  714. start;
  715. /*
  716. Converts a PrimitiveInfo structure into a vector path structure.
  717. */
  718. switch (primitive_info->primitive)
  719. {
  720. case AlphaPrimitive:
  721. case ColorPrimitive:
  722. case ImagePrimitive:
  723. case PointPrimitive:
  724. case TextPrimitive:
  725. return((PathInfo *) NULL);
  726. default:
  727. break;
  728. }
  729. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
  730. path_info=(PathInfo *) AcquireQuantumMemory((size_t) (3UL*i+1UL),
  731. sizeof(*path_info));
  732. if (path_info == (PathInfo *) NULL)
  733. return((PathInfo *) NULL);
  734. coordinates=0;
  735. closed_subpath=MagickFalse;
  736. n=0;
  737. p.x=(-1.0);
  738. p.y=(-1.0);
  739. q.x=(-1.0);
  740. q.y=(-1.0);
  741. start=0;
  742. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
  743. {
  744. code=LineToCode;
  745. if (coordinates <= 0)
  746. {
  747. /*
  748. New subpath.
  749. */
  750. coordinates=(ssize_t) primitive_info[i].coordinates;
  751. p=primitive_info[i].point;
  752. start=n;
  753. code=MoveToCode;
  754. closed_subpath=primitive_info[i].closed_subpath;
  755. }
  756. coordinates--;
  757. if ((code == MoveToCode) || (coordinates <= 0) ||
  758. (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
  759. (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
  760. {
  761. /*
  762. Eliminate duplicate points.
  763. */
  764. path_info[n].code=code;
  765. path_info[n].point=primitive_info[i].point;
  766. q=primitive_info[i].point;
  767. n++;
  768. }
  769. if (coordinates > 0)
  770. continue; /* next point in current subpath */
  771. if (closed_subpath != MagickFalse)
  772. {
  773. closed_subpath=MagickFalse;
  774. continue;
  775. }
  776. /*
  777. Mark the p point as open if the subpath is not closed.
  778. */
  779. path_info[start].code=OpenCode;
  780. path_info[n].code=GhostlineCode;
  781. path_info[n].point=primitive_info[i].point;
  782. n++;
  783. path_info[n].code=LineToCode;
  784. path_info[n].point=p;
  785. n++;
  786. }
  787. path_info[n].code=EndCode;
  788. path_info[n].point.x=0.0;
  789. path_info[n].point.y=0.0;
  790. if (IsEventLogging() != MagickFalse)
  791. LogPathInfo(path_info);
  792. path_info=(PathInfo *) ResizeQuantumMemory(path_info,(size_t) (n+1),
  793. sizeof(*path_info));
  794. return(path_info);
  795. }
  796. /*
  797. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  798. % %
  799. % %
  800. % %
  801. % D e s t r o y D r a w I n f o %
  802. % %
  803. % %
  804. % %
  805. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  806. %
  807. % DestroyDrawInfo() deallocates memory associated with an DrawInfo structure.
  808. %
  809. % The format of the DestroyDrawInfo method is:
  810. %
  811. % DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
  812. %
  813. % A description of each parameter follows:
  814. %
  815. % o draw_info: the draw info.
  816. %
  817. */
  818. MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
  819. {
  820. assert(draw_info != (DrawInfo *) NULL);
  821. if (draw_info->debug != MagickFalse)
  822. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  823. assert(draw_info->signature == MagickCoreSignature);
  824. if (draw_info->primitive != (char *) NULL)
  825. draw_info->primitive=DestroyString(draw_info->primitive);
  826. if (draw_info->text != (char *) NULL)
  827. draw_info->text=DestroyString(draw_info->text);
  828. if (draw_info->geometry != (char *) NULL)
  829. draw_info->geometry=DestroyString(draw_info->geometry);
  830. if (draw_info->fill_pattern != (Image *) NULL)
  831. draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
  832. if (draw_info->stroke_pattern != (Image *) NULL)
  833. draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
  834. if (draw_info->font != (char *) NULL)
  835. draw_info->font=DestroyString(draw_info->font);
  836. if (draw_info->metrics != (char *) NULL)
  837. draw_info->metrics=DestroyString(draw_info->metrics);
  838. if (draw_info->family != (char *) NULL)
  839. draw_info->family=DestroyString(draw_info->family);
  840. if (draw_info->encoding != (char *) NULL)
  841. draw_info->encoding=DestroyString(draw_info->encoding);
  842. if (draw_info->density != (char *) NULL)
  843. draw_info->density=DestroyString(draw_info->density);
  844. if (draw_info->server_name != (char *) NULL)
  845. draw_info->server_name=(char *)
  846. RelinquishMagickMemory(draw_info->server_name);
  847. if (draw_info->dash_pattern != (double *) NULL)
  848. draw_info->dash_pattern=(double *) RelinquishMagickMemory(
  849. draw_info->dash_pattern);
  850. if (draw_info->gradient.stops != (StopInfo *) NULL)
  851. draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
  852. draw_info->gradient.stops);
  853. if (draw_info->clip_mask != (char *) NULL)
  854. draw_info->clip_mask=DestroyString(draw_info->clip_mask);
  855. if (draw_info->clipping_mask != (Image *) NULL)
  856. draw_info->clipping_mask=DestroyImage(draw_info->clipping_mask);
  857. if (draw_info->composite_mask != (Image *) NULL)
  858. draw_info->composite_mask=DestroyImage(draw_info->composite_mask);
  859. draw_info->signature=(~MagickCoreSignature);
  860. draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
  861. return(draw_info);
  862. }
  863. /*
  864. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  865. % %
  866. % %
  867. % %
  868. + D e s t r o y E d g e %
  869. % %
  870. % %
  871. % %
  872. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  873. %
  874. % DestroyEdge() destroys the specified polygon edge.
  875. %
  876. % The format of the DestroyEdge method is:
  877. %
  878. % ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
  879. %
  880. % A description of each parameter follows:
  881. %
  882. % o polygon_info: Specifies a pointer to an PolygonInfo structure.
  883. %
  884. % o edge: the polygon edge number to destroy.
  885. %
  886. */
  887. static size_t DestroyEdge(PolygonInfo *polygon_info,
  888. const size_t edge)
  889. {
  890. assert(edge < polygon_info->number_edges);
  891. polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
  892. polygon_info->edges[edge].points);
  893. polygon_info->number_edges--;
  894. if (edge < polygon_info->number_edges)
  895. (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
  896. (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
  897. return(polygon_info->number_edges);
  898. }
  899. /*
  900. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  901. % %
  902. % %
  903. % %
  904. + D e s t r o y P o l y g o n I n f o %
  905. % %
  906. % %
  907. % %
  908. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  909. %
  910. % DestroyPolygonInfo() destroys the PolygonInfo data structure.
  911. %
  912. % The format of the DestroyPolygonInfo method is:
  913. %
  914. % PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
  915. %
  916. % A description of each parameter follows:
  917. %
  918. % o polygon_info: Specifies a pointer to an PolygonInfo structure.
  919. %
  920. */
  921. static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
  922. {
  923. register ssize_t
  924. i;
  925. for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
  926. polygon_info->edges[i].points=(PointInfo *)
  927. RelinquishMagickMemory(polygon_info->edges[i].points);
  928. polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
  929. return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
  930. }
  931. /*
  932. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  933. % %
  934. % %
  935. % %
  936. % D r a w A f f i n e I m a g e %
  937. % %
  938. % %
  939. % %
  940. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  941. %
  942. % DrawAffineImage() composites the source over the destination image as
  943. % dictated by the affine transform.
  944. %
  945. % The format of the DrawAffineImage method is:
  946. %
  947. % MagickBooleanType DrawAffineImage(Image *image,const Image *source,
  948. % const AffineMatrix *affine,ExceptionInfo *exception)
  949. %
  950. % A description of each parameter follows:
  951. %
  952. % o image: the image.
  953. %
  954. % o source: the source image.
  955. %
  956. % o affine: the affine transform.
  957. %
  958. % o exception: return any errors or warnings in this structure.
  959. %
  960. */
  961. static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
  962. const double y,const SegmentInfo *edge)
  963. {
  964. double
  965. intercept,
  966. z;
  967. register double
  968. x;
  969. SegmentInfo
  970. inverse_edge;
  971. /*
  972. Determine left and right edges.
  973. */
  974. inverse_edge.x1=edge->x1;
  975. inverse_edge.y1=edge->y1;
  976. inverse_edge.x2=edge->x2;
  977. inverse_edge.y2=edge->y2;
  978. z=affine->ry*y+affine->tx;
  979. if (affine->sx >= MagickEpsilon)
  980. {
  981. intercept=(-z/affine->sx);
  982. x=intercept;
  983. if (x > inverse_edge.x1)
  984. inverse_edge.x1=x;
  985. intercept=(-z+(double) image->columns)/affine->sx;
  986. x=intercept;
  987. if (x < inverse_edge.x2)
  988. inverse_edge.x2=x;
  989. }
  990. else
  991. if (affine->sx < -MagickEpsilon)
  992. {
  993. intercept=(-z+(double) image->columns)/affine->sx;
  994. x=intercept;
  995. if (x > inverse_edge.x1)
  996. inverse_edge.x1=x;
  997. intercept=(-z/affine->sx);
  998. x=intercept;
  999. if (x < inverse_edge.x2)
  1000. inverse_edge.x2=x;
  1001. }
  1002. else
  1003. if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
  1004. {
  1005. inverse_edge.x2=edge->x1;
  1006. return(inverse_edge);
  1007. }
  1008. /*
  1009. Determine top and bottom edges.
  1010. */
  1011. z=affine->sy*y+affine->ty;
  1012. if (affine->rx >= MagickEpsilon)
  1013. {
  1014. intercept=(-z/affine->rx);
  1015. x=intercept;
  1016. if (x > inverse_edge.x1)
  1017. inverse_edge.x1=x;
  1018. intercept=(-z+(double) image->rows)/affine->rx;
  1019. x=intercept;
  1020. if (x < inverse_edge.x2)
  1021. inverse_edge.x2=x;
  1022. }
  1023. else
  1024. if (affine->rx < -MagickEpsilon)
  1025. {
  1026. intercept=(-z+(double) image->rows)/affine->rx;
  1027. x=intercept;
  1028. if (x > inverse_edge.x1)
  1029. inverse_edge.x1=x;
  1030. intercept=(-z/affine->rx);
  1031. x=intercept;
  1032. if (x < inverse_edge.x2)
  1033. inverse_edge.x2=x;
  1034. }
  1035. else
  1036. if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
  1037. {
  1038. inverse_edge.x2=edge->x2;
  1039. return(inverse_edge);
  1040. }
  1041. return(inverse_edge);
  1042. }
  1043. static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
  1044. {
  1045. AffineMatrix
  1046. inverse_affine;
  1047. double
  1048. determinant;
  1049. determinant=PerceptibleReciprocal(affine->sx*affine->sy-affine->rx*
  1050. affine->ry);
  1051. inverse_affine.sx=determinant*affine->sy;
  1052. inverse_affine.rx=determinant*(-affine->rx);
  1053. inverse_affine.ry=determinant*(-affine->ry);
  1054. inverse_affine.sy=determinant*affine->sx;
  1055. inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
  1056. inverse_affine.ry;
  1057. inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
  1058. inverse_affine.sy;
  1059. return(inverse_affine);
  1060. }
  1061. MagickExport MagickBooleanType DrawAffineImage(Image *image,
  1062. const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
  1063. {
  1064. AffineMatrix
  1065. inverse_affine;
  1066. CacheView
  1067. *image_view,
  1068. *source_view;
  1069. MagickBooleanType
  1070. status;
  1071. PixelInfo
  1072. zero;
  1073. PointInfo
  1074. extent[4],
  1075. min,
  1076. max;
  1077. register ssize_t
  1078. i;
  1079. SegmentInfo
  1080. edge;
  1081. ssize_t
  1082. start,
  1083. stop,
  1084. y;
  1085. /*
  1086. Determine bounding box.
  1087. */
  1088. assert(image != (Image *) NULL);
  1089. assert(image->signature == MagickCoreSignature);
  1090. if (image->debug != MagickFalse)
  1091. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1092. assert(source != (const Image *) NULL);
  1093. assert(source->signature == MagickCoreSignature);
  1094. assert(affine != (AffineMatrix *) NULL);
  1095. extent[0].x=0.0;
  1096. extent[0].y=0.0;
  1097. extent[1].x=(double) source->columns-1.0;
  1098. extent[1].y=0.0;
  1099. extent[2].x=(double) source->columns-1.0;
  1100. extent[2].y=(double) source->rows-1.0;
  1101. extent[3].x=0.0;
  1102. extent[3].y=(double) source->rows-1.0;
  1103. for (i=0; i < 4; i++)
  1104. {
  1105. PointInfo
  1106. point;
  1107. point=extent[i];
  1108. extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
  1109. extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
  1110. }
  1111. min=extent[0];
  1112. max=extent[0];
  1113. for (i=1; i < 4; i++)
  1114. {
  1115. if (min.x > extent[i].x)
  1116. min.x=extent[i].x;
  1117. if (min.y > extent[i].y)
  1118. min.y=extent[i].y;
  1119. if (max.x < extent[i].x)
  1120. max.x=extent[i].x;
  1121. if (max.y < extent[i].y)
  1122. max.y=extent[i].y;
  1123. }
  1124. /*
  1125. Affine transform image.
  1126. */
  1127. if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
  1128. return(MagickFalse);
  1129. status=MagickTrue;
  1130. edge.x1=MagickMax(min.x,0.0);
  1131. edge.y1=MagickMax(min.y,0.0);
  1132. edge.x2=MagickMin(max.x,(double) image->columns-1.0);
  1133. edge.y2=MagickMin(max.y,(double) image->rows-1.0);
  1134. inverse_affine=InverseAffineMatrix(affine);
  1135. GetPixelInfo(image,&zero);
  1136. start=(ssize_t) ceil(edge.y1-0.5);
  1137. stop=(ssize_t) floor(edge.y2+0.5);
  1138. source_view=AcquireVirtualCacheView(source,exception);
  1139. image_view=AcquireAuthenticCacheView(image,exception);
  1140. #if defined(MAGICKCORE_OPENMP_SUPPORT)
  1141. #pragma omp parallel for schedule(static) shared(status) \
  1142. magick_number_threads(source,image,stop-start,1)
  1143. #endif
  1144. for (y=start; y <= stop; y++)
  1145. {
  1146. PixelInfo
  1147. composite,
  1148. pixel;
  1149. PointInfo
  1150. point;
  1151. register ssize_t
  1152. x;
  1153. register Quantum
  1154. *magick_restrict q;
  1155. SegmentInfo
  1156. inverse_edge;
  1157. ssize_t
  1158. x_offset;
  1159. inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
  1160. if (inverse_edge.x2 < inverse_edge.x1)
  1161. continue;
  1162. q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
  1163. 0.5),y,(size_t) (floor(inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),
  1164. 1,exception);
  1165. if (q == (Quantum *) NULL)
  1166. continue;
  1167. pixel=zero;
  1168. composite=zero;
  1169. x_offset=0;
  1170. for (x=(ssize_t) ceil(inverse_edge.x1-0.5); x <= (ssize_t) floor(inverse_edge.x2+0.5); x++)
  1171. {
  1172. point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
  1173. inverse_affine.tx;
  1174. point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
  1175. inverse_affine.ty;
  1176. status=InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
  1177. point.x,point.y,&pixel,exception);
  1178. if (status == MagickFalse)
  1179. break;
  1180. GetPixelInfoPixel(image,q,&composite);
  1181. CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
  1182. &composite);
  1183. SetPixelViaPixelInfo(image,&composite,q);
  1184. x_offset++;
  1185. q+=GetPixelChannels(image);
  1186. }
  1187. if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
  1188. status=MagickFalse;
  1189. }
  1190. source_view=DestroyCacheView(source_view);
  1191. image_view=DestroyCacheView(image_view);
  1192. return(status);
  1193. }
  1194. /*
  1195. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1196. % %
  1197. % %
  1198. % %
  1199. + D r a w B o u n d i n g R e c t a n g l e s %
  1200. % %
  1201. % %
  1202. % %
  1203. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1204. %
  1205. % DrawBoundingRectangles() draws the bounding rectangles on the image. This
  1206. % is only useful for developers debugging the rendering algorithm.
  1207. %
  1208. % The format of the DrawBoundingRectangles method is:
  1209. %
  1210. % MagickBooleanType DrawBoundingRectangles(Image *image,
  1211. % const DrawInfo *draw_info,PolygonInfo *polygon_info,
  1212. % ExceptionInfo *exception)
  1213. %
  1214. % A description of each parameter follows:
  1215. %
  1216. % o image: the image.
  1217. %
  1218. % o draw_info: the draw info.
  1219. %
  1220. % o polygon_info: Specifies a pointer to a PolygonInfo structure.
  1221. %
  1222. % o exception: return any errors or warnings in this structure.
  1223. %
  1224. */
  1225. static inline double SaneStrokeWidth(const Image *image,
  1226. const DrawInfo *draw_info)
  1227. {
  1228. return(MagickMin((double) draw_info->stroke_width,
  1229. (2.0*sqrt(2.0)+MagickEpsilon)*MagickMax(image->columns,image->rows)));
  1230. }
  1231. static MagickBooleanType DrawBoundingRectangles(Image *image,
  1232. const DrawInfo *draw_info,const PolygonInfo *polygon_info,
  1233. ExceptionInfo *exception)
  1234. {
  1235. double
  1236. mid;
  1237. DrawInfo
  1238. *clone_info;
  1239. MagickStatusType
  1240. status;
  1241. PointInfo
  1242. end,
  1243. resolution,
  1244. start;
  1245. PrimitiveInfo
  1246. primitive_info[6];
  1247. register ssize_t
  1248. i;
  1249. SegmentInfo
  1250. bounds;
  1251. ssize_t
  1252. coordinates;
  1253. (void) memset(primitive_info,0,sizeof(primitive_info));
  1254. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  1255. status=QueryColorCompliance("#000F",AllCompliance,&clone_info->fill,
  1256. exception);
  1257. if (status == MagickFalse)
  1258. {
  1259. clone_info=DestroyDrawInfo(clone_info);
  1260. return(MagickFalse);
  1261. }
  1262. resolution.x=96.0;
  1263. resolution.y=96.0;
  1264. if (clone_info->density != (char *) NULL)
  1265. {
  1266. GeometryInfo
  1267. geometry_info;
  1268. MagickStatusType
  1269. flags;
  1270. flags=ParseGeometry(clone_info->density,&geometry_info);
  1271. resolution.x=geometry_info.rho;
  1272. resolution.y=geometry_info.sigma;
  1273. if ((flags & SigmaValue) == MagickFalse)
  1274. resolution.y=resolution.x;
  1275. }
  1276. mid=(resolution.x/96.0)*ExpandAffine(&clone_info->affine)*
  1277. SaneStrokeWidth(image,clone_info)/2.0;
  1278. bounds.x1=0.0;
  1279. bounds.y1=0.0;
  1280. bounds.x2=0.0;
  1281. bounds.y2=0.0;
  1282. if (polygon_info != (PolygonInfo *) NULL)
  1283. {
  1284. bounds=polygon_info->edges[0].bounds;
  1285. for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
  1286. {
  1287. if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
  1288. bounds.x1=polygon_info->edges[i].bounds.x1;
  1289. if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
  1290. bounds.y1=polygon_info->edges[i].bounds.y1;
  1291. if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
  1292. bounds.x2=polygon_info->edges[i].bounds.x2;
  1293. if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
  1294. bounds.y2=polygon_info->edges[i].bounds.y2;
  1295. }
  1296. bounds.x1-=mid;
  1297. bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
  1298. image->columns ? (double) image->columns-1 : bounds.x1;
  1299. bounds.y1-=mid;
  1300. bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
  1301. image->rows ? (double) image->rows-1 : bounds.y1;
  1302. bounds.x2+=mid;
  1303. bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
  1304. image->columns ? (double) image->columns-1 : bounds.x2;
  1305. bounds.y2+=mid;
  1306. bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
  1307. image->rows ? (double) image->rows-1 : bounds.y2;
  1308. for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
  1309. {
  1310. if (polygon_info->edges[i].direction != 0)
  1311. status=QueryColorCompliance("#f00",AllCompliance,&clone_info->stroke,
  1312. exception);
  1313. else
  1314. status=QueryColorCompliance("#0f0",AllCompliance,&clone_info->stroke,
  1315. exception);
  1316. if (status == MagickFalse)
  1317. break;
  1318. start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
  1319. start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
  1320. end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
  1321. end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
  1322. primitive_info[0].primitive=RectanglePrimitive;
  1323. status&=TraceRectangle(primitive_info,start,end);
  1324. primitive_info[0].method=ReplaceMethod;
  1325. coordinates=(ssize_t) primitive_info[0].coordinates;
  1326. primitive_info[coordinates].primitive=UndefinedPrimitive;
  1327. status=DrawPrimitive(image,clone_info,primitive_info,exception);
  1328. if (status == MagickFalse)
  1329. break;
  1330. }
  1331. if (i < (ssize_t) polygon_info->number_edges)
  1332. {
  1333. clone_info=DestroyDrawInfo(clone_info);
  1334. return(status == 0 ? MagickFalse : MagickTrue);
  1335. }
  1336. }
  1337. status=QueryColorCompliance("#00f",AllCompliance,&clone_info->stroke,
  1338. exception);
  1339. if (status == MagickFalse)
  1340. {
  1341. clone_info=DestroyDrawInfo(clone_info);
  1342. return(MagickFalse);
  1343. }
  1344. start.x=(double) (bounds.x1-mid);
  1345. start.y=(double) (bounds.y1-mid);
  1346. end.x=(double) (bounds.x2+mid);
  1347. end.y=(double) (bounds.y2+mid);
  1348. primitive_info[0].primitive=RectanglePrimitive;
  1349. status&=TraceRectangle(primitive_info,start,end);
  1350. primitive_info[0].method=ReplaceMethod;
  1351. coordinates=(ssize_t) primitive_info[0].coordinates;
  1352. primitive_info[coordinates].primitive=UndefinedPrimitive;
  1353. status=DrawPrimitive(image,clone_info,primitive_info,exception);
  1354. clone_info=DestroyDrawInfo(clone_info);
  1355. return(status == 0 ? MagickFalse : MagickTrue);
  1356. }
  1357. /*
  1358. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1359. % %
  1360. % %
  1361. % %
  1362. % D r a w C l i p P a t h %
  1363. % %
  1364. % %
  1365. % %
  1366. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1367. %
  1368. % DrawClipPath() draws the clip path on the image mask.
  1369. %
  1370. % The format of the DrawClipPath method is:
  1371. %
  1372. % MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
  1373. % const char *id,ExceptionInfo *exception)
  1374. %
  1375. % A description of each parameter follows:
  1376. %
  1377. % o image: the image.
  1378. %
  1379. % o draw_info: the draw info.
  1380. %
  1381. % o id: the clip path id.
  1382. %
  1383. % o exception: return any errors or warnings in this structure.
  1384. %
  1385. */
  1386. MagickExport MagickBooleanType DrawClipPath(Image *image,
  1387. const DrawInfo *draw_info,const char *id,ExceptionInfo *exception)
  1388. {
  1389. const char
  1390. *clip_path;
  1391. Image
  1392. *clipping_mask;
  1393. MagickBooleanType
  1394. status;
  1395. clip_path=GetImageArtifact(image,id);
  1396. if (clip_path == (const char *) NULL)
  1397. return(MagickFalse);
  1398. clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
  1399. exception);
  1400. if (clipping_mask == (Image *) NULL)
  1401. return(MagickFalse);
  1402. status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
  1403. clipping_mask=DestroyImage(clipping_mask);
  1404. return(status);
  1405. }
  1406. /*
  1407. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1408. % %
  1409. % %
  1410. % %
  1411. % D r a w C l i p p i n g M a s k %
  1412. % %
  1413. % %
  1414. % %
  1415. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1416. %
  1417. % DrawClippingMask() draws the clip path and returns it as an image clipping
  1418. % mask.
  1419. %
  1420. % The format of the DrawClippingMask method is:
  1421. %
  1422. % Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
  1423. % const char *id,const char *clip_path,ExceptionInfo *exception)
  1424. %
  1425. % A description of each parameter follows:
  1426. %
  1427. % o image: the image.
  1428. %
  1429. % o draw_info: the draw info.
  1430. %
  1431. % o id: the clip path id.
  1432. %
  1433. % o clip_path: the clip path.
  1434. %
  1435. % o exception: return any errors or warnings in this structure.
  1436. %
  1437. */
  1438. static Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
  1439. const char *id,const char *clip_path,ExceptionInfo *exception)
  1440. {
  1441. DrawInfo
  1442. *clone_info;
  1443. Image
  1444. *clip_mask,
  1445. *separate_mask;
  1446. MagickStatusType
  1447. status;
  1448. /*
  1449. Draw a clip path.
  1450. */
  1451. assert(image != (Image *) NULL);
  1452. assert(image->signature == MagickCoreSignature);
  1453. if (image->debug != MagickFalse)
  1454. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1455. assert(draw_info != (const DrawInfo *) NULL);
  1456. clip_mask=AcquireImage((const ImageInfo *) NULL,exception);
  1457. status=SetImageExtent(clip_mask,image->columns,image->rows,exception);
  1458. if (status == MagickFalse)
  1459. return(DestroyImage(clip_mask));
  1460. status=SetImageMask(clip_mask,WritePixelMask,(Image *) NULL,exception);
  1461. status=QueryColorCompliance("#0000",AllCompliance,
  1462. &clip_mask->background_color,exception);
  1463. clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
  1464. clip_mask->background_color.alpha_trait=BlendPixelTrait;
  1465. status=SetImageBackgroundColor(clip_mask,exception);
  1466. if (image->debug != MagickFalse)
  1467. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
  1468. id);
  1469. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  1470. (void) CloneString(&clone_info->primitive,clip_path);
  1471. status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
  1472. exception);
  1473. if (clone_info->clip_mask != (char *) NULL)
  1474. clone_info->clip_mask=DestroyString(clone_info->clip_mask);
  1475. status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
  1476. exception);
  1477. clone_info->stroke_width=0.0;
  1478. clone_info->alpha=OpaqueAlpha;
  1479. clone_info->clip_path=MagickTrue;
  1480. status=RenderMVGContent(clip_mask,clone_info,0,exception);
  1481. clone_info=DestroyDrawInfo(clone_info);
  1482. separate_mask=SeparateImage(clip_mask,AlphaChannel,exception);
  1483. if (separate_mask != (Image *) NULL)
  1484. {
  1485. clip_mask=DestroyImage(clip_mask);
  1486. clip_mask=separate_mask;
  1487. status=NegateImage(clip_mask,MagickFalse,exception);
  1488. if (status == MagickFalse)
  1489. clip_mask=DestroyImage(clip_mask);
  1490. }
  1491. if (image->debug != MagickFalse)
  1492. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
  1493. return(clip_mask);
  1494. }
  1495. /*
  1496. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1497. % %
  1498. % %
  1499. % %
  1500. % D r a w C o m p o s i t e M a s k %
  1501. % %
  1502. % %
  1503. % %
  1504. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1505. %
  1506. % DrawCompositeMask() draws the mask path and returns it as an image mask.
  1507. %
  1508. % The format of the DrawCompositeMask method is:
  1509. %
  1510. % Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
  1511. % const char *id,const char *mask_path,ExceptionInfo *exception)
  1512. %
  1513. % A description of each parameter follows:
  1514. %
  1515. % o image: the image.
  1516. %
  1517. % o draw_info: the draw info.
  1518. %
  1519. % o id: the mask path id.
  1520. %
  1521. % o mask_path: the mask path.
  1522. %
  1523. % o exception: return any errors or warnings in this structure.
  1524. %
  1525. */
  1526. static Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
  1527. const char *id,const char *mask_path,ExceptionInfo *exception)
  1528. {
  1529. Image
  1530. *composite_mask,
  1531. *separate_mask;
  1532. DrawInfo
  1533. *clone_info;
  1534. MagickStatusType
  1535. status;
  1536. /*
  1537. Draw a mask path.
  1538. */
  1539. assert(image != (Image *) NULL);
  1540. assert(image->signature == MagickCoreSignature);
  1541. if (image->debug != MagickFalse)
  1542. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1543. assert(draw_info != (const DrawInfo *) NULL);
  1544. composite_mask=AcquireImage((const ImageInfo *) NULL,exception);
  1545. status=SetImageExtent(composite_mask,image->columns,image->rows,exception);
  1546. if (status == MagickFalse)
  1547. return(DestroyImage(composite_mask));
  1548. status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
  1549. exception);
  1550. status=QueryColorCompliance("#0000",AllCompliance,
  1551. &composite_mask->background_color,exception);
  1552. composite_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
  1553. composite_mask->background_color.alpha_trait=BlendPixelTrait;
  1554. (void) SetImageBackgroundColor(composite_mask,exception);
  1555. if (image->debug != MagickFalse)
  1556. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin mask-path %s",
  1557. id);
  1558. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  1559. (void) CloneString(&clone_info->primitive,mask_path);
  1560. status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
  1561. exception);
  1562. status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
  1563. exception);
  1564. clone_info->stroke_width=0.0;
  1565. clone_info->alpha=OpaqueAlpha;
  1566. status=RenderMVGContent(composite_mask,clone_info,0,exception);
  1567. clone_info=DestroyDrawInfo(clone_info);
  1568. separate_mask=SeparateImage(composite_mask,AlphaChannel,exception);
  1569. if (separate_mask != (Image *) NULL)
  1570. {
  1571. composite_mask=DestroyImage(composite_mask);
  1572. composite_mask=separate_mask;
  1573. status=NegateImage(composite_mask,MagickFalse,exception);
  1574. if (status == MagickFalse)
  1575. composite_mask=DestroyImage(composite_mask);
  1576. }
  1577. if (image->debug != MagickFalse)
  1578. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end mask-path");
  1579. return(composite_mask);
  1580. }
  1581. /*
  1582. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1583. % %
  1584. % %
  1585. % %
  1586. + D r a w D a s h P o l y g o n %
  1587. % %
  1588. % %
  1589. % %
  1590. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1591. %
  1592. % DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
  1593. % image while respecting the dash offset and dash pattern attributes.
  1594. %
  1595. % The format of the DrawDashPolygon method is:
  1596. %
  1597. % MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
  1598. % const PrimitiveInfo *primitive_info,Image *image,
  1599. % ExceptionInfo *exception)
  1600. %
  1601. % A description of each parameter follows:
  1602. %
  1603. % o draw_info: the draw info.
  1604. %
  1605. % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
  1606. %
  1607. % o image: the image.
  1608. %
  1609. % o exception: return any errors or warnings in this structure.
  1610. %
  1611. */
  1612. static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
  1613. const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
  1614. {
  1615. double
  1616. length,
  1617. maximum_length,
  1618. offset,
  1619. scale,
  1620. total_length;
  1621. DrawInfo
  1622. *clone_info;
  1623. MagickStatusType
  1624. status;
  1625. PrimitiveInfo
  1626. *dash_polygon;
  1627. register double
  1628. dx,
  1629. dy;
  1630. register ssize_t
  1631. i;
  1632. size_t
  1633. number_vertices;
  1634. ssize_t
  1635. j,
  1636. n;
  1637. assert(draw_info != (const DrawInfo *) NULL);
  1638. if (image->debug != MagickFalse)
  1639. (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
  1640. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
  1641. number_vertices=(size_t) i;
  1642. dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
  1643. (2UL*number_vertices+32UL),sizeof(*dash_polygon));
  1644. if (dash_polygon == (PrimitiveInfo *) NULL)
  1645. return(MagickFalse);
  1646. (void) memset(dash_polygon,0,(2UL*number_vertices+32UL)*
  1647. sizeof(*dash_polygon));
  1648. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  1649. clone_info->miterlimit=0;
  1650. dash_polygon[0]=primitive_info[0];
  1651. scale=ExpandAffine(&draw_info->affine);
  1652. length=scale*draw_info->dash_pattern[0];
  1653. offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
  1654. scale*draw_info->dash_offset : 0.0;
  1655. j=1;
  1656. for (n=0; offset > 0.0; j=0)
  1657. {
  1658. if (draw_info->dash_pattern[n] <= 0.0)
  1659. break;
  1660. length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
  1661. if (offset > length)
  1662. {
  1663. offset-=length;
  1664. n++;
  1665. length=scale*draw_info->dash_pattern[n];
  1666. continue;
  1667. }
  1668. if (offset < length)
  1669. {
  1670. length-=offset;
  1671. offset=0.0;
  1672. break;
  1673. }
  1674. offset=0.0;
  1675. n++;
  1676. }
  1677. status=MagickTrue;
  1678. maximum_length=0.0;
  1679. total_length=0.0;
  1680. for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
  1681. {
  1682. dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
  1683. dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
  1684. maximum_length=hypot(dx,dy);
  1685. if (maximum_length > MaxBezierCoordinates)
  1686. break;
  1687. if (fabs(length) < MagickEpsilon)
  1688. {
  1689. if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
  1690. n++;
  1691. if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
  1692. n=0;
  1693. length=scale*draw_info->dash_pattern[n];
  1694. }
  1695. for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
  1696. {
  1697. total_length+=length;
  1698. if ((n & 0x01) != 0)
  1699. {
  1700. dash_polygon[0]=primitive_info[0];
  1701. dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
  1702. total_length*PerceptibleReciprocal(maximum_length));
  1703. dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
  1704. total_length*PerceptibleReciprocal(maximum_length));
  1705. j=1;
  1706. }
  1707. else
  1708. {
  1709. if ((j+1) > (ssize_t) number_vertices)
  1710. break;
  1711. dash_polygon[j]=primitive_info[i-1];
  1712. dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
  1713. total_length*PerceptibleReciprocal(maximum_length));
  1714. dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
  1715. total_length*PerceptibleReciprocal(maximum_length));
  1716. dash_polygon[j].coordinates=1;
  1717. j++;
  1718. dash_polygon[0].coordinates=(size_t) j;
  1719. dash_polygon[j].primitive=UndefinedPrimitive;
  1720. status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
  1721. }
  1722. if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
  1723. n++;
  1724. if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
  1725. n=0;
  1726. length=scale*draw_info->dash_pattern[n];
  1727. }
  1728. length-=(maximum_length-total_length);
  1729. if ((n & 0x01) != 0)
  1730. continue;
  1731. dash_polygon[j]=primitive_info[i];
  1732. dash_polygon[j].coordinates=1;
  1733. j++;
  1734. }
  1735. if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
  1736. {
  1737. dash_polygon[j]=primitive_info[i-1];
  1738. dash_polygon[j].point.x+=MagickEpsilon;
  1739. dash_polygon[j].point.y+=MagickEpsilon;
  1740. dash_polygon[j].coordinates=1;
  1741. j++;
  1742. dash_polygon[0].coordinates=(size_t) j;
  1743. dash_polygon[j].primitive=UndefinedPrimitive;
  1744. status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
  1745. }
  1746. dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
  1747. clone_info=DestroyDrawInfo(clone_info);
  1748. if (image->debug != MagickFalse)
  1749. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
  1750. return(status != 0 ? MagickTrue : MagickFalse);
  1751. }
  1752. /*
  1753. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1754. % %
  1755. % %
  1756. % %
  1757. % D r a w G r a d i e n t I m a g e %
  1758. % %
  1759. % %
  1760. % %
  1761. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1762. %
  1763. % DrawGradientImage() draws a linear gradient on the image.
  1764. %
  1765. % The format of the DrawGradientImage method is:
  1766. %
  1767. % MagickBooleanType DrawGradientImage(Image *image,
  1768. % const DrawInfo *draw_info,ExceptionInfo *exception)
  1769. %
  1770. % A description of each parameter follows:
  1771. %
  1772. % o image: the image.
  1773. %
  1774. % o draw_info: the draw info.
  1775. %
  1776. % o exception: return any errors or warnings in this structure.
  1777. %
  1778. */
  1779. static inline double GetStopColorOffset(const GradientInfo *gradient,
  1780. const ssize_t x,const ssize_t y)
  1781. {
  1782. switch (gradient->type)
  1783. {
  1784. case UndefinedGradient:
  1785. case LinearGradient:
  1786. {
  1787. double
  1788. gamma,
  1789. length,
  1790. offset,
  1791. scale;
  1792. PointInfo
  1793. p,
  1794. q;
  1795. const SegmentInfo
  1796. *gradient_vector;
  1797. gradient_vector=(&gradient->gradient_vector);
  1798. p.x=gradient_vector->x2-gradient_vector->x1;
  1799. p.y=gradient_vector->y2-gradient_vector->y1;
  1800. q.x=(double) x-gradient_vector->x1;
  1801. q.y=(double) y-gradient_vector->y1;
  1802. length=sqrt(q.x*q.x+q.y*q.y);
  1803. gamma=sqrt(p.x*p.x+p.y*p.y)*length;
  1804. gamma=PerceptibleReciprocal(gamma);
  1805. scale=p.x*q.x+p.y*q.y;
  1806. offset=gamma*scale*length;
  1807. return(offset);
  1808. }
  1809. case RadialGradient:
  1810. {
  1811. PointInfo
  1812. v;
  1813. if (gradient->spread == RepeatSpread)
  1814. {
  1815. v.x=(double) x-gradient->center.x;
  1816. v.y=(double) y-gradient->center.y;
  1817. return(sqrt(v.x*v.x+v.y*v.y));
  1818. }
  1819. v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
  1820. gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
  1821. gradient->angle))))*PerceptibleReciprocal(gradient->radii.x);
  1822. v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
  1823. gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
  1824. gradient->angle))))*PerceptibleReciprocal(gradient->radii.y);
  1825. return(sqrt(v.x*v.x+v.y*v.y));
  1826. }
  1827. }
  1828. return(0.0);
  1829. }
  1830. static int StopInfoCompare(const void *x,const void *y)
  1831. {
  1832. StopInfo
  1833. *stop_1,
  1834. *stop_2;
  1835. stop_1=(StopInfo *) x;
  1836. stop_2=(StopInfo *) y;
  1837. if (stop_1->offset > stop_2->offset)
  1838. return(1);
  1839. if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
  1840. return(0);
  1841. return(-1);
  1842. }
  1843. MagickExport MagickBooleanType DrawGradientImage(Image *image,
  1844. const DrawInfo *draw_info,ExceptionInfo *exception)
  1845. {
  1846. CacheView
  1847. *image_view;
  1848. const GradientInfo
  1849. *gradient;
  1850. const SegmentInfo
  1851. *gradient_vector;
  1852. double
  1853. length;
  1854. MagickBooleanType
  1855. status;
  1856. PixelInfo
  1857. zero;
  1858. PointInfo
  1859. point;
  1860. RectangleInfo
  1861. bounding_box;
  1862. ssize_t
  1863. y;
  1864. /*
  1865. Draw linear or radial gradient on image.
  1866. */
  1867. assert(image != (Image *) NULL);
  1868. assert(image->signature == MagickCoreSignature);
  1869. if (image->debug != MagickFalse)
  1870. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1871. assert(draw_info != (const DrawInfo *) NULL);
  1872. gradient=(&draw_info->gradient);
  1873. qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
  1874. StopInfoCompare);
  1875. gradient_vector=(&gradient->gradient_vector);
  1876. point.x=gradient_vector->x2-gradient_vector->x1;
  1877. point.y=gradient_vector->y2-gradient_vector->y1;
  1878. length=sqrt(point.x*point.x+point.y*point.y);
  1879. bounding_box=gradient->bounding_box;
  1880. status=MagickTrue;
  1881. GetPixelInfo(image,&zero);
  1882. image_view=AcquireAuthenticCacheView(image,exception);
  1883. #if defined(MAGICKCORE_OPENMP_SUPPORT)
  1884. #pragma omp parallel for schedule(static) shared(status) \
  1885. magick_number_threads(image,image,bounding_box.height-bounding_box.y,1)
  1886. #endif
  1887. for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
  1888. {
  1889. PixelInfo
  1890. composite,
  1891. pixel;
  1892. double
  1893. alpha,
  1894. offset;
  1895. register Quantum
  1896. *magick_restrict q;
  1897. register ssize_t
  1898. i,
  1899. x;
  1900. ssize_t
  1901. j;
  1902. if (status == MagickFalse)
  1903. continue;
  1904. q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
  1905. if (q == (Quantum *) NULL)
  1906. {
  1907. status=MagickFalse;
  1908. continue;
  1909. }
  1910. pixel=zero;
  1911. composite=zero;
  1912. offset=GetStopColorOffset(gradient,0,y);
  1913. if (gradient->type != RadialGradient)
  1914. offset*=PerceptibleReciprocal(length);
  1915. for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
  1916. {
  1917. GetPixelInfoPixel(image,q,&pixel);
  1918. switch (gradient->spread)
  1919. {
  1920. case UndefinedSpread:
  1921. case PadSpread:
  1922. {
  1923. if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
  1924. (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
  1925. {
  1926. offset=GetStopColorOffset(gradient,x,y);
  1927. if (gradient->type != RadialGradient)
  1928. offset*=PerceptibleReciprocal(length);
  1929. }
  1930. for (i=0; i < (ssize_t) gradient->number_stops; i++)
  1931. if (offset < gradient->stops[i].offset)
  1932. break;
  1933. if ((offset < 0.0) || (i == 0))
  1934. composite=gradient->stops[0].color;
  1935. else
  1936. if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
  1937. composite=gradient->stops[gradient->number_stops-1].color;
  1938. else
  1939. {
  1940. j=i;
  1941. i--;
  1942. alpha=(offset-gradient->stops[i].offset)/
  1943. (gradient->stops[j].offset-gradient->stops[i].offset);
  1944. CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
  1945. &gradient->stops[j].color,alpha,&composite);
  1946. }
  1947. break;
  1948. }
  1949. case ReflectSpread:
  1950. {
  1951. if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
  1952. (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
  1953. {
  1954. offset=GetStopColorOffset(gradient,x,y);
  1955. if (gradient->type != RadialGradient)
  1956. offset*=PerceptibleReciprocal(length);
  1957. }
  1958. if (offset < 0.0)
  1959. offset=(-offset);
  1960. if ((ssize_t) fmod(offset,2.0) == 0)
  1961. offset=fmod(offset,1.0);
  1962. else
  1963. offset=1.0-fmod(offset,1.0);
  1964. for (i=0; i < (ssize_t) gradient->number_stops; i++)
  1965. if (offset < gradient->stops[i].offset)
  1966. break;
  1967. if (i == 0)
  1968. composite=gradient->stops[0].color;
  1969. else
  1970. if (i == (ssize_t) gradient->number_stops)
  1971. composite=gradient->stops[gradient->number_stops-1].color;
  1972. else
  1973. {
  1974. j=i;
  1975. i--;
  1976. alpha=(offset-gradient->stops[i].offset)/
  1977. (gradient->stops[j].offset-gradient->stops[i].offset);
  1978. CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
  1979. &gradient->stops[j].color,alpha,&composite);
  1980. }
  1981. break;
  1982. }
  1983. case RepeatSpread:
  1984. {
  1985. MagickBooleanType
  1986. antialias;
  1987. double
  1988. repeat;
  1989. antialias=MagickFalse;
  1990. repeat=0.0;
  1991. if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
  1992. (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
  1993. {
  1994. offset=GetStopColorOffset(gradient,x,y);
  1995. if (gradient->type == LinearGradient)
  1996. {
  1997. repeat=fmod(offset,length);
  1998. if (repeat < 0.0)
  1999. repeat=length-fmod(-repeat,length);
  2000. else
  2001. repeat=fmod(offset,length);
  2002. antialias=(repeat < length) && ((repeat+1.0) > length) ?
  2003. MagickTrue : MagickFalse;
  2004. offset=PerceptibleReciprocal(length)*repeat;
  2005. }
  2006. else
  2007. {
  2008. repeat=fmod(offset,gradient->radius);
  2009. if (repeat < 0.0)
  2010. repeat=gradient->radius-fmod(-repeat,gradient->radius);
  2011. else
  2012. repeat=fmod(offset,gradient->radius);
  2013. antialias=repeat+1.0 > gradient->radius ? MagickTrue :
  2014. MagickFalse;
  2015. offset=repeat/gradient->radius;
  2016. }
  2017. }
  2018. for (i=0; i < (ssize_t) gradient->number_stops; i++)
  2019. if (offset < gradient->stops[i].offset)
  2020. break;
  2021. if (i == 0)
  2022. composite=gradient->stops[0].color;
  2023. else
  2024. if (i == (ssize_t) gradient->number_stops)
  2025. composite=gradient->stops[gradient->number_stops-1].color;
  2026. else
  2027. {
  2028. j=i;
  2029. i--;
  2030. alpha=(offset-gradient->stops[i].offset)/
  2031. (gradient->stops[j].offset-gradient->stops[i].offset);
  2032. if (antialias != MagickFalse)
  2033. {
  2034. if (gradient->type == LinearGradient)
  2035. alpha=length-repeat;
  2036. else
  2037. alpha=gradient->radius-repeat;
  2038. i=0;
  2039. j=(ssize_t) gradient->number_stops-1L;
  2040. }
  2041. CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
  2042. &gradient->stops[j].color,alpha,&composite);
  2043. }
  2044. break;
  2045. }
  2046. }
  2047. CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
  2048. &pixel);
  2049. SetPixelViaPixelInfo(image,&pixel,q);
  2050. q+=GetPixelChannels(image);
  2051. }
  2052. if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
  2053. status=MagickFalse;
  2054. }
  2055. image_view=DestroyCacheView(image_view);
  2056. return(status);
  2057. }
  2058. /*
  2059. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2060. % %
  2061. % %
  2062. % %
  2063. % D r a w I m a g e %
  2064. % %
  2065. % %
  2066. % %
  2067. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2068. %
  2069. % DrawImage() draws a graphic primitive on your image. The primitive
  2070. % may be represented as a string or filename. Precede the filename with an
  2071. % "at" sign (@) and the contents of the file are drawn on the image. You
  2072. % can affect how text is drawn by setting one or more members of the draw
  2073. % info structure.
  2074. %
  2075. % The format of the DrawImage method is:
  2076. %
  2077. % MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
  2078. % ExceptionInfo *exception)
  2079. %
  2080. % A description of each parameter follows:
  2081. %
  2082. % o image: the image.
  2083. %
  2084. % o draw_info: the draw info.
  2085. %
  2086. % o exception: return any errors or warnings in this structure.
  2087. %
  2088. */
  2089. static MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
  2090. const size_t pad)
  2091. {
  2092. double
  2093. extent;
  2094. size_t
  2095. quantum;
  2096. /*
  2097. Check if there is enough storage for drawing pimitives.
  2098. */
  2099. extent=(double) mvg_info->offset+pad+PrimitiveExtentPad;
  2100. quantum=sizeof(**mvg_info->primitive_info);
  2101. if (((extent*quantum) < (double) SSIZE_MAX) &&
  2102. ((extent*quantum) < (double) GetMaxMemoryRequest()))
  2103. {
  2104. if (extent <= (double) *mvg_info->extent)
  2105. return(MagickTrue);
  2106. *mvg_info->primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
  2107. *mvg_info->primitive_info,(size_t) extent,quantum);
  2108. if (*mvg_info->primitive_info != (PrimitiveInfo *) NULL)
  2109. {
  2110. register ssize_t
  2111. i;
  2112. *mvg_info->extent=(size_t) extent;
  2113. for (i=mvg_info->offset+1; i < (ssize_t) extent; i++)
  2114. (*mvg_info->primitive_info)[i].primitive=UndefinedPrimitive;
  2115. return(MagickTrue);
  2116. }
  2117. }
  2118. /*
  2119. Reallocation failed, allocate a primitive to facilitate unwinding.
  2120. */
  2121. (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
  2122. ResourceLimitError,"MemoryAllocationFailed","`%s'","");
  2123. if (*mvg_info->primitive_info != (PrimitiveInfo *) NULL)
  2124. *mvg_info->primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(
  2125. *mvg_info->primitive_info);
  2126. *mvg_info->primitive_info=(PrimitiveInfo *) AcquireCriticalMemory(
  2127. PrimitiveExtentPad*quantum);
  2128. (void) memset(*mvg_info->primitive_info,0,PrimitiveExtentPad*quantum);
  2129. *mvg_info->extent=1;
  2130. return(MagickFalse);
  2131. }
  2132. MagickExport int MVGMacroCompare(const void *target,const void *source)
  2133. {
  2134. const char
  2135. *p,
  2136. *q;
  2137. p=(const char *) target;
  2138. q=(const char *) source;
  2139. return(strcmp(p,q));
  2140. }
  2141. static SplayTreeInfo *GetMVGMacros(const char *primitive)
  2142. {
  2143. char
  2144. *macro,
  2145. *token;
  2146. const char
  2147. *q;
  2148. size_t
  2149. extent;
  2150. SplayTreeInfo
  2151. *macros;
  2152. /*
  2153. Scan graphic primitives for definitions and classes.
  2154. */
  2155. if (primitive == (const char *) NULL)
  2156. return((SplayTreeInfo *) NULL);
  2157. macros=NewSplayTree(MVGMacroCompare,RelinquishMagickMemory,
  2158. RelinquishMagickMemory);
  2159. macro=AcquireString(primitive);
  2160. token=AcquireString(primitive);
  2161. extent=strlen(token)+MagickPathExtent;
  2162. for (q=primitive; *q != '\0'; )
  2163. {
  2164. GetNextToken(q,&q,extent,token);
  2165. if (*token == '\0')
  2166. break;
  2167. if (LocaleCompare("push",token) == 0)
  2168. {
  2169. register const char
  2170. *end,
  2171. *start;
  2172. GetNextToken(q,&q,extent,token);
  2173. if (*q == '"')
  2174. {
  2175. char
  2176. name[MagickPathExtent];
  2177. const char
  2178. *p;
  2179. ssize_t
  2180. n;
  2181. /*
  2182. Named macro (e.g. push graphic-context "wheel").
  2183. */
  2184. GetNextToken(q,&q,extent,token);
  2185. start=q;
  2186. end=q;
  2187. (void) CopyMagickString(name,token,MagickPathExtent);
  2188. n=1;
  2189. for (p=q; *p != '\0'; )
  2190. {
  2191. GetNextToken(p,&p,extent,token);
  2192. if (*token == '\0')
  2193. break;
  2194. if (LocaleCompare(token,"pop") == 0)
  2195. {
  2196. end=p-strlen(token)-1;
  2197. n--;
  2198. }
  2199. if (LocaleCompare(token,"push") == 0)
  2200. n++;
  2201. if ((n == 0) && (end > start))
  2202. {
  2203. /*
  2204. Extract macro.
  2205. */
  2206. GetNextToken(p,&p,extent,token);
  2207. (void) CopyMagickString(macro,start,(size_t) (end-start));
  2208. (void) AddValueToSplayTree(macros,ConstantString(name),
  2209. ConstantString(macro));
  2210. break;
  2211. }
  2212. }
  2213. }
  2214. }
  2215. }
  2216. token=DestroyString(token);
  2217. macro=DestroyString(macro);
  2218. return(macros);
  2219. }
  2220. static inline MagickBooleanType IsPoint(const char *point)
  2221. {
  2222. char
  2223. *p;
  2224. double
  2225. value;
  2226. value=StringToDouble(point,&p);
  2227. return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
  2228. MagickTrue);
  2229. }
  2230. static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
  2231. const PointInfo point)
  2232. {
  2233. primitive_info->coordinates=1;
  2234. primitive_info->closed_subpath=MagickFalse;
  2235. primitive_info->point=point;
  2236. return(MagickTrue);
  2237. }
  2238. static MagickBooleanType RenderMVGContent(Image *image,
  2239. const DrawInfo *draw_info,const size_t depth,ExceptionInfo *exception)
  2240. {
  2241. #define RenderImageTag "Render/Image"
  2242. AffineMatrix
  2243. affine,
  2244. current;
  2245. char
  2246. keyword[MagickPathExtent],
  2247. geometry[MagickPathExtent],
  2248. *next_token,
  2249. pattern[MagickPathExtent],
  2250. *primitive,
  2251. *token;
  2252. const char
  2253. *q;
  2254. double
  2255. angle,
  2256. coordinates,
  2257. cursor,
  2258. factor,
  2259. primitive_extent;
  2260. DrawInfo
  2261. *clone_info,
  2262. **graphic_context;
  2263. MagickBooleanType
  2264. proceed;
  2265. MagickStatusType
  2266. status;
  2267. MVGInfo
  2268. mvg_info;
  2269. PointInfo
  2270. point;
  2271. PrimitiveInfo
  2272. *primitive_info;
  2273. PrimitiveType
  2274. primitive_type;
  2275. register const char
  2276. *p;
  2277. register ssize_t
  2278. i,
  2279. x;
  2280. SegmentInfo
  2281. bounds;
  2282. size_t
  2283. extent,
  2284. number_points,
  2285. number_stops;
  2286. SplayTreeInfo
  2287. *macros;
  2288. ssize_t
  2289. defsDepth,
  2290. j,
  2291. k,
  2292. n,
  2293. symbolDepth;
  2294. StopInfo
  2295. *stops;
  2296. TypeMetric
  2297. metrics;
  2298. assert(image != (Image *) NULL);
  2299. assert(image->signature == MagickCoreSignature);
  2300. if (image->debug != MagickFalse)
  2301. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  2302. assert(draw_info != (DrawInfo *) NULL);
  2303. assert(draw_info->signature == MagickCoreSignature);
  2304. if (image->debug != MagickFalse)
  2305. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  2306. if (depth > MagickMaxRecursionDepth)
  2307. ThrowBinaryException(DrawError,"VectorGraphicsNestedTooDeeply",
  2308. image->filename);
  2309. if ((draw_info->primitive == (char *) NULL) ||
  2310. (*draw_info->primitive == '\0'))
  2311. return(MagickFalse);
  2312. if (image->debug != MagickFalse)
  2313. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
  2314. if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
  2315. return(MagickFalse);
  2316. if (image->alpha_trait == UndefinedPixelTrait)
  2317. {
  2318. status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
  2319. if (status == MagickFalse)
  2320. return(MagickFalse);
  2321. }
  2322. primitive=(char *) NULL;
  2323. if (*draw_info->primitive != '@')
  2324. primitive=AcquireString(draw_info->primitive);
  2325. else
  2326. if ((strlen(draw_info->primitive) > 1) &&
  2327. (*(draw_info->primitive+1) != '-'))
  2328. primitive=FileToString(draw_info->primitive+1,~0UL,exception);
  2329. if (primitive == (char *) NULL)
  2330. return(MagickFalse);
  2331. primitive_extent=(double) strlen(primitive);
  2332. (void) SetImageArtifact(image,"mvg:vector-graphics",primitive);
  2333. n=0;
  2334. number_stops=0;
  2335. stops=(StopInfo *) NULL;
  2336. /*
  2337. Allocate primitive info memory.
  2338. */
  2339. graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
  2340. if (graphic_context == (DrawInfo **) NULL)
  2341. {
  2342. primitive=DestroyString(primitive);
  2343. ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
  2344. image->filename);
  2345. }
  2346. number_points=PrimitiveExtentPad;
  2347. primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
  2348. sizeof(*primitive_info));
  2349. if (primitive_info == (PrimitiveInfo *) NULL)
  2350. {
  2351. primitive=DestroyString(primitive);
  2352. for ( ; n >= 0; n--)
  2353. graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
  2354. graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
  2355. ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
  2356. image->filename);
  2357. }
  2358. (void) memset(primitive_info,0,(size_t) number_points*
  2359. sizeof(*primitive_info));
  2360. (void) memset(&mvg_info,0,sizeof(mvg_info));
  2361. mvg_info.primitive_info=(&primitive_info);
  2362. mvg_info.extent=(&number_points);
  2363. mvg_info.exception=exception;
  2364. graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  2365. graphic_context[n]->viewbox=image->page;
  2366. if ((image->page.width == 0) || (image->page.height == 0))
  2367. {
  2368. graphic_context[n]->viewbox.width=image->columns;
  2369. graphic_context[n]->viewbox.height=image->rows;
  2370. }
  2371. token=AcquireString(primitive);
  2372. extent=strlen(token)+MagickPathExtent;
  2373. defsDepth=0;
  2374. symbolDepth=0;
  2375. cursor=0.0;
  2376. macros=GetMVGMacros(primitive);
  2377. status=MagickTrue;
  2378. for (q=primitive; *q != '\0'; )
  2379. {
  2380. /*
  2381. Interpret graphic primitive.
  2382. */
  2383. GetNextToken(q,&q,MagickPathExtent,keyword);
  2384. if (*keyword == '\0')
  2385. break;
  2386. if (*keyword == '#')
  2387. {
  2388. /*
  2389. Comment.
  2390. */
  2391. while ((*q != '\n') && (*q != '\0'))
  2392. q++;
  2393. continue;
  2394. }
  2395. p=q-strlen(keyword)-1;
  2396. primitive_type=UndefinedPrimitive;
  2397. current=graphic_context[n]->affine;
  2398. GetAffineMatrix(&affine);
  2399. switch (*keyword)
  2400. {
  2401. case ';':
  2402. break;
  2403. case 'a':
  2404. case 'A':
  2405. {
  2406. if (LocaleCompare("affine",keyword) == 0)
  2407. {
  2408. GetNextToken(q,&q,extent,token);
  2409. affine.sx=StringToDouble(token,&next_token);
  2410. if (token == next_token)
  2411. ThrowPointExpectedException(token,exception);
  2412. GetNextToken(q,&q,extent,token);
  2413. if (*token == ',')
  2414. GetNextToken(q,&q,extent,token);
  2415. affine.rx=StringToDouble(token,&next_token);
  2416. if (token == next_token)
  2417. ThrowPointExpectedException(token,exception);
  2418. GetNextToken(q,&q,extent,token);
  2419. if (*token == ',')
  2420. GetNextToken(q,&q,extent,token);
  2421. affine.ry=StringToDouble(token,&next_token);
  2422. if (token == next_token)
  2423. ThrowPointExpectedException(token,exception);
  2424. GetNextToken(q,&q,extent,token);
  2425. if (*token == ',')
  2426. GetNextToken(q,&q,extent,token);
  2427. affine.sy=StringToDouble(token,&next_token);
  2428. if (token == next_token)
  2429. ThrowPointExpectedException(token,exception);
  2430. GetNextToken(q,&q,extent,token);
  2431. if (*token == ',')
  2432. GetNextToken(q,&q,extent,token);
  2433. affine.tx=StringToDouble(token,&next_token);
  2434. if (token == next_token)
  2435. ThrowPointExpectedException(token,exception);
  2436. GetNextToken(q,&q,extent,token);
  2437. if (*token == ',')
  2438. GetNextToken(q,&q,extent,token);
  2439. affine.ty=StringToDouble(token,&next_token);
  2440. if (token == next_token)
  2441. ThrowPointExpectedException(token,exception);
  2442. break;
  2443. }
  2444. if (LocaleCompare("alpha",keyword) == 0)
  2445. {
  2446. primitive_type=AlphaPrimitive;
  2447. break;
  2448. }
  2449. if (LocaleCompare("arc",keyword) == 0)
  2450. {
  2451. primitive_type=ArcPrimitive;
  2452. break;
  2453. }
  2454. status=MagickFalse;
  2455. break;
  2456. }
  2457. case 'b':
  2458. case 'B':
  2459. {
  2460. if (LocaleCompare("bezier",keyword) == 0)
  2461. {
  2462. primitive_type=BezierPrimitive;
  2463. break;
  2464. }
  2465. if (LocaleCompare("border-color",keyword) == 0)
  2466. {
  2467. GetNextToken(q,&q,extent,token);
  2468. status&=QueryColorCompliance(token,AllCompliance,
  2469. &graphic_context[n]->border_color,exception);
  2470. break;
  2471. }
  2472. status=MagickFalse;
  2473. break;
  2474. }
  2475. case 'c':
  2476. case 'C':
  2477. {
  2478. if (LocaleCompare("class",keyword) == 0)
  2479. {
  2480. const char
  2481. *mvg_class;
  2482. GetNextToken(q,&q,extent,token);
  2483. if (*token == '\0')
  2484. {
  2485. status=MagickFalse;
  2486. break;
  2487. }
  2488. mvg_class=(const char *) GetValueFromSplayTree(macros,token);
  2489. if (mvg_class != (const char *) NULL)
  2490. {
  2491. char
  2492. *elements;
  2493. ssize_t
  2494. offset;
  2495. /*
  2496. Inject class elements in stream.
  2497. */
  2498. offset=(ssize_t) (p-primitive);
  2499. elements=AcquireString(primitive);
  2500. elements[offset]='\0';
  2501. (void) ConcatenateString(&elements,mvg_class);
  2502. (void) ConcatenateString(&elements,"\n");
  2503. (void) ConcatenateString(&elements,q);
  2504. primitive=DestroyString(primitive);
  2505. primitive=elements;
  2506. q=primitive+offset;
  2507. }
  2508. break;
  2509. }
  2510. if (LocaleCompare("clip-path",keyword) == 0)
  2511. {
  2512. const char
  2513. *clip_path;
  2514. /*
  2515. Take a node from within the MVG document, and duplicate it here.
  2516. */
  2517. GetNextToken(q,&q,extent,token);
  2518. if (*token == '\0')
  2519. {
  2520. status=MagickFalse;
  2521. break;
  2522. }
  2523. (void) CloneString(&graphic_context[n]->clip_mask,token);
  2524. clip_path=(const char *) GetValueFromSplayTree(macros,token);
  2525. if (clip_path != (const char *) NULL)
  2526. {
  2527. if (graphic_context[n]->clipping_mask != (Image *) NULL)
  2528. graphic_context[n]->clipping_mask=
  2529. DestroyImage(graphic_context[n]->clipping_mask);
  2530. graphic_context[n]->clipping_mask=DrawClippingMask(image,
  2531. graphic_context[n],token,clip_path,exception);
  2532. if (draw_info->compliance != SVGCompliance)
  2533. {
  2534. const char
  2535. *clip_path;
  2536. clip_path=(const char *) GetValueFromSplayTree(macros,
  2537. graphic_context[n]->clip_mask);
  2538. if (clip_path != (const char *) NULL)
  2539. (void) SetImageArtifact(image,
  2540. graphic_context[n]->clip_mask,clip_path);
  2541. status&=DrawClipPath(image,graphic_context[n],
  2542. graphic_context[n]->clip_mask,exception);
  2543. }
  2544. }
  2545. break;
  2546. }
  2547. if (LocaleCompare("clip-rule",keyword) == 0)
  2548. {
  2549. ssize_t
  2550. fill_rule;
  2551. GetNextToken(q,&q,extent,token);
  2552. fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
  2553. token);
  2554. if (fill_rule == -1)
  2555. {
  2556. status=MagickFalse;
  2557. break;
  2558. }
  2559. graphic_context[n]->fill_rule=(FillRule) fill_rule;
  2560. break;
  2561. }
  2562. if (LocaleCompare("clip-units",keyword) == 0)
  2563. {
  2564. ssize_t
  2565. clip_units;
  2566. GetNextToken(q,&q,extent,token);
  2567. clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
  2568. token);
  2569. if (clip_units == -1)
  2570. {
  2571. status=MagickFalse;
  2572. break;
  2573. }
  2574. graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
  2575. if (clip_units == ObjectBoundingBox)
  2576. {
  2577. GetAffineMatrix(&current);
  2578. affine.sx=draw_info->bounds.x2;
  2579. affine.sy=draw_info->bounds.y2;
  2580. affine.tx=draw_info->bounds.x1;
  2581. affine.ty=draw_info->bounds.y1;
  2582. break;
  2583. }
  2584. break;
  2585. }
  2586. if (LocaleCompare("circle",keyword) == 0)
  2587. {
  2588. primitive_type=CirclePrimitive;
  2589. break;
  2590. }
  2591. if (LocaleCompare("color",keyword) == 0)
  2592. {
  2593. primitive_type=ColorPrimitive;
  2594. break;
  2595. }
  2596. if (LocaleCompare("compliance",keyword) == 0)
  2597. {
  2598. /*
  2599. MVG compliance associates a clipping mask with an image; SVG
  2600. compliance associates a clipping mask with a graphics context.
  2601. */
  2602. GetNextToken(q,&q,extent,token);
  2603. graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
  2604. MagickComplianceOptions,MagickFalse,token);
  2605. break;
  2606. }
  2607. status=MagickFalse;
  2608. break;
  2609. }
  2610. case 'd':
  2611. case 'D':
  2612. {
  2613. if (LocaleCompare("decorate",keyword) == 0)
  2614. {
  2615. ssize_t
  2616. decorate;
  2617. GetNextToken(q,&q,extent,token);
  2618. decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
  2619. token);
  2620. if (decorate == -1)
  2621. {
  2622. status=MagickFalse;
  2623. break;
  2624. }
  2625. graphic_context[n]->decorate=(DecorationType) decorate;
  2626. break;
  2627. }
  2628. if (LocaleCompare("density",keyword) == 0)
  2629. {
  2630. GetNextToken(q,&q,extent,token);
  2631. (void) CloneString(&graphic_context[n]->density,token);
  2632. break;
  2633. }
  2634. if (LocaleCompare("direction",keyword) == 0)
  2635. {
  2636. ssize_t
  2637. direction;
  2638. GetNextToken(q,&q,extent,token);
  2639. direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
  2640. token);
  2641. if (direction == -1)
  2642. status=MagickFalse;
  2643. else
  2644. graphic_context[n]->direction=(DirectionType) direction;
  2645. break;
  2646. }
  2647. status=MagickFalse;
  2648. break;
  2649. }
  2650. case 'e':
  2651. case 'E':
  2652. {
  2653. if (LocaleCompare("ellipse",keyword) == 0)
  2654. {
  2655. primitive_type=EllipsePrimitive;
  2656. break;
  2657. }
  2658. if (LocaleCompare("encoding",keyword) == 0)
  2659. {
  2660. GetNextToken(q,&q,extent,token);
  2661. (void) CloneString(&graphic_context[n]->encoding,token);
  2662. break;
  2663. }
  2664. status=MagickFalse;
  2665. break;
  2666. }
  2667. case 'f':
  2668. case 'F':
  2669. {
  2670. if (LocaleCompare("fill",keyword) == 0)
  2671. {
  2672. GetNextToken(q,&q,extent,token);
  2673. if (graphic_context[n]->clip_path != MagickFalse)
  2674. break;
  2675. (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
  2676. if (GetImageArtifact(image,pattern) != (const char *) NULL)
  2677. (void) DrawPatternPath(image,draw_info,token,
  2678. &graphic_context[n]->fill_pattern,exception);
  2679. else
  2680. {
  2681. status&=QueryColorCompliance(token,AllCompliance,
  2682. &graphic_context[n]->fill,exception);
  2683. if (graphic_context[n]->fill_alpha != OpaqueAlpha)
  2684. graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
  2685. }
  2686. break;
  2687. }
  2688. if (LocaleCompare("fill-opacity",keyword) == 0)
  2689. {
  2690. double
  2691. opacity;
  2692. GetNextToken(q,&q,extent,token);
  2693. if (graphic_context[n]->clip_path != MagickFalse)
  2694. break;
  2695. factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
  2696. opacity=MagickMin(MagickMax(factor*
  2697. StringToDouble(token,&next_token),0.0),1.0);
  2698. if (token == next_token)
  2699. ThrowPointExpectedException(token,exception);
  2700. graphic_context[n]->fill_alpha*=opacity;
  2701. if (graphic_context[n]->fill.alpha != TransparentAlpha)
  2702. graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
  2703. else
  2704. graphic_context[n]->fill.alpha=(MagickRealType)
  2705. ClampToQuantum(QuantumRange*(1.0-opacity));
  2706. break;
  2707. }
  2708. if (LocaleCompare("fill-rule",keyword) == 0)
  2709. {
  2710. ssize_t
  2711. fill_rule;
  2712. GetNextToken(q,&q,extent,token);
  2713. fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
  2714. token);
  2715. if (fill_rule == -1)
  2716. {
  2717. status=MagickFalse;
  2718. break;
  2719. }
  2720. graphic_context[n]->fill_rule=(FillRule) fill_rule;
  2721. break;
  2722. }
  2723. if (LocaleCompare("font",keyword) == 0)
  2724. {
  2725. GetNextToken(q,&q,extent,token);
  2726. (void) CloneString(&graphic_context[n]->font,token);
  2727. if (LocaleCompare("none",token) == 0)
  2728. graphic_context[n]->font=(char *) RelinquishMagickMemory(
  2729. graphic_context[n]->font);
  2730. break;
  2731. }
  2732. if (LocaleCompare("font-family",keyword) == 0)
  2733. {
  2734. GetNextToken(q,&q,extent,token);
  2735. (void) CloneString(&graphic_context[n]->family,token);
  2736. break;
  2737. }
  2738. if (LocaleCompare("font-size",keyword) == 0)
  2739. {
  2740. GetNextToken(q,&q,extent,token);
  2741. graphic_context[n]->pointsize=StringToDouble(token,&next_token);
  2742. if (token == next_token)
  2743. ThrowPointExpectedException(token,exception);
  2744. break;
  2745. }
  2746. if (LocaleCompare("font-stretch",keyword) == 0)
  2747. {
  2748. ssize_t
  2749. stretch;
  2750. GetNextToken(q,&q,extent,token);
  2751. stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
  2752. if (stretch == -1)
  2753. {
  2754. status=MagickFalse;
  2755. break;
  2756. }
  2757. graphic_context[n]->stretch=(StretchType) stretch;
  2758. break;
  2759. }
  2760. if (LocaleCompare("font-style",keyword) == 0)
  2761. {
  2762. ssize_t
  2763. style;
  2764. GetNextToken(q,&q,extent,token);
  2765. style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
  2766. if (style == -1)
  2767. {
  2768. status=MagickFalse;
  2769. break;
  2770. }
  2771. graphic_context[n]->style=(StyleType) style;
  2772. break;
  2773. }
  2774. if (LocaleCompare("font-weight",keyword) == 0)
  2775. {
  2776. ssize_t
  2777. weight;
  2778. GetNextToken(q,&q,extent,token);
  2779. weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
  2780. if (weight == -1)
  2781. weight=(ssize_t) StringToUnsignedLong(token);
  2782. graphic_context[n]->weight=(size_t) weight;
  2783. break;
  2784. }
  2785. status=MagickFalse;
  2786. break;
  2787. }
  2788. case 'g':
  2789. case 'G':
  2790. {
  2791. if (LocaleCompare("gradient-units",keyword) == 0)
  2792. {
  2793. GetNextToken(q,&q,extent,token);
  2794. break;
  2795. }
  2796. if (LocaleCompare("gravity",keyword) == 0)
  2797. {
  2798. ssize_t
  2799. gravity;
  2800. GetNextToken(q,&q,extent,token);
  2801. gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
  2802. if (gravity == -1)
  2803. {
  2804. status=MagickFalse;
  2805. break;
  2806. }
  2807. graphic_context[n]->gravity=(GravityType) gravity;
  2808. break;
  2809. }
  2810. status=MagickFalse;
  2811. break;
  2812. }
  2813. case 'i':
  2814. case 'I':
  2815. {
  2816. if (LocaleCompare("image",keyword) == 0)
  2817. {
  2818. ssize_t
  2819. compose;
  2820. primitive_type=ImagePrimitive;
  2821. GetNextToken(q,&q,extent,token);
  2822. compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
  2823. if (compose == -1)
  2824. {
  2825. status=MagickFalse;
  2826. break;
  2827. }
  2828. graphic_context[n]->compose=(CompositeOperator) compose;
  2829. break;
  2830. }
  2831. if (LocaleCompare("interline-spacing",keyword) == 0)
  2832. {
  2833. GetNextToken(q,&q,extent,token);
  2834. graphic_context[n]->interline_spacing=StringToDouble(token,
  2835. &next_token);
  2836. if (token == next_token)
  2837. ThrowPointExpectedException(token,exception);
  2838. break;
  2839. }
  2840. if (LocaleCompare("interword-spacing",keyword) == 0)
  2841. {
  2842. GetNextToken(q,&q,extent,token);
  2843. graphic_context[n]->interword_spacing=StringToDouble(token,
  2844. &next_token);
  2845. if (token == next_token)
  2846. ThrowPointExpectedException(token,exception);
  2847. break;
  2848. }
  2849. status=MagickFalse;
  2850. break;
  2851. }
  2852. case 'k':
  2853. case 'K':
  2854. {
  2855. if (LocaleCompare("kerning",keyword) == 0)
  2856. {
  2857. GetNextToken(q,&q,extent,token);
  2858. graphic_context[n]->kerning=StringToDouble(token,&next_token);
  2859. if (token == next_token)
  2860. ThrowPointExpectedException(token,exception);
  2861. break;
  2862. }
  2863. status=MagickFalse;
  2864. break;
  2865. }
  2866. case 'l':
  2867. case 'L':
  2868. {
  2869. if (LocaleCompare("letter-spacing",keyword) == 0)
  2870. {
  2871. GetNextToken(q,&q,extent,token);
  2872. clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
  2873. clone_info->text=AcquireString(" ");
  2874. status&=GetTypeMetrics(image,clone_info,&metrics,exception);
  2875. graphic_context[n]->kerning=metrics.width*
  2876. StringToDouble(token,&next_token);
  2877. clone_info=DestroyDrawInfo(clone_info);
  2878. if (token == next_token)
  2879. ThrowPointExpectedException(token,exception);
  2880. break;
  2881. }
  2882. if (LocaleCompare("line",keyword) == 0)
  2883. {
  2884. primitive_type=LinePrimitive;
  2885. break;
  2886. }
  2887. status=MagickFalse;
  2888. break;
  2889. }
  2890. case 'm':
  2891. case 'M':
  2892. {
  2893. if (LocaleCompare("mask",keyword) == 0)
  2894. {
  2895. const char
  2896. *mask_path;
  2897. /*
  2898. Take a node from within the MVG document, and duplicate it here.
  2899. */
  2900. GetNextToken(q,&q,extent,token);
  2901. mask_path=(const char *) GetValueFromSplayTree(macros,token);
  2902. if (mask_path != (const char *) NULL)
  2903. {
  2904. if (graphic_context[n]->composite_mask != (Image *) NULL)
  2905. graphic_context[n]->composite_mask=
  2906. DestroyImage(graphic_context[n]->composite_mask);
  2907. graphic_context[n]->composite_mask=DrawCompositeMask(image,
  2908. graphic_context[n],token,mask_path,exception);
  2909. if (draw_info->compliance != SVGCompliance)
  2910. status=SetImageMask(image,CompositePixelMask,
  2911. graphic_context[n]->composite_mask,exception);
  2912. }
  2913. break;
  2914. }
  2915. break;
  2916. }
  2917. case 'o':
  2918. case 'O':
  2919. {
  2920. if (LocaleCompare("offset",keyword) == 0)
  2921. {
  2922. GetNextToken(q,&q,extent,token);
  2923. break;
  2924. }
  2925. if (LocaleCompare("opacity",keyword) == 0)
  2926. {
  2927. double
  2928. opacity;
  2929. GetNextToken(q,&q,extent,token);
  2930. if (graphic_context[n]->clip_path != MagickFalse)
  2931. break;
  2932. factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
  2933. opacity=MagickMin(MagickMax(factor*
  2934. StringToDouble(token,&next_token),0.0),1.0);
  2935. if (token == next_token)
  2936. ThrowPointExpectedException(token,exception);
  2937. graphic_context[n]->fill_alpha*=opacity;
  2938. graphic_context[n]->stroke_alpha*=opacity;
  2939. break;
  2940. }
  2941. status=MagickFalse;
  2942. break;
  2943. }
  2944. case 'p':
  2945. case 'P':
  2946. {
  2947. if (LocaleCompare("path",keyword) == 0)
  2948. {
  2949. primitive_type=PathPrimitive;
  2950. break;
  2951. }
  2952. if (LocaleCompare("point",keyword) == 0)
  2953. {
  2954. primitive_type=PointPrimitive;
  2955. break;
  2956. }
  2957. if (LocaleCompare("polyline",keyword) == 0)
  2958. {
  2959. primitive_type=PolylinePrimitive;
  2960. break;
  2961. }
  2962. if (LocaleCompare("polygon",keyword) == 0)
  2963. {
  2964. primitive_type=PolygonPrimitive;
  2965. break;
  2966. }
  2967. if (LocaleCompare("pop",keyword) == 0)
  2968. {
  2969. GetNextToken(q,&q,extent,token);
  2970. if (LocaleCompare("class",token) == 0)
  2971. break;
  2972. if (LocaleCompare("clip-path",token) == 0)
  2973. break;
  2974. if (LocaleCompare("defs",token) == 0)
  2975. {
  2976. defsDepth--;
  2977. graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
  2978. MagickTrue;
  2979. break;
  2980. }
  2981. if (LocaleCompare("gradient",token) == 0)
  2982. break;
  2983. if (LocaleCompare("graphic-context",token) == 0)
  2984. {
  2985. if (n <= 0)
  2986. {
  2987. (void) ThrowMagickException(exception,GetMagickModule(),
  2988. DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
  2989. status=MagickFalse;
  2990. n=0;
  2991. break;
  2992. }
  2993. if ((graphic_context[n]->clip_mask != (char *) NULL) &&
  2994. (draw_info->compliance != SVGCompliance))
  2995. if (LocaleCompare(graphic_context[n]->clip_mask,
  2996. graphic_context[n-1]->clip_mask) != 0)
  2997. status=SetImageMask(image,WritePixelMask,(Image *) NULL,
  2998. exception);
  2999. graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
  3000. n--;
  3001. break;
  3002. }
  3003. if (LocaleCompare("mask",token) == 0)
  3004. break;
  3005. if (LocaleCompare("pattern",token) == 0)
  3006. break;
  3007. if (LocaleCompare("symbol",token) == 0)
  3008. {
  3009. symbolDepth--;
  3010. graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
  3011. MagickTrue;
  3012. break;
  3013. }
  3014. status=MagickFalse;
  3015. break;
  3016. }
  3017. if (LocaleCompare("push",keyword) == 0)
  3018. {
  3019. GetNextToken(q,&q,extent,token);
  3020. if (LocaleCompare("class",token) == 0)
  3021. {
  3022. /*
  3023. Class context.
  3024. */
  3025. for (p=q; *q != '\0'; )
  3026. {
  3027. GetNextToken(q,&q,extent,token);
  3028. if (LocaleCompare(token,"pop") != 0)
  3029. continue;
  3030. GetNextToken(q,(const char **) NULL,extent,token);
  3031. if (LocaleCompare(token,"class") != 0)
  3032. continue;
  3033. break;
  3034. }
  3035. GetNextToken(q,&q,extent,token);
  3036. break;
  3037. }
  3038. if (LocaleCompare("clip-path",token) == 0)
  3039. {
  3040. GetNextToken(q,&q,extent,token);
  3041. for (p=q; *q != '\0'; )
  3042. {
  3043. GetNextToken(q,&q,extent,token);
  3044. if (LocaleCompare(token,"pop") != 0)
  3045. continue;
  3046. GetNextToken(q,(const char **) NULL,extent,token);
  3047. if (LocaleCompare(token,"clip-path") != 0)
  3048. continue;
  3049. break;
  3050. }
  3051. if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
  3052. {
  3053. status=MagickFalse;
  3054. break;
  3055. }
  3056. GetNextToken(q,&q,extent,token);
  3057. break;
  3058. }
  3059. if (LocaleCompare("defs",token) == 0)
  3060. {
  3061. defsDepth++;
  3062. graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
  3063. MagickTrue;
  3064. break;
  3065. }
  3066. if (LocaleCompare("gradient",token) == 0)
  3067. {
  3068. char
  3069. key[2*MagickPathExtent],
  3070. name[MagickPathExtent],
  3071. type[MagickPathExtent];
  3072. SegmentInfo
  3073. segment;
  3074. GetNextToken(q,&q,extent,token);
  3075. (void) CopyMagickString(name,token,MagickPathExtent);
  3076. GetNextToken(q,&q,extent,token);
  3077. (void) CopyMagickString(type,token,MagickPathExtent);
  3078. GetNextToken(q,&q,extent,token);
  3079. segment.x1=StringToDouble(token,&next_token);
  3080. if (token == next_token)
  3081. ThrowPointExpectedException(token,exception);
  3082. GetNextToken(q,&q,extent,token);
  3083. if (*token == ',')
  3084. GetNextToken(q,&q,extent,token);
  3085. segment.y1=StringToDouble(token,&next_token);
  3086. if (token == next_token)
  3087. ThrowPointExpectedException(token,exception);
  3088. GetNextToken(q,&q,extent,token);
  3089. if (*token == ',')
  3090. GetNextToken(q,&q,extent,token);
  3091. segment.x2=StringToDouble(token,&next_token);
  3092. if (token == next_token)
  3093. ThrowPointExpectedException(token,exception);
  3094. GetNextToken(q,&q,extent,token);
  3095. if (*token == ',')
  3096. GetNextToken(q,&q,extent,token);
  3097. segment.y2=StringToDouble(token,&next_token);
  3098. if (token == next_token)
  3099. ThrowPointExpectedException(token,exception);
  3100. if (LocaleCompare(type,"radial") == 0)
  3101. {
  3102. GetNextToken(q,&q,extent,token);
  3103. if (*token == ',')
  3104. GetNextToken(q,&q,extent,token);
  3105. }
  3106. for (p=q; *q != '\0'; )
  3107. {
  3108. GetNextToken(q,&q,extent,token);
  3109. if (LocaleCompare(token,"pop") != 0)
  3110. continue;
  3111. GetNextToken(q,(const char **) NULL,extent,token);
  3112. if (LocaleCompare(token,"gradient") != 0)
  3113. continue;
  3114. break;
  3115. }
  3116. if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
  3117. {
  3118. status=MagickFalse;
  3119. break;
  3120. }
  3121. (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
  3122. bounds.x1=graphic_context[n]->affine.sx*segment.x1+
  3123. graphic_context[n]->affine.ry*segment.y1+
  3124. graphic_context[n]->affine.tx;
  3125. bounds.y1=graphic_context[n]->affine.rx*segment.x1+
  3126. graphic_context[n]->affine.sy*segment.y1+
  3127. graphic_context[n]->affine.ty;
  3128. bounds.x2=graphic_context[n]->affine.sx*segment.x2+
  3129. graphic_context[n]->affine.ry*segment.y2+
  3130. graphic_context[n]->affine.tx;
  3131. bounds.y2=graphic_context[n]->affine.rx*segment.x2+
  3132. graphic_context[n]->affine.sy*segment.y2+
  3133. graphic_context[n]->affine.ty;
  3134. (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
  3135. (void) SetImageArtifact(image,key,token);
  3136. (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name);
  3137. (void) SetImageArtifact(image,key,type);
  3138. (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
  3139. name);
  3140. (void) FormatLocaleString(geometry,MagickPathExtent,
  3141. "%gx%g%+.15g%+.15g",
  3142. MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
  3143. MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
  3144. bounds.x1,bounds.y1);
  3145. (void) SetImageArtifact(image,key,geometry);
  3146. GetNextToken(q,&q,extent,token);
  3147. break;
  3148. }
  3149. if (LocaleCompare("graphic-context",token) == 0)
  3150. {
  3151. n++;
  3152. graphic_context=(DrawInfo **) ResizeQuantumMemory(
  3153. graphic_context,(size_t) (n+1),sizeof(*graphic_context));
  3154. if (graphic_context == (DrawInfo **) NULL)
  3155. {
  3156. (void) ThrowMagickException(exception,GetMagickModule(),
  3157. ResourceLimitError,"MemoryAllocationFailed","`%s'",
  3158. image->filename);
  3159. break;
  3160. }
  3161. graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
  3162. graphic_context[n-1]);
  3163. if (*q == '"')
  3164. GetNextToken(q,&q,extent,token);
  3165. break;
  3166. }
  3167. if (LocaleCompare("mask",token) == 0)
  3168. {
  3169. GetNextToken(q,&q,extent,token);
  3170. break;
  3171. }
  3172. if (LocaleCompare("pattern",token) == 0)
  3173. {
  3174. char
  3175. key[2*MagickPathExtent],
  3176. name[MagickPathExtent];
  3177. RectangleInfo
  3178. bounds;
  3179. GetNextToken(q,&q,extent,token);
  3180. (void) CopyMagickString(name,token,MagickPathExtent);
  3181. GetNextToken(q,&q,extent,token);
  3182. bounds.x=(ssize_t) ceil(StringToDouble(token,&next_token)-0.5);
  3183. if (token == next_token)
  3184. ThrowPointExpectedException(token,exception);
  3185. GetNextToken(q,&q,extent,token);
  3186. if (*token == ',')
  3187. GetNextToken(q,&q,extent,token);
  3188. bounds.y=(ssize_t) ceil(StringToDouble(token,&next_token)-0.5);
  3189. if (token == next_token)
  3190. ThrowPointExpectedException(token,exception);
  3191. GetNextToken(q,&q,extent,token);
  3192. if (*token == ',')
  3193. GetNextToken(q,&q,extent,token);
  3194. bounds.width=(size_t) floor(StringToDouble(token,&next_token)+
  3195. 0.5);
  3196. if (token == next_token)
  3197. ThrowPointExpectedException(token,exception);
  3198. GetNextToken(q,&q,extent,token);
  3199. if (*token == ',')
  3200. GetNextToken(q,&q,extent,token);
  3201. bounds.height=(size_t) floor(StringToDouble(token,&next_token)+
  3202. 0.5);
  3203. if (token == next_token)
  3204. ThrowPointExpectedException(token,exception);
  3205. for (p=q; *q != '\0'; )
  3206. {
  3207. GetNextToken(q,&q,extent,token);
  3208. if (LocaleCompare(token,"pop") != 0)
  3209. continue;
  3210. GetNextToken(q,(const char **) NULL,extent,token);
  3211. if (LocaleCompare(token,"pattern") != 0)
  3212. continue;
  3213. break;
  3214. }
  3215. if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
  3216. {
  3217. status=MagickFalse;
  3218. break;
  3219. }
  3220. (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
  3221. (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
  3222. (void) SetImageArtifact(image,key,token);
  3223. (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
  3224. name);
  3225. (void) FormatLocaleString(geometry,MagickPathExtent,
  3226. "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
  3227. bounds.height,(double) bounds.x,(double) bounds.y);
  3228. (void) SetImageArtifact(image,key,geometry);
  3229. GetNextToken(q,&q,extent,token);
  3230. break;
  3231. }
  3232. if (LocaleCompare("symbol",token) == 0)
  3233. {
  3234. symbolDepth++;
  3235. graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
  3236. MagickTrue;
  3237. break;
  3238. }
  3239. status=MagickFalse;
  3240. break;
  3241. }
  3242. status=MagickFalse;
  3243. break;
  3244. }
  3245. case 'r':
  3246. case 'R':
  3247. {
  3248. if (LocaleCompare("rectangle",keyword) == 0)
  3249. {
  3250. primitive_type=RectanglePrimitive;
  3251. break;
  3252. }
  3253. if (LocaleCompare("rotate",keyword) == 0)
  3254. {
  3255. GetNextToken(q,&q,extent,token);
  3256. angle=StringToDouble(token,&next_token);
  3257. if (token == next_token)
  3258. ThrowPointExpectedException(token,exception);
  3259. affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
  3260. affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
  3261. affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
  3262. affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
  3263. break;
  3264. }
  3265. if (LocaleCompare("roundRectangle",keyword) == 0)
  3266. {
  3267. primitive_type=RoundRectanglePrimitive;
  3268. break;
  3269. }
  3270. status=MagickFalse;
  3271. break;
  3272. }
  3273. case 's':
  3274. case 'S':
  3275. {
  3276. if (LocaleCompare("scale",keyword) == 0)
  3277. {
  3278. GetNextToken(q,&q,extent,token);
  3279. affine.sx=StringToDouble(token,&next_token);
  3280. if (token == next_token)
  3281. ThrowPointExpectedException(token,exception);
  3282. GetNextToken(q,&q,extent,token);
  3283. if (*token == ',')
  3284. GetNextToken(q,&q,extent,token);
  3285. affine.sy=StringToDouble(token,&next_token);
  3286. if (token == next_token)
  3287. ThrowPointExpectedException(token,exception);
  3288. break;
  3289. }
  3290. if (LocaleCompare("skewX",keyword) == 0)
  3291. {
  3292. GetNextToken(q,&q,extent,token);
  3293. angle=StringToDouble(token,&next_token);
  3294. if (token == next_token)
  3295. ThrowPointExpectedException(token,exception);
  3296. affine.ry=sin(DegreesToRadians(angle));
  3297. break;
  3298. }
  3299. if (LocaleCompare("skewY",keyword) == 0)
  3300. {
  3301. GetNextToken(q,&q,extent,token);
  3302. angle=StringToDouble(token,&next_token);
  3303. if (token == next_token)
  3304. ThrowPointExpectedException(token,exception);
  3305. affine.rx=(-tan(DegreesToRadians(angle)/2.0));
  3306. break;
  3307. }
  3308. if (LocaleCompare("stop-color",keyword) == 0)
  3309. {
  3310. PixelInfo
  3311. stop_color;
  3312. number_stops++;
  3313. if (number_stops == 1)
  3314. stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops));
  3315. else
  3316. if (number_stops > 2)
  3317. stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
  3318. sizeof(*stops));
  3319. if (stops == (StopInfo *) NULL)
  3320. {
  3321. (void) ThrowMagickException(exception,GetMagickModule(),
  3322. ResourceLimitError,"MemoryAllocationFailed","`%s'",
  3323. image->filename);
  3324. break;
  3325. }
  3326. GetNextToken(q,&q,extent,token);
  3327. status&=QueryColorCompliance(token,AllCompliance,&stop_color,
  3328. exception);
  3329. stops[number_stops-1].color=stop_color;
  3330. GetNextToken(q,&q,extent,token);
  3331. factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
  3332. stops[number_stops-1].offset=factor*StringToDouble(token,
  3333. &next_token);
  3334. if (token == next_token)
  3335. ThrowPointExpectedException(token,exception);
  3336. break;
  3337. }
  3338. if (LocaleCompare("stroke",keyword) == 0)
  3339. {
  3340. GetNextToken(q,&q,extent,token);
  3341. if (graphic_context[n]->clip_path != MagickFalse)
  3342. break;
  3343. (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
  3344. if (GetImageArtifact(image,pattern) != (const char *) NULL)
  3345. (void) DrawPatternPath(image,draw_info,token,
  3346. &graphic_context[n]->stroke_pattern,exception);
  3347. else
  3348. {
  3349. status&=QueryColorCompliance(token,AllCompliance,
  3350. &graphic_context[n]->stroke,exception);
  3351. if (graphic_context[n]->stroke_alpha != OpaqueAlpha)
  3352. graphic_context[n]->stroke.alpha=
  3353. graphic_context[n]->stroke_alpha;
  3354. }
  3355. break;
  3356. }
  3357. if (LocaleCompare("stroke-antialias",keyword) == 0)
  3358. {
  3359. GetNextToken(q,&q,extent,token);
  3360. graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
  3361. MagickTrue : MagickFalse;
  3362. break;
  3363. }
  3364. if (LocaleCompare("stroke-dasharray",keyword) == 0)
  3365. {
  3366. if (graphic_context[n]->dash_pattern != (double *) NULL)
  3367. graphic_context[n]->dash_pattern=(double *)
  3368. RelinquishMagickMemory(graphic_context[n]->dash_pattern);
  3369. if (IsPoint(q) != MagickFalse)
  3370. {
  3371. const char
  3372. *r;
  3373. r=q;
  3374. GetNextToken(r,&r,extent,token);
  3375. if (*token == ',')
  3376. GetNextToken(r,&r,extent,token);
  3377. for (x=0; IsPoint(token) != MagickFalse; x++)
  3378. {
  3379. GetNextToken(r,&r,extent,token);
  3380. if (*token == ',')
  3381. GetNextToken(r,&r,extent,token);
  3382. }
  3383. graphic_context[n]->dash_pattern=(double *)
  3384. AcquireQuantumMemory((size_t) (2*x+2),
  3385. sizeof(*graphic_context[n]->dash_pattern));
  3386. if (graphic_context[n]->dash_pattern == (double *) NULL)
  3387. {
  3388. (void) ThrowMagickException(exception,GetMagickModule(),
  3389. ResourceLimitError,"MemoryAllocationFailed","`%s'",
  3390. image->filename);
  3391. status=MagickFalse;
  3392. break;
  3393. }
  3394. (void) memset(graphic_context[n]->dash_pattern,0,(size_t)
  3395. (2*x+2)*sizeof(*graphic_context[n]->dash_pattern));
  3396. for (j=0; j < x; j++)
  3397. {
  3398. GetNextToken(q,&q,extent,token);
  3399. if (*token == ',')
  3400. GetNextToken(q,&q,extent,token);
  3401. graphic_context[n]->dash_pattern[j]=StringToDouble(token,
  3402. &next_token);
  3403. if (token == next_token)
  3404. ThrowPointExpectedException(token,exception);
  3405. if (graphic_context[n]->dash_pattern[j] < 0.0)
  3406. status=MagickFalse;
  3407. }
  3408. if ((x & 0x01) != 0)
  3409. for ( ; j < (2*x); j++)
  3410. graphic_context[n]->dash_pattern[j]=
  3411. graphic_context[n]->dash_pattern[j-x];
  3412. graphic_context[n]->dash_pattern[j]=0.0;
  3413. break;
  3414. }
  3415. GetNextToken(q,&q,extent,token);
  3416. break;
  3417. }
  3418. if (LocaleCompare("stroke-dashoffset",keyword) == 0)
  3419. {
  3420. GetNextToken(q,&q,extent,token);
  3421. graphic_context[n]->dash_offset=StringToDouble(token,&next_token);
  3422. if (token == next_token)
  3423. ThrowPointExpectedException(token,exception);
  3424. break;
  3425. }
  3426. if (LocaleCompare("stroke-linecap",keyword) == 0)
  3427. {
  3428. ssize_t
  3429. linecap;
  3430. GetNextToken(q,&q,extent,token);
  3431. linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
  3432. if (linecap == -1)
  3433. {
  3434. status=MagickFalse;
  3435. break;
  3436. }
  3437. graphic_context[n]->linecap=(LineCap) linecap;
  3438. break;
  3439. }
  3440. if (LocaleCompare("stroke-linejoin",keyword) == 0)
  3441. {
  3442. ssize_t
  3443. linejoin;
  3444. GetNextToken(q,&q,extent,token);
  3445. linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
  3446. token);
  3447. if (linejoin == -1)
  3448. {
  3449. status=MagickFalse;
  3450. break;
  3451. }
  3452. graphic_context[n]->linejoin=(LineJoin) linejoin;
  3453. break;
  3454. }
  3455. if (LocaleCompare("stroke-miterlimit",keyword) == 0)
  3456. {
  3457. GetNextToken(q,&q,extent,token);
  3458. graphic_context[n]->miterlimit=StringToUnsignedLong(token);
  3459. break;
  3460. }
  3461. if (LocaleCompare("stroke-opacity",keyword) == 0)
  3462. {
  3463. double
  3464. opacity;
  3465. GetNextToken(q,&q,extent,token);
  3466. if (graphic_context[n]->clip_path != MagickFalse)
  3467. break;
  3468. factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
  3469. opacity=MagickMin(MagickMax(factor*
  3470. StringToDouble(token,&next_token),0.0),1.0);
  3471. if (token == next_token)
  3472. ThrowPointExpectedException(token,exception);
  3473. graphic_context[n]->stroke_alpha*=opacity;
  3474. if (graphic_context[n]->stroke.alpha != TransparentAlpha)
  3475. graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
  3476. else
  3477. graphic_context[n]->stroke.alpha=(MagickRealType)
  3478. ClampToQuantum(QuantumRange*(1.0-opacity));
  3479. break;
  3480. }
  3481. if (LocaleCompare("stroke-width",keyword) == 0)
  3482. {
  3483. GetNextToken(q,&q,extent,token);
  3484. if (graphic_context[n]->clip_path != MagickFalse)
  3485. break;
  3486. graphic_context[n]->stroke_width=StringToDouble(token,&next_token);
  3487. if (token == next_token)
  3488. ThrowPointExpectedException(token,exception);
  3489. break;
  3490. }
  3491. status=MagickFalse;
  3492. break;
  3493. }
  3494. case 't':
  3495. case 'T':
  3496. {
  3497. if (LocaleCompare("text",keyword) == 0)
  3498. {
  3499. primitive_type=TextPrimitive;
  3500. break;
  3501. }
  3502. if (LocaleCompare("text-align",keyword) == 0)
  3503. {
  3504. ssize_t
  3505. align;
  3506. GetNextToken(q,&q,extent,token);
  3507. align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
  3508. if (align == -1)
  3509. {
  3510. status=MagickFalse;
  3511. break;
  3512. }
  3513. graphic_context[n]->align=(AlignType) align;
  3514. break;
  3515. }
  3516. if (LocaleCompare("text-anchor",keyword) == 0)
  3517. {
  3518. ssize_t
  3519. align;
  3520. GetNextToken(q,&q,extent,token);
  3521. align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
  3522. if (align == -1)
  3523. {
  3524. status=MagickFalse;
  3525. break;
  3526. }
  3527. graphic_context[n]->align=(AlignType) align;
  3528. break;
  3529. }
  3530. if (LocaleCompare("text-antialias",keyword) == 0)
  3531. {
  3532. GetNextToken(q,&q,extent,token);
  3533. graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
  3534. MagickTrue : MagickFalse;
  3535. break;
  3536. }
  3537. if (LocaleCompare("text-undercolor",keyword) == 0)
  3538. {
  3539. GetNextToken(q,&q,extent,token);
  3540. status&=QueryColorCompliance(token,AllCompliance,
  3541. &graphic_context[n]->undercolor,exception);
  3542. break;
  3543. }
  3544. if (LocaleCompare("translate",keyword) == 0)
  3545. {
  3546. GetNextToken(q,&q,extent,token);
  3547. affine.tx=StringToDouble(token,&next_token);
  3548. if (token == next_token)
  3549. ThrowPointExpectedException(token,exception);
  3550. GetNextToken(q,&q,extent,token);
  3551. if (*token == ',')
  3552. GetNextToken(q,&q,extent,token);
  3553. affine.ty=StringToDouble(token,&next_token);
  3554. if (token == next_token)
  3555. ThrowPointExpectedException(token,exception);
  3556. cursor=0.0;
  3557. break;
  3558. }
  3559. status=MagickFalse;
  3560. break;
  3561. }
  3562. case 'u':
  3563. case 'U':
  3564. {
  3565. if (LocaleCompare("use",keyword) == 0)
  3566. {
  3567. const char
  3568. *use;
  3569. /*
  3570. Get a macro from the MVG document, and "use" it here.
  3571. */
  3572. GetNextToken(q,&q,extent,token);
  3573. use=(const char *) GetValueFromSplayTree(macros,token);
  3574. if (use != (const char *) NULL)
  3575. {
  3576. clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
  3577. (void) CloneString(&clone_info->primitive,use);
  3578. status=RenderMVGContent(image,clone_info,depth+1,exception);
  3579. clone_info=DestroyDrawInfo(clone_info);
  3580. }
  3581. break;
  3582. }
  3583. break;
  3584. }
  3585. case 'v':
  3586. case 'V':
  3587. {
  3588. if (LocaleCompare("viewbox",keyword) == 0)
  3589. {
  3590. GetNextToken(q,&q,extent,token);
  3591. graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
  3592. &next_token)-0.5);
  3593. if (token == next_token)
  3594. ThrowPointExpectedException(token,exception);
  3595. GetNextToken(q,&q,extent,token);
  3596. if (*token == ',')
  3597. GetNextToken(q,&q,extent,token);
  3598. graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
  3599. &next_token)-0.5);
  3600. if (token == next_token)
  3601. ThrowPointExpectedException(token,exception);
  3602. GetNextToken(q,&q,extent,token);
  3603. if (*token == ',')
  3604. GetNextToken(q,&q,extent,token);
  3605. graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
  3606. token,&next_token)+0.5);
  3607. if (token == next_token)
  3608. ThrowPointExpectedException(token,exception);
  3609. GetNextToken(q,&q,extent,token);
  3610. if (*token == ',')
  3611. GetNextToken(q,&q,extent,token);
  3612. graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
  3613. token,&next_token)+0.5);
  3614. if (token == next_token)
  3615. ThrowPointExpectedException(token,exception);
  3616. break;
  3617. }
  3618. status=MagickFalse;
  3619. break;
  3620. }
  3621. case 'w':
  3622. case 'W':
  3623. {
  3624. if (LocaleCompare("word-spacing",keyword) == 0)
  3625. {
  3626. GetNextToken(q,&q,extent,token);
  3627. graphic_context[n]->interword_spacing=StringToDouble(token,
  3628. &next_token);
  3629. if (token == next_token)
  3630. ThrowPointExpectedException(token,exception);
  3631. break;
  3632. }
  3633. status=MagickFalse;
  3634. break;
  3635. }
  3636. default:
  3637. {
  3638. status=MagickFalse;
  3639. break;
  3640. }
  3641. }
  3642. if (status == MagickFalse)
  3643. break;
  3644. if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
  3645. (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
  3646. (fabs(affine.sy-1.0) >= MagickEpsilon) ||
  3647. (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
  3648. {
  3649. graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
  3650. graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
  3651. graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
  3652. graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
  3653. graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
  3654. current.tx;
  3655. graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
  3656. current.ty;
  3657. }
  3658. if (primitive_type == UndefinedPrimitive)
  3659. {
  3660. if (*q == '\0')
  3661. {
  3662. if (number_stops > 1)
  3663. {
  3664. GradientType
  3665. type;
  3666. type=LinearGradient;
  3667. if (draw_info->gradient.type == RadialGradient)
  3668. type=RadialGradient;
  3669. (void) GradientImage(image,type,PadSpread,stops,number_stops,
  3670. exception);
  3671. }
  3672. if (number_stops > 0)
  3673. stops=(StopInfo *) RelinquishMagickMemory(stops);
  3674. }
  3675. if ((image->debug != MagickFalse) && (q > p))
  3676. (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int)
  3677. (q-p-1),p);
  3678. continue;
  3679. }
  3680. /*
  3681. Parse the primitive attributes.
  3682. */
  3683. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
  3684. if ((primitive_info[i].primitive == TextPrimitive) ||
  3685. (primitive_info[i].primitive == ImagePrimitive))
  3686. if (primitive_info[i].text != (char *) NULL)
  3687. primitive_info[i].text=DestroyString(primitive_info[i].text);
  3688. i=0;
  3689. mvg_info.offset=i;
  3690. j=0;
  3691. primitive_info[0].point.x=0.0;
  3692. primitive_info[0].point.y=0.0;
  3693. primitive_info[0].coordinates=0;
  3694. primitive_info[0].method=FloodfillMethod;
  3695. primitive_info[0].closed_subpath=MagickFalse;
  3696. for (x=0; *q != '\0'; x++)
  3697. {
  3698. /*
  3699. Define points.
  3700. */
  3701. if (IsPoint(q) == MagickFalse)
  3702. break;
  3703. GetNextToken(q,&q,extent,token);
  3704. point.x=StringToDouble(token,&next_token);
  3705. if (token == next_token)
  3706. ThrowPointExpectedException(token,exception);
  3707. GetNextToken(q,&q,extent,token);
  3708. if (*token == ',')
  3709. GetNextToken(q,&q,extent,token);
  3710. point.y=StringToDouble(token,&next_token);
  3711. if (token == next_token)
  3712. ThrowPointExpectedException(token,exception);
  3713. GetNextToken(q,(const char **) NULL,extent,token);
  3714. if (*token == ',')
  3715. GetNextToken(q,&q,extent,token);
  3716. primitive_info[i].primitive=primitive_type;
  3717. primitive_info[i].point=point;
  3718. primitive_info[i].coordinates=0;
  3719. primitive_info[i].method=FloodfillMethod;
  3720. primitive_info[i].closed_subpath=MagickFalse;
  3721. i++;
  3722. mvg_info.offset=i;
  3723. if (i < (ssize_t) number_points)
  3724. continue;
  3725. status&=CheckPrimitiveExtent(&mvg_info,number_points);
  3726. }
  3727. if (status == MagickFalse)
  3728. break;
  3729. if ((primitive_info[j].primitive == TextPrimitive) ||
  3730. (primitive_info[j].primitive == ImagePrimitive))
  3731. if (primitive_info[j].text != (char *) NULL)
  3732. primitive_info[j].text=DestroyString(primitive_info[j].text);
  3733. primitive_info[j].primitive=primitive_type;
  3734. primitive_info[j].coordinates=(size_t) x;
  3735. primitive_info[j].method=FloodfillMethod;
  3736. primitive_info[j].closed_subpath=MagickFalse;
  3737. /*
  3738. Circumscribe primitive within a circle.
  3739. */
  3740. bounds.x1=primitive_info[j].point.x;
  3741. bounds.y1=primitive_info[j].point.y;
  3742. bounds.x2=primitive_info[j].point.x;
  3743. bounds.y2=primitive_info[j].point.y;
  3744. for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
  3745. {
  3746. point=primitive_info[j+k].point;
  3747. if (point.x < bounds.x1)
  3748. bounds.x1=point.x;
  3749. if (point.y < bounds.y1)
  3750. bounds.y1=point.y;
  3751. if (point.x > bounds.x2)
  3752. bounds.x2=point.x;
  3753. if (point.y > bounds.y2)
  3754. bounds.y2=point.y;
  3755. }
  3756. /*
  3757. Speculate how many points our primitive might consume.
  3758. */
  3759. coordinates=(double) primitive_info[j].coordinates;
  3760. switch (primitive_type)
  3761. {
  3762. case RectanglePrimitive:
  3763. {
  3764. coordinates*=5.0;
  3765. break;
  3766. }
  3767. case RoundRectanglePrimitive:
  3768. {
  3769. double
  3770. alpha,
  3771. beta,
  3772. radius;
  3773. alpha=bounds.x2-bounds.x1;
  3774. beta=bounds.y2-bounds.y1;
  3775. radius=hypot((double) alpha,(double) beta);
  3776. coordinates*=5.0;
  3777. coordinates+=2.0*((size_t) ceil((double) MagickPI*radius))+6.0*
  3778. BezierQuantum+360.0;
  3779. break;
  3780. }
  3781. case BezierPrimitive:
  3782. {
  3783. coordinates=(double) (BezierQuantum*primitive_info[j].coordinates);
  3784. if (primitive_info[j].coordinates > (107*BezierQuantum))
  3785. {
  3786. (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
  3787. "TooManyBezierCoordinates","`%s'",token);
  3788. status=MagickFalse;
  3789. break;
  3790. }
  3791. break;
  3792. }
  3793. case PathPrimitive:
  3794. {
  3795. char
  3796. *s,
  3797. *t;
  3798. GetNextToken(q,&q,extent,token);
  3799. coordinates=1.0;
  3800. t=token;
  3801. for (s=token; *s != '\0'; s=t)
  3802. {
  3803. double
  3804. value;
  3805. value=StringToDouble(s,&t);
  3806. (void) value;
  3807. if (s == t)
  3808. {
  3809. t++;
  3810. continue;
  3811. }
  3812. coordinates++;
  3813. }
  3814. for (s=token; *s != '\0'; s++)
  3815. if (strspn(s,"AaCcQqSsTt") != 0)
  3816. coordinates+=(20.0*BezierQuantum)+360.0;
  3817. break;
  3818. }
  3819. case CirclePrimitive:
  3820. case ArcPrimitive:
  3821. case EllipsePrimitive:
  3822. {
  3823. double
  3824. alpha,
  3825. beta,
  3826. radius;
  3827. alpha=bounds.x2-bounds.x1;
  3828. beta=bounds.y2-bounds.y1;
  3829. radius=hypot(alpha,beta);
  3830. coordinates=2.0*(ceil(MagickPI*radius))+6.0*BezierQuantum+360.0;
  3831. break;
  3832. }
  3833. default:
  3834. break;
  3835. }
  3836. if (coordinates > MaxBezierCoordinates)
  3837. {
  3838. (void) ThrowMagickException(exception,GetMagickModule(),
  3839. ResourceLimitError,"TooManyBezierCoordinates","`%s'",token);
  3840. status=MagickFalse;
  3841. }
  3842. if (status == MagickFalse)
  3843. break;
  3844. if (((size_t) (i+coordinates)) >= number_points)
  3845. {
  3846. /*
  3847. Resize based on speculative points required by primitive.
  3848. */
  3849. number_points+=coordinates+1;
  3850. if (number_points < (size_t) coordinates)
  3851. {
  3852. (void) ThrowMagickException(exception,GetMagickModule(),
  3853. ResourceLimitError,"MemoryAllocationFailed","`%s'",
  3854. image->filename);
  3855. break;
  3856. }
  3857. mvg_info.offset=i;
  3858. status&=CheckPrimitiveExtent(&mvg_info,number_points);
  3859. }
  3860. status&=CheckPrimitiveExtent(&mvg_info,PrimitiveExtentPad);
  3861. if (status == MagickFalse)
  3862. break;
  3863. mvg_info.offset=j;
  3864. switch (primitive_type)
  3865. {
  3866. case PointPrimitive:
  3867. default:
  3868. {
  3869. if (primitive_info[j].coordinates != 1)
  3870. {
  3871. status=MagickFalse;
  3872. break;
  3873. }
  3874. status&=TracePoint(primitive_info+j,primitive_info[j].point);
  3875. i=(ssize_t) (j+primitive_info[j].coordinates);
  3876. break;
  3877. }
  3878. case LinePrimitive:
  3879. {
  3880. if (primitive_info[j].coordinates != 2)
  3881. {
  3882. status=MagickFalse;
  3883. break;
  3884. }
  3885. status&=TraceLine(primitive_info+j,primitive_info[j].point,
  3886. primitive_info[j+1].point);
  3887. i=(ssize_t) (j+primitive_info[j].coordinates);
  3888. break;
  3889. }
  3890. case RectanglePrimitive:
  3891. {
  3892. if (primitive_info[j].coordinates != 2)
  3893. {
  3894. status=MagickFalse;
  3895. break;
  3896. }
  3897. status&=TraceRectangle(primitive_info+j,primitive_info[j].point,
  3898. primitive_info[j+1].point);
  3899. i=(ssize_t) (j+primitive_info[j].coordinates);
  3900. break;
  3901. }
  3902. case RoundRectanglePrimitive:
  3903. {
  3904. if (primitive_info[j].coordinates != 3)
  3905. {
  3906. status=MagickFalse;
  3907. break;
  3908. }
  3909. if ((primitive_info[j+2].point.x < 0.0) ||
  3910. (primitive_info[j+2].point.y < 0.0))
  3911. {
  3912. status=MagickFalse;
  3913. break;
  3914. }
  3915. if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
  3916. {
  3917. status=MagickFalse;
  3918. break;
  3919. }
  3920. if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
  3921. {
  3922. status=MagickFalse;
  3923. break;
  3924. }
  3925. status&=TraceRoundRectangle(&mvg_info,primitive_info[j].point,
  3926. primitive_info[j+1].point,primitive_info[j+2].point);
  3927. i=(ssize_t) (j+primitive_info[j].coordinates);
  3928. break;
  3929. }
  3930. case ArcPrimitive:
  3931. {
  3932. if (primitive_info[j].coordinates != 3)
  3933. {
  3934. primitive_type=UndefinedPrimitive;
  3935. break;
  3936. }
  3937. status&=TraceArc(&mvg_info,primitive_info[j].point,
  3938. primitive_info[j+1].point,primitive_info[j+2].point);
  3939. i=(ssize_t) (j+primitive_info[j].coordinates);
  3940. break;
  3941. }
  3942. case EllipsePrimitive:
  3943. {
  3944. if (primitive_info[j].coordinates != 3)
  3945. {
  3946. status=MagickFalse;
  3947. break;
  3948. }
  3949. if ((primitive_info[j+1].point.x < 0.0) ||
  3950. (primitive_info[j+1].point.y < 0.0))
  3951. {
  3952. status=MagickFalse;
  3953. break;
  3954. }
  3955. status&=TraceEllipse(&mvg_info,primitive_info[j].point,
  3956. primitive_info[j+1].point,primitive_info[j+2].point);
  3957. i=(ssize_t) (j+primitive_info[j].coordinates);
  3958. break;
  3959. }
  3960. case CirclePrimitive:
  3961. {
  3962. if (primitive_info[j].coordinates != 2)
  3963. {
  3964. status=MagickFalse;
  3965. break;
  3966. }
  3967. status&=TraceCircle(&mvg_info,primitive_info[j].point,
  3968. primitive_info[j+1].point);
  3969. i=(ssize_t) (j+primitive_info[j].coordinates);
  3970. break;
  3971. }
  3972. case PolylinePrimitive:
  3973. {
  3974. if (primitive_info[j].coordinates < 1)
  3975. {
  3976. status=MagickFalse;
  3977. break;
  3978. }
  3979. break;
  3980. }
  3981. case PolygonPrimitive:
  3982. {
  3983. if (primitive_info[j].coordinates < 3)
  3984. {
  3985. status=MagickFalse;
  3986. break;
  3987. }
  3988. primitive_info[i]=primitive_info[j];
  3989. primitive_info[i].coordinates=0;
  3990. primitive_info[j].coordinates++;
  3991. primitive_info[j].closed_subpath=MagickTrue;
  3992. i++;
  3993. break;
  3994. }
  3995. case BezierPrimitive:
  3996. {
  3997. if (primitive_info[j].coordinates < 3)
  3998. {
  3999. status=MagickFalse;
  4000. break;
  4001. }
  4002. status&=TraceBezier(&mvg_info,primitive_info[j].coordinates);
  4003. i=(ssize_t) (j+primitive_info[j].coordinates);
  4004. break;
  4005. }
  4006. case PathPrimitive:
  4007. {
  4008. coordinates=(double) TracePath(&mvg_info,token,exception);
  4009. if (coordinates == 0.0)
  4010. {
  4011. status=MagickFalse;
  4012. break;
  4013. }
  4014. i=(ssize_t) (j+coordinates);
  4015. break;
  4016. }
  4017. case AlphaPrimitive:
  4018. case ColorPrimitive:
  4019. {
  4020. ssize_t
  4021. method;
  4022. if (primitive_info[j].coordinates != 1)
  4023. {
  4024. status=MagickFalse;
  4025. break;
  4026. }
  4027. GetNextToken(q,&q,extent,token);
  4028. method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
  4029. if (method == -1)
  4030. {
  4031. status=MagickFalse;
  4032. break;
  4033. }
  4034. primitive_info[j].method=(PaintMethod) method;
  4035. break;
  4036. }
  4037. case TextPrimitive:
  4038. {
  4039. char
  4040. geometry[MagickPathExtent];
  4041. if (primitive_info[j].coordinates != 1)
  4042. {
  4043. status=MagickFalse;
  4044. break;
  4045. }
  4046. if (*token != ',')
  4047. GetNextToken(q,&q,extent,token);
  4048. (void) CloneString(&primitive_info[j].text,token);
  4049. /*
  4050. Compute text cursor offset.
  4051. */
  4052. clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
  4053. if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
  4054. (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
  4055. {
  4056. mvg_info.point=primitive_info->point;
  4057. primitive_info->point.x+=cursor;
  4058. }
  4059. else
  4060. {
  4061. mvg_info.point=primitive_info->point;
  4062. cursor=0.0;
  4063. }
  4064. (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
  4065. primitive_info->point.x,primitive_info->point.y);
  4066. clone_info->render=MagickFalse;
  4067. clone_info->text=AcquireString(token);
  4068. (void) ConcatenateString(&clone_info->text," ");
  4069. status&=GetTypeMetrics(image,clone_info,&metrics,exception);
  4070. clone_info=DestroyDrawInfo(clone_info);
  4071. cursor+=metrics.width;
  4072. break;
  4073. }
  4074. case ImagePrimitive:
  4075. {
  4076. if (primitive_info[j].coordinates != 2)
  4077. {
  4078. status=MagickFalse;
  4079. break;
  4080. }
  4081. GetNextToken(q,&q,extent,token);
  4082. (void) CloneString(&primitive_info[j].text,token);
  4083. break;
  4084. }
  4085. }
  4086. mvg_info.offset=i;
  4087. if ((image->debug != MagickFalse) && (q > p))
  4088. (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p-1),
  4089. p);
  4090. if (status == MagickFalse)
  4091. break;
  4092. primitive_info[i].primitive=UndefinedPrimitive;
  4093. if (i == 0)
  4094. continue;
  4095. /*
  4096. Transform points.
  4097. */
  4098. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
  4099. {
  4100. point=primitive_info[i].point;
  4101. primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
  4102. graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
  4103. primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
  4104. graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
  4105. point=primitive_info[i].point;
  4106. if (point.x < graphic_context[n]->bounds.x1)
  4107. graphic_context[n]->bounds.x1=point.x;
  4108. if (point.y < graphic_context[n]->bounds.y1)
  4109. graphic_context[n]->bounds.y1=point.y;
  4110. if (point.x > graphic_context[n]->bounds.x2)
  4111. graphic_context[n]->bounds.x2=point.x;
  4112. if (point.y > graphic_context[n]->bounds.y2)
  4113. graphic_context[n]->bounds.y2=point.y;
  4114. if (primitive_info[i].primitive == ImagePrimitive)
  4115. break;
  4116. if (i >= (ssize_t) number_points)
  4117. ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
  4118. }
  4119. if (graphic_context[n]->render != MagickFalse)
  4120. {
  4121. if ((n != 0) && (draw_info->compliance != SVGCompliance) &&
  4122. (graphic_context[n]->clip_mask != (char *) NULL) &&
  4123. (LocaleCompare(graphic_context[n]->clip_mask,
  4124. graphic_context[n-1]->clip_mask) != 0))
  4125. {
  4126. const char
  4127. *clip_path;
  4128. clip_path=(const char *) GetValueFromSplayTree(macros,
  4129. graphic_context[n]->clip_mask);
  4130. if (clip_path != (const char *) NULL)
  4131. (void) SetImageArtifact(image,graphic_context[n]->clip_mask,
  4132. clip_path);
  4133. status&=DrawClipPath(image,graphic_context[n],
  4134. graphic_context[n]->clip_mask,exception);
  4135. }
  4136. status&=DrawPrimitive(image,graphic_context[n],primitive_info,
  4137. exception);
  4138. }
  4139. proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
  4140. primitive_extent);
  4141. if (proceed == MagickFalse)
  4142. break;
  4143. if (status == 0)
  4144. break;
  4145. }
  4146. if (image->debug != MagickFalse)
  4147. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
  4148. /*
  4149. Relinquish resources.
  4150. */
  4151. macros=DestroySplayTree(macros);
  4152. token=DestroyString(token);
  4153. if (primitive_info != (PrimitiveInfo *) NULL)
  4154. {
  4155. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
  4156. if ((primitive_info[i].primitive == TextPrimitive) ||
  4157. (primitive_info[i].primitive == ImagePrimitive))
  4158. if (primitive_info[i].text != (char *) NULL)
  4159. primitive_info[i].text=DestroyString(primitive_info[i].text);
  4160. primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
  4161. }
  4162. primitive=DestroyString(primitive);
  4163. if (stops != (StopInfo *) NULL)
  4164. stops=(StopInfo *) RelinquishMagickMemory(stops);
  4165. for ( ; n >= 0; n--)
  4166. graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
  4167. graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
  4168. if (status == MagickFalse)
  4169. ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
  4170. keyword);
  4171. return(status != 0 ? MagickTrue : MagickFalse);
  4172. }
  4173. MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
  4174. ExceptionInfo *exception)
  4175. {
  4176. return(RenderMVGContent(image,draw_info,0,exception));
  4177. }
  4178. /*
  4179. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4180. % %
  4181. % %
  4182. % %
  4183. % D r a w P a t t e r n P a t h %
  4184. % %
  4185. % %
  4186. % %
  4187. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4188. %
  4189. % DrawPatternPath() draws a pattern.
  4190. %
  4191. % The format of the DrawPatternPath method is:
  4192. %
  4193. % MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
  4194. % const char *name,Image **pattern,ExceptionInfo *exception)
  4195. %
  4196. % A description of each parameter follows:
  4197. %
  4198. % o image: the image.
  4199. %
  4200. % o draw_info: the draw info.
  4201. %
  4202. % o name: the pattern name.
  4203. %
  4204. % o image: the image.
  4205. %
  4206. % o exception: return any errors or warnings in this structure.
  4207. %
  4208. */
  4209. MagickExport MagickBooleanType DrawPatternPath(Image *image,
  4210. const DrawInfo *draw_info,const char *name,Image **pattern,
  4211. ExceptionInfo *exception)
  4212. {
  4213. char
  4214. property[MagickPathExtent];
  4215. const char
  4216. *geometry,
  4217. *path,
  4218. *type;
  4219. DrawInfo
  4220. *clone_info;
  4221. ImageInfo
  4222. *image_info;
  4223. MagickBooleanType
  4224. status;
  4225. assert(image != (Image *) NULL);
  4226. assert(image->signature == MagickCoreSignature);
  4227. if (image->debug != MagickFalse)
  4228. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  4229. assert(draw_info != (const DrawInfo *) NULL);
  4230. assert(name != (const char *) NULL);
  4231. (void) FormatLocaleString(property,MagickPathExtent,"%s",name);
  4232. path=GetImageArtifact(image,property);
  4233. if (path == (const char *) NULL)
  4234. return(MagickFalse);
  4235. (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name);
  4236. geometry=GetImageArtifact(image,property);
  4237. if (geometry == (const char *) NULL)
  4238. return(MagickFalse);
  4239. if ((*pattern) != (Image *) NULL)
  4240. *pattern=DestroyImage(*pattern);
  4241. image_info=AcquireImageInfo();
  4242. image_info->size=AcquireString(geometry);
  4243. *pattern=AcquireImage(image_info,exception);
  4244. image_info=DestroyImageInfo(image_info);
  4245. (void) QueryColorCompliance("#000000ff",AllCompliance,
  4246. &(*pattern)->background_color,exception);
  4247. (void) SetImageBackgroundColor(*pattern,exception);
  4248. if (image->debug != MagickFalse)
  4249. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4250. "begin pattern-path %s %s",name,geometry);
  4251. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  4252. clone_info->fill_pattern=NewImageList();
  4253. clone_info->stroke_pattern=NewImageList();
  4254. (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name);
  4255. type=GetImageArtifact(image,property);
  4256. if (type != (const char *) NULL)
  4257. clone_info->gradient.type=(GradientType) ParseCommandOption(
  4258. MagickGradientOptions,MagickFalse,type);
  4259. (void) CloneString(&clone_info->primitive,path);
  4260. status=RenderMVGContent(*pattern,clone_info,0,exception);
  4261. clone_info=DestroyDrawInfo(clone_info);
  4262. if (image->debug != MagickFalse)
  4263. (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
  4264. return(status);
  4265. }
  4266. /*
  4267. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4268. % %
  4269. % %
  4270. % %
  4271. + D r a w P o l y g o n P r i m i t i v e %
  4272. % %
  4273. % %
  4274. % %
  4275. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4276. %
  4277. % DrawPolygonPrimitive() draws a polygon on the image.
  4278. %
  4279. % The format of the DrawPolygonPrimitive method is:
  4280. %
  4281. % MagickBooleanType DrawPolygonPrimitive(Image *image,
  4282. % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  4283. % ExceptionInfo *exception)
  4284. %
  4285. % A description of each parameter follows:
  4286. %
  4287. % o image: the image.
  4288. %
  4289. % o draw_info: the draw info.
  4290. %
  4291. % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
  4292. %
  4293. % o exception: return any errors or warnings in this structure.
  4294. %
  4295. */
  4296. static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
  4297. {
  4298. register ssize_t
  4299. i;
  4300. assert(polygon_info != (PolygonInfo **) NULL);
  4301. for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
  4302. if (polygon_info[i] != (PolygonInfo *) NULL)
  4303. polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
  4304. polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
  4305. return(polygon_info);
  4306. }
  4307. static PolygonInfo **AcquirePolygonThreadSet(
  4308. const PrimitiveInfo *primitive_info)
  4309. {
  4310. PathInfo
  4311. *magick_restrict path_info;
  4312. PolygonInfo
  4313. **polygon_info;
  4314. register ssize_t
  4315. i;
  4316. size_t
  4317. number_threads;
  4318. number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
  4319. polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
  4320. sizeof(*polygon_info));
  4321. if (polygon_info == (PolygonInfo **) NULL)
  4322. return((PolygonInfo **) NULL);
  4323. (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
  4324. path_info=ConvertPrimitiveToPath(primitive_info);
  4325. if (path_info == (PathInfo *) NULL)
  4326. return(DestroyPolygonThreadSet(polygon_info));
  4327. for (i=0; i < (ssize_t) number_threads; i++)
  4328. {
  4329. polygon_info[i]=ConvertPathToPolygon(path_info);
  4330. if (polygon_info[i] == (PolygonInfo *) NULL)
  4331. return(DestroyPolygonThreadSet(polygon_info));
  4332. }
  4333. path_info=(PathInfo *) RelinquishMagickMemory(path_info);
  4334. return(polygon_info);
  4335. }
  4336. static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
  4337. const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
  4338. const ssize_t y,double *stroke_alpha)
  4339. {
  4340. double
  4341. alpha,
  4342. beta,
  4343. distance,
  4344. subpath_alpha;
  4345. PointInfo
  4346. delta;
  4347. register const PointInfo
  4348. *q;
  4349. register EdgeInfo
  4350. *p;
  4351. register ssize_t
  4352. i;
  4353. ssize_t
  4354. j,
  4355. winding_number;
  4356. /*
  4357. Compute fill & stroke opacity for this (x,y) point.
  4358. */
  4359. *stroke_alpha=0.0;
  4360. subpath_alpha=0.0;
  4361. p=polygon_info->edges;
  4362. for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
  4363. {
  4364. if ((double) y <= (p->bounds.y1-mid-0.5))
  4365. break;
  4366. if ((double) y > (p->bounds.y2+mid+0.5))
  4367. {
  4368. (void) DestroyEdge(polygon_info,(size_t) j);
  4369. continue;
  4370. }
  4371. if (((double) x <= (p->bounds.x1-mid-0.5)) ||
  4372. ((double) x > (p->bounds.x2+mid+0.5)))
  4373. continue;
  4374. i=(ssize_t) MagickMax((double) p->highwater,1.0);
  4375. for ( ; i < (ssize_t) p->number_points; i++)
  4376. {
  4377. if ((double) y <= (p->points[i-1].y-mid-0.5))
  4378. break;
  4379. if ((double) y > (p->points[i].y+mid+0.5))
  4380. continue;
  4381. if (p->scanline != (double) y)
  4382. {
  4383. p->scanline=(double) y;
  4384. p->highwater=(size_t) i;
  4385. }
  4386. /*
  4387. Compute distance between a point and an edge.
  4388. */
  4389. q=p->points+i-1;
  4390. delta.x=(q+1)->x-q->x;
  4391. delta.y=(q+1)->y-q->y;
  4392. beta=delta.x*(x-q->x)+delta.y*(y-q->y);
  4393. if (beta <= 0.0)
  4394. {
  4395. delta.x=(double) x-q->x;
  4396. delta.y=(double) y-q->y;
  4397. distance=delta.x*delta.x+delta.y*delta.y;
  4398. }
  4399. else
  4400. {
  4401. alpha=delta.x*delta.x+delta.y*delta.y;
  4402. if (beta >= alpha)
  4403. {
  4404. delta.x=(double) x-(q+1)->x;
  4405. delta.y=(double) y-(q+1)->y;
  4406. distance=delta.x*delta.x+delta.y*delta.y;
  4407. }
  4408. else
  4409. {
  4410. alpha=PerceptibleReciprocal(alpha);
  4411. beta=delta.x*(y-q->y)-delta.y*(x-q->x);
  4412. distance=alpha*beta*beta;
  4413. }
  4414. }
  4415. /*
  4416. Compute stroke & subpath opacity.
  4417. */
  4418. beta=0.0;
  4419. if (p->ghostline == MagickFalse)
  4420. {
  4421. alpha=mid+0.5;
  4422. if ((*stroke_alpha < 1.0) &&
  4423. (distance <= ((alpha+0.25)*(alpha+0.25))))
  4424. {
  4425. alpha=mid-0.5;
  4426. if (distance <= ((alpha+0.25)*(alpha+0.25)))
  4427. *stroke_alpha=1.0;
  4428. else
  4429. {
  4430. beta=1.0;
  4431. if (fabs(distance-1.0) >= MagickEpsilon)
  4432. beta=sqrt((double) distance);
  4433. alpha=beta-mid-0.5;
  4434. if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
  4435. *stroke_alpha=(alpha-0.25)*(alpha-0.25);
  4436. }
  4437. }
  4438. }
  4439. if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
  4440. continue;
  4441. if (distance <= 0.0)
  4442. {
  4443. subpath_alpha=1.0;
  4444. continue;
  4445. }
  4446. if (distance > 1.0)
  4447. continue;
  4448. if (fabs(beta) < MagickEpsilon)
  4449. {
  4450. beta=1.0;
  4451. if (fabs(distance-1.0) >= MagickEpsilon)
  4452. beta=sqrt(distance);
  4453. }
  4454. alpha=beta-1.0;
  4455. if (subpath_alpha < (alpha*alpha))
  4456. subpath_alpha=alpha*alpha;
  4457. }
  4458. }
  4459. /*
  4460. Compute fill opacity.
  4461. */
  4462. if (fill == MagickFalse)
  4463. return(0.0);
  4464. if (subpath_alpha >= 1.0)
  4465. return(1.0);
  4466. /*
  4467. Determine winding number.
  4468. */
  4469. winding_number=0;
  4470. p=polygon_info->edges;
  4471. for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
  4472. {
  4473. if ((double) y <= p->bounds.y1)
  4474. break;
  4475. if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
  4476. continue;
  4477. if ((double) x > p->bounds.x2)
  4478. {
  4479. winding_number+=p->direction ? 1 : -1;
  4480. continue;
  4481. }
  4482. i=(ssize_t) MagickMax((double) p->highwater,1.0);
  4483. for ( ; i < (ssize_t) (p->number_points-1); i++)
  4484. if ((double) y <= p->points[i].y)
  4485. break;
  4486. q=p->points+i-1;
  4487. if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
  4488. winding_number+=p->direction ? 1 : -1;
  4489. }
  4490. if (fill_rule != NonZeroRule)
  4491. {
  4492. if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
  4493. return(1.0);
  4494. }
  4495. else
  4496. if (MagickAbsoluteValue(winding_number) != 0)
  4497. return(1.0);
  4498. return(subpath_alpha);
  4499. }
  4500. static MagickBooleanType DrawPolygonPrimitive(Image *image,
  4501. const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  4502. ExceptionInfo *exception)
  4503. {
  4504. CacheView
  4505. *image_view;
  4506. MagickBooleanType
  4507. fill,
  4508. status;
  4509. double
  4510. mid;
  4511. PolygonInfo
  4512. **magick_restrict polygon_info;
  4513. register EdgeInfo
  4514. *p;
  4515. register ssize_t
  4516. i;
  4517. SegmentInfo
  4518. bounds;
  4519. ssize_t
  4520. start_y,
  4521. stop_y,
  4522. y;
  4523. assert(image != (Image *) NULL);
  4524. assert(image->signature == MagickCoreSignature);
  4525. if (image->debug != MagickFalse)
  4526. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  4527. assert(draw_info != (DrawInfo *) NULL);
  4528. assert(draw_info->signature == MagickCoreSignature);
  4529. assert(primitive_info != (PrimitiveInfo *) NULL);
  4530. if (primitive_info->coordinates <= 1)
  4531. return(MagickTrue);
  4532. /*
  4533. Compute bounding box.
  4534. */
  4535. polygon_info=AcquirePolygonThreadSet(primitive_info);
  4536. if (polygon_info == (PolygonInfo **) NULL)
  4537. return(MagickFalse);
  4538. DisableMSCWarning(4127)
  4539. if (0)
  4540. {
  4541. status=DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
  4542. if (status == MagickFalse)
  4543. {
  4544. polygon_info=DestroyPolygonThreadSet(polygon_info);
  4545. return(status);
  4546. }
  4547. }
  4548. RestoreMSCWarning
  4549. if (image->debug != MagickFalse)
  4550. (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
  4551. fill=(primitive_info->method == FillToBorderMethod) ||
  4552. (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
  4553. mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
  4554. bounds=polygon_info[0]->edges[0].bounds;
  4555. for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
  4556. {
  4557. p=polygon_info[0]->edges+i;
  4558. if (p->bounds.x1 < bounds.x1)
  4559. bounds.x1=p->bounds.x1;
  4560. if (p->bounds.y1 < bounds.y1)
  4561. bounds.y1=p->bounds.y1;
  4562. if (p->bounds.x2 > bounds.x2)
  4563. bounds.x2=p->bounds.x2;
  4564. if (p->bounds.y2 > bounds.y2)
  4565. bounds.y2=p->bounds.y2;
  4566. }
  4567. bounds.x1-=(mid+1.0);
  4568. bounds.y1-=(mid+1.0);
  4569. bounds.x2+=(mid+1.0);
  4570. bounds.y2+=(mid+1.0);
  4571. if ((bounds.x1 >= (double) image->columns) ||
  4572. (bounds.y1 >= (double) image->rows) ||
  4573. (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
  4574. {
  4575. polygon_info=DestroyPolygonThreadSet(polygon_info);
  4576. return(MagickTrue); /* virtual polygon */
  4577. }
  4578. bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
  4579. (double) image->columns-1.0 : bounds.x1;
  4580. bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
  4581. (double) image->rows-1.0 : bounds.y1;
  4582. bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
  4583. (double) image->columns-1.0 : bounds.x2;
  4584. bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
  4585. (double) image->rows-1.0 : bounds.y2;
  4586. status=MagickTrue;
  4587. image_view=AcquireAuthenticCacheView(image,exception);
  4588. if ((primitive_info->coordinates == 1) ||
  4589. (polygon_info[0]->number_edges == 0))
  4590. {
  4591. /*
  4592. Draw point.
  4593. */
  4594. start_y=(ssize_t) ceil(bounds.y1-0.5);
  4595. stop_y=(ssize_t) floor(bounds.y2+0.5);
  4596. #if defined(MAGICKCORE_OPENMP_SUPPORT)
  4597. #pragma omp parallel for schedule(static) shared(status) \
  4598. magick_number_threads(image,image,stop_y-start_y+1,1)
  4599. #endif
  4600. for (y=start_y; y <= stop_y; y++)
  4601. {
  4602. MagickBooleanType
  4603. sync;
  4604. PixelInfo
  4605. pixel;
  4606. register ssize_t
  4607. x;
  4608. register Quantum
  4609. *magick_restrict q;
  4610. ssize_t
  4611. start_x,
  4612. stop_x;
  4613. if (status == MagickFalse)
  4614. continue;
  4615. start_x=(ssize_t) ceil(bounds.x1-0.5);
  4616. stop_x=(ssize_t) floor(bounds.x2+0.5);
  4617. x=start_x;
  4618. q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop_x-x+1),1,
  4619. exception);
  4620. if (q == (Quantum *) NULL)
  4621. {
  4622. status=MagickFalse;
  4623. continue;
  4624. }
  4625. GetPixelInfo(image,&pixel);
  4626. for ( ; x <= stop_x; x++)
  4627. {
  4628. if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
  4629. (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
  4630. {
  4631. GetFillColor(draw_info,x-start_x,y-start_y,&pixel,exception);
  4632. SetPixelViaPixelInfo(image,&pixel,q);
  4633. }
  4634. q+=GetPixelChannels(image);
  4635. }
  4636. sync=SyncCacheViewAuthenticPixels(image_view,exception);
  4637. if (sync == MagickFalse)
  4638. status=MagickFalse;
  4639. }
  4640. image_view=DestroyCacheView(image_view);
  4641. polygon_info=DestroyPolygonThreadSet(polygon_info);
  4642. if (image->debug != MagickFalse)
  4643. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4644. " end draw-polygon");
  4645. return(status);
  4646. }
  4647. /*
  4648. Draw polygon or line.
  4649. */
  4650. start_y=(ssize_t) ceil(bounds.y1-0.5);
  4651. stop_y=(ssize_t) floor(bounds.y2+0.5);
  4652. #if defined(MAGICKCORE_OPENMP_SUPPORT)
  4653. #pragma omp parallel for schedule(static) shared(status) \
  4654. magick_number_threads(image,image,stop_y-start_y+1,1)
  4655. #endif
  4656. for (y=start_y; y <= stop_y; y++)
  4657. {
  4658. const int
  4659. id = GetOpenMPThreadId();
  4660. register Quantum
  4661. *magick_restrict q;
  4662. register ssize_t
  4663. x;
  4664. ssize_t
  4665. start_x,
  4666. stop_x;
  4667. if (status == MagickFalse)
  4668. continue;
  4669. start_x=(ssize_t) ceil(bounds.x1-0.5);
  4670. stop_x=(ssize_t) floor(bounds.x2+0.5);
  4671. q=GetCacheViewAuthenticPixels(image_view,start_x,y,(size_t) (stop_x-start_x+
  4672. 1),1,exception);
  4673. if (q == (Quantum *) NULL)
  4674. {
  4675. status=MagickFalse;
  4676. continue;
  4677. }
  4678. for (x=start_x; x <= stop_x; x++)
  4679. {
  4680. double
  4681. fill_alpha,
  4682. stroke_alpha;
  4683. PixelInfo
  4684. fill_color,
  4685. stroke_color;
  4686. /*
  4687. Fill and/or stroke.
  4688. */
  4689. fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
  4690. x,y,&stroke_alpha);
  4691. if (draw_info->stroke_antialias == MagickFalse)
  4692. {
  4693. fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0;
  4694. stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0;
  4695. }
  4696. GetFillColor(draw_info,x-start_x,y-start_y,&fill_color,exception);
  4697. CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
  4698. (double) GetPixelAlpha(image,q),q);
  4699. GetStrokeColor(draw_info,x-start_x,y-start_y,&stroke_color,exception);
  4700. CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
  4701. (double) GetPixelAlpha(image,q),q);
  4702. q+=GetPixelChannels(image);
  4703. }
  4704. if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
  4705. status=MagickFalse;
  4706. }
  4707. image_view=DestroyCacheView(image_view);
  4708. polygon_info=DestroyPolygonThreadSet(polygon_info);
  4709. if (image->debug != MagickFalse)
  4710. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
  4711. return(status);
  4712. }
  4713. /*
  4714. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4715. % %
  4716. % %
  4717. % %
  4718. % D r a w P r i m i t i v e %
  4719. % %
  4720. % %
  4721. % %
  4722. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4723. %
  4724. % DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
  4725. %
  4726. % The format of the DrawPrimitive method is:
  4727. %
  4728. % MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
  4729. % PrimitiveInfo *primitive_info,ExceptionInfo *exception)
  4730. %
  4731. % A description of each parameter follows:
  4732. %
  4733. % o image: the image.
  4734. %
  4735. % o draw_info: the draw info.
  4736. %
  4737. % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
  4738. %
  4739. % o exception: return any errors or warnings in this structure.
  4740. %
  4741. */
  4742. static inline double ConstrainCoordinate(double x)
  4743. {
  4744. if (x < (double) -SSIZE_MAX)
  4745. return((double) -SSIZE_MAX);
  4746. if (x > (double) SSIZE_MAX)
  4747. return((double) SSIZE_MAX);
  4748. return(x);
  4749. }
  4750. static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
  4751. {
  4752. const char
  4753. *methods[] =
  4754. {
  4755. "point",
  4756. "replace",
  4757. "floodfill",
  4758. "filltoborder",
  4759. "reset",
  4760. "?"
  4761. };
  4762. PointInfo
  4763. p,
  4764. point,
  4765. q;
  4766. register ssize_t
  4767. i,
  4768. x;
  4769. ssize_t
  4770. coordinates,
  4771. y;
  4772. x=(ssize_t) ceil(primitive_info->point.x-0.5);
  4773. y=(ssize_t) ceil(primitive_info->point.y-0.5);
  4774. switch (primitive_info->primitive)
  4775. {
  4776. case AlphaPrimitive:
  4777. {
  4778. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4779. "AlphaPrimitive %.20g,%.20g %s",(double) x,(double) y,
  4780. methods[primitive_info->method]);
  4781. return;
  4782. }
  4783. case ColorPrimitive:
  4784. {
  4785. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4786. "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
  4787. methods[primitive_info->method]);
  4788. return;
  4789. }
  4790. case ImagePrimitive:
  4791. {
  4792. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4793. "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
  4794. return;
  4795. }
  4796. case PointPrimitive:
  4797. {
  4798. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4799. "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
  4800. methods[primitive_info->method]);
  4801. return;
  4802. }
  4803. case TextPrimitive:
  4804. {
  4805. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4806. "TextPrimitive %.20g,%.20g",(double) x,(double) y);
  4807. return;
  4808. }
  4809. default:
  4810. break;
  4811. }
  4812. coordinates=0;
  4813. p=primitive_info[0].point;
  4814. q.x=(-1.0);
  4815. q.y=(-1.0);
  4816. for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
  4817. {
  4818. point=primitive_info[i].point;
  4819. if (coordinates <= 0)
  4820. {
  4821. coordinates=(ssize_t) primitive_info[i].coordinates;
  4822. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4823. " begin open (%.20g)",(double) coordinates);
  4824. p=point;
  4825. }
  4826. point=primitive_info[i].point;
  4827. if ((fabs(q.x-point.x) >= MagickEpsilon) ||
  4828. (fabs(q.y-point.y) >= MagickEpsilon))
  4829. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4830. " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
  4831. else
  4832. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4833. " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
  4834. q=point;
  4835. coordinates--;
  4836. if (coordinates > 0)
  4837. continue;
  4838. if ((fabs(p.x-point.x) >= MagickEpsilon) ||
  4839. (fabs(p.y-point.y) >= MagickEpsilon))
  4840. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
  4841. (double) coordinates);
  4842. else
  4843. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
  4844. (double) coordinates);
  4845. }
  4846. }
  4847. MagickExport MagickBooleanType DrawPrimitive(Image *image,
  4848. const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  4849. ExceptionInfo *exception)
  4850. {
  4851. CacheView
  4852. *image_view;
  4853. MagickStatusType
  4854. status;
  4855. register ssize_t
  4856. i,
  4857. x;
  4858. ssize_t
  4859. y;
  4860. if (image->debug != MagickFalse)
  4861. {
  4862. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4863. " begin draw-primitive");
  4864. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  4865. " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
  4866. draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
  4867. draw_info->affine.tx,draw_info->affine.ty);
  4868. }
  4869. status=MagickTrue;
  4870. if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
  4871. ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
  4872. (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
  4873. status=SetImageColorspace(image,sRGBColorspace,exception);
  4874. if (draw_info->compliance == SVGCompliance)
  4875. {
  4876. status&=SetImageMask(image,WritePixelMask,draw_info->clipping_mask,
  4877. exception);
  4878. status&=SetImageMask(image,CompositePixelMask,draw_info->composite_mask,
  4879. exception);
  4880. }
  4881. x=(ssize_t) ceil(ConstrainCoordinate(primitive_info->point.x-0.5));
  4882. y=(ssize_t) ceil(ConstrainCoordinate(primitive_info->point.y-0.5));
  4883. image_view=AcquireAuthenticCacheView(image,exception);
  4884. switch (primitive_info->primitive)
  4885. {
  4886. case AlphaPrimitive:
  4887. {
  4888. if (image->alpha_trait == UndefinedPixelTrait)
  4889. (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
  4890. switch (primitive_info->method)
  4891. {
  4892. case PointMethod:
  4893. default:
  4894. {
  4895. PixelInfo
  4896. pixel;
  4897. register Quantum
  4898. *q;
  4899. q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
  4900. if (q == (Quantum *) NULL)
  4901. break;
  4902. GetFillColor(draw_info,x,y,&pixel,exception);
  4903. SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
  4904. (void) SyncCacheViewAuthenticPixels(image_view,exception);
  4905. break;
  4906. }
  4907. case ReplaceMethod:
  4908. {
  4909. MagickBooleanType
  4910. sync;
  4911. PixelInfo
  4912. pixel,
  4913. target;
  4914. (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
  4915. exception);
  4916. GetPixelInfo(image,&pixel);
  4917. for (y=0; y < (ssize_t) image->rows; y++)
  4918. {
  4919. register Quantum
  4920. *magick_restrict q;
  4921. q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
  4922. exception);
  4923. if (q == (Quantum *) NULL)
  4924. break;
  4925. for (x=0; x < (ssize_t) image->columns; x++)
  4926. {
  4927. GetPixelInfoPixel(image,q,&pixel);
  4928. if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
  4929. {
  4930. q+=GetPixelChannels(image);
  4931. continue;
  4932. }
  4933. GetFillColor(draw_info,x,y,&pixel,exception);
  4934. SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
  4935. q+=GetPixelChannels(image);
  4936. }
  4937. sync=SyncCacheViewAuthenticPixels(image_view,exception);
  4938. if (sync == MagickFalse)
  4939. break;
  4940. }
  4941. break;
  4942. }
  4943. case FloodfillMethod:
  4944. case FillToBorderMethod:
  4945. {
  4946. ChannelType
  4947. channel_mask;
  4948. PixelInfo
  4949. target;
  4950. (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
  4951. &target,exception);
  4952. if (primitive_info->method == FillToBorderMethod)
  4953. {
  4954. target.red=(double) draw_info->border_color.red;
  4955. target.green=(double) draw_info->border_color.green;
  4956. target.blue=(double) draw_info->border_color.blue;
  4957. }
  4958. channel_mask=SetImageChannelMask(image,AlphaChannel);
  4959. status&=FloodfillPaintImage(image,draw_info,&target,x,y,
  4960. primitive_info->method == FloodfillMethod ? MagickFalse :
  4961. MagickTrue,exception);
  4962. (void) SetImageChannelMask(image,channel_mask);
  4963. break;
  4964. }
  4965. case ResetMethod:
  4966. {
  4967. MagickBooleanType
  4968. sync;
  4969. PixelInfo
  4970. pixel;
  4971. for (y=0; y < (ssize_t) image->rows; y++)
  4972. {
  4973. register Quantum
  4974. *magick_restrict q;
  4975. q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
  4976. exception);
  4977. if (q == (Quantum *) NULL)
  4978. break;
  4979. for (x=0; x < (ssize_t) image->columns; x++)
  4980. {
  4981. GetFillColor(draw_info,x,y,&pixel,exception);
  4982. SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
  4983. q+=GetPixelChannels(image);
  4984. }
  4985. sync=SyncCacheViewAuthenticPixels(image_view,exception);
  4986. if (sync == MagickFalse)
  4987. break;
  4988. }
  4989. break;
  4990. }
  4991. }
  4992. break;
  4993. }
  4994. case ColorPrimitive:
  4995. {
  4996. switch (primitive_info->method)
  4997. {
  4998. case PointMethod:
  4999. default:
  5000. {
  5001. PixelInfo
  5002. pixel;
  5003. register Quantum
  5004. *q;
  5005. q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
  5006. if (q == (Quantum *) NULL)
  5007. break;
  5008. GetPixelInfo(image,&pixel);
  5009. GetFillColor(draw_info,x,y,&pixel,exception);
  5010. SetPixelViaPixelInfo(image,&pixel,q);
  5011. (void) SyncCacheViewAuthenticPixels(image_view,exception);
  5012. break;
  5013. }
  5014. case ReplaceMethod:
  5015. {
  5016. MagickBooleanType
  5017. sync;
  5018. PixelInfo
  5019. pixel,
  5020. target;
  5021. (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
  5022. exception);
  5023. for (y=0; y < (ssize_t) image->rows; y++)
  5024. {
  5025. register Quantum
  5026. *magick_restrict q;
  5027. q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
  5028. exception);
  5029. if (q == (Quantum *) NULL)
  5030. break;
  5031. for (x=0; x < (ssize_t) image->columns; x++)
  5032. {
  5033. GetPixelInfoPixel(image,q,&pixel);
  5034. if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
  5035. {
  5036. q+=GetPixelChannels(image);
  5037. continue;
  5038. }
  5039. GetFillColor(draw_info,x,y,&pixel,exception);
  5040. SetPixelViaPixelInfo(image,&pixel,q);
  5041. q+=GetPixelChannels(image);
  5042. }
  5043. sync=SyncCacheViewAuthenticPixels(image_view,exception);
  5044. if (sync == MagickFalse)
  5045. break;
  5046. }
  5047. break;
  5048. }
  5049. case FloodfillMethod:
  5050. case FillToBorderMethod:
  5051. {
  5052. PixelInfo
  5053. target;
  5054. (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
  5055. &target,exception);
  5056. if (primitive_info->method == FillToBorderMethod)
  5057. {
  5058. target.red=(double) draw_info->border_color.red;
  5059. target.green=(double) draw_info->border_color.green;
  5060. target.blue=(double) draw_info->border_color.blue;
  5061. }
  5062. status&=FloodfillPaintImage(image,draw_info,&target,x,y,
  5063. primitive_info->method == FloodfillMethod ? MagickFalse :
  5064. MagickTrue,exception);
  5065. break;
  5066. }
  5067. case ResetMethod:
  5068. {
  5069. MagickBooleanType
  5070. sync;
  5071. PixelInfo
  5072. pixel;
  5073. GetPixelInfo(image,&pixel);
  5074. for (y=0; y < (ssize_t) image->rows; y++)
  5075. {
  5076. register Quantum
  5077. *magick_restrict q;
  5078. q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
  5079. exception);
  5080. if (q == (Quantum *) NULL)
  5081. break;
  5082. for (x=0; x < (ssize_t) image->columns; x++)
  5083. {
  5084. GetFillColor(draw_info,x,y,&pixel,exception);
  5085. SetPixelViaPixelInfo(image,&pixel,q);
  5086. q+=GetPixelChannels(image);
  5087. }
  5088. sync=SyncCacheViewAuthenticPixels(image_view,exception);
  5089. if (sync == MagickFalse)
  5090. break;
  5091. }
  5092. break;
  5093. }
  5094. }
  5095. break;
  5096. }
  5097. case ImagePrimitive:
  5098. {
  5099. AffineMatrix
  5100. affine;
  5101. char
  5102. composite_geometry[MagickPathExtent];
  5103. Image
  5104. *composite_image,
  5105. *composite_images;
  5106. ImageInfo
  5107. *clone_info;
  5108. RectangleInfo
  5109. geometry;
  5110. ssize_t
  5111. x1,
  5112. y1;
  5113. if (primitive_info->text == (char *) NULL)
  5114. break;
  5115. clone_info=AcquireImageInfo();
  5116. if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
  5117. composite_images=ReadInlineImage(clone_info,primitive_info->text,
  5118. exception);
  5119. else
  5120. {
  5121. (void) CopyMagickString(clone_info->filename,primitive_info->text,
  5122. MagickPathExtent);
  5123. composite_images=ReadImage(clone_info,exception);
  5124. }
  5125. clone_info=DestroyImageInfo(clone_info);
  5126. if (composite_images == (Image *) NULL)
  5127. {
  5128. status=0;
  5129. break;
  5130. }
  5131. composite_image=RemoveFirstImageFromList(&composite_images);
  5132. composite_images=DestroyImageList(composite_images);
  5133. (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
  5134. NULL,(void *) NULL);
  5135. x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
  5136. y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
  5137. if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
  5138. ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
  5139. {
  5140. /*
  5141. Resize image.
  5142. */
  5143. (void) FormatLocaleString(composite_geometry,MagickPathExtent,
  5144. "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
  5145. composite_image->filter=image->filter;
  5146. (void) TransformImage(&composite_image,(char *) NULL,
  5147. composite_geometry,exception);
  5148. }
  5149. if (composite_image->alpha_trait == UndefinedPixelTrait)
  5150. (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
  5151. exception);
  5152. if (draw_info->alpha != OpaqueAlpha)
  5153. (void) SetImageAlpha(composite_image,draw_info->alpha,exception);
  5154. SetGeometry(image,&geometry);
  5155. image->gravity=draw_info->gravity;
  5156. geometry.x=x;
  5157. geometry.y=y;
  5158. (void) FormatLocaleString(composite_geometry,MagickPathExtent,
  5159. "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
  5160. composite_image->rows,(double) geometry.x,(double) geometry.y);
  5161. (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
  5162. affine=draw_info->affine;
  5163. affine.tx=(double) geometry.x;
  5164. affine.ty=(double) geometry.y;
  5165. composite_image->interpolate=image->interpolate;
  5166. if ((draw_info->compose == OverCompositeOp) ||
  5167. (draw_info->compose == SrcOverCompositeOp))
  5168. (void) DrawAffineImage(image,composite_image,&affine,exception);
  5169. else
  5170. (void) CompositeImage(image,composite_image,draw_info->compose,
  5171. MagickTrue,geometry.x,geometry.y,exception);
  5172. composite_image=DestroyImage(composite_image);
  5173. break;
  5174. }
  5175. case PointPrimitive:
  5176. {
  5177. PixelInfo
  5178. fill_color;
  5179. register Quantum
  5180. *q;
  5181. if ((y < 0) || (y >= (ssize_t) image->rows))
  5182. break;
  5183. if ((x < 0) || (x >= (ssize_t) image->columns))
  5184. break;
  5185. q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
  5186. if (q == (Quantum *) NULL)
  5187. break;
  5188. GetFillColor(draw_info,x,y,&fill_color,exception);
  5189. CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,
  5190. (double) GetPixelAlpha(image,q),q);
  5191. (void) SyncCacheViewAuthenticPixels(image_view,exception);
  5192. break;
  5193. }
  5194. case TextPrimitive:
  5195. {
  5196. char
  5197. geometry[MagickPathExtent];
  5198. DrawInfo
  5199. *clone_info;
  5200. if (primitive_info->text == (char *) NULL)
  5201. break;
  5202. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  5203. (void) CloneString(&clone_info->text,primitive_info->text);
  5204. (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
  5205. primitive_info->point.x,primitive_info->point.y);
  5206. (void) CloneString(&clone_info->geometry,geometry);
  5207. status&=AnnotateImage(image,clone_info,exception);
  5208. clone_info=DestroyDrawInfo(clone_info);
  5209. break;
  5210. }
  5211. default:
  5212. {
  5213. double
  5214. mid,
  5215. scale;
  5216. DrawInfo
  5217. *clone_info;
  5218. if (IsEventLogging() != MagickFalse)
  5219. LogPrimitiveInfo(primitive_info);
  5220. scale=ExpandAffine(&draw_info->affine);
  5221. if ((draw_info->dash_pattern != (double *) NULL) &&
  5222. (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
  5223. (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
  5224. (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
  5225. {
  5226. /*
  5227. Draw dash polygon.
  5228. */
  5229. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  5230. clone_info->stroke_width=0.0;
  5231. clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
  5232. status&=DrawPolygonPrimitive(image,clone_info,primitive_info,
  5233. exception);
  5234. clone_info=DestroyDrawInfo(clone_info);
  5235. status=DrawDashPolygon(draw_info,primitive_info,image,exception);
  5236. break;
  5237. }
  5238. mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
  5239. if ((mid > 1.0) &&
  5240. ((draw_info->stroke.alpha != (Quantum) TransparentAlpha) ||
  5241. (draw_info->stroke_pattern != (Image *) NULL)))
  5242. {
  5243. double
  5244. x,
  5245. y;
  5246. MagickBooleanType
  5247. closed_path;
  5248. /*
  5249. Draw strokes while respecting line cap/join attributes.
  5250. */
  5251. closed_path=primitive_info[0].closed_subpath;
  5252. i=(ssize_t) primitive_info[0].coordinates;
  5253. x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
  5254. y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
  5255. if ((x < MagickEpsilon) && (y < MagickEpsilon))
  5256. closed_path=MagickTrue;
  5257. if ((((draw_info->linecap == RoundCap) ||
  5258. (closed_path != MagickFalse)) &&
  5259. (draw_info->linejoin == RoundJoin)) ||
  5260. (primitive_info[i].primitive != UndefinedPrimitive))
  5261. {
  5262. status=DrawPolygonPrimitive(image,draw_info,primitive_info,
  5263. exception);
  5264. break;
  5265. }
  5266. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  5267. clone_info->stroke_width=0.0;
  5268. clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
  5269. status&=DrawPolygonPrimitive(image,clone_info,primitive_info,
  5270. exception);
  5271. clone_info=DestroyDrawInfo(clone_info);
  5272. status&=DrawStrokePolygon(image,draw_info,primitive_info,exception);
  5273. break;
  5274. }
  5275. status&=DrawPolygonPrimitive(image,draw_info,primitive_info,exception);
  5276. break;
  5277. }
  5278. }
  5279. image_view=DestroyCacheView(image_view);
  5280. if (draw_info->compliance == SVGCompliance)
  5281. {
  5282. status&=SetImageMask(image,WritePixelMask,(Image *) NULL,exception);
  5283. status&=SetImageMask(image,CompositePixelMask,(Image *) NULL,exception);
  5284. }
  5285. if (image->debug != MagickFalse)
  5286. (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
  5287. return(status != 0 ? MagickTrue : MagickFalse);
  5288. }
  5289. /*
  5290. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5291. % %
  5292. % %
  5293. % %
  5294. + D r a w S t r o k e P o l y g o n %
  5295. % %
  5296. % %
  5297. % %
  5298. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5299. %
  5300. % DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
  5301. % the image while respecting the line cap and join attributes.
  5302. %
  5303. % The format of the DrawStrokePolygon method is:
  5304. %
  5305. % MagickBooleanType DrawStrokePolygon(Image *image,
  5306. % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
  5307. %
  5308. % A description of each parameter follows:
  5309. %
  5310. % o image: the image.
  5311. %
  5312. % o draw_info: the draw info.
  5313. %
  5314. % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
  5315. %
  5316. %
  5317. */
  5318. static MagickBooleanType DrawRoundLinecap(Image *image,
  5319. const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  5320. ExceptionInfo *exception)
  5321. {
  5322. PrimitiveInfo
  5323. linecap[5];
  5324. register ssize_t
  5325. i;
  5326. for (i=0; i < 4; i++)
  5327. linecap[i]=(*primitive_info);
  5328. linecap[0].coordinates=4;
  5329. linecap[1].point.x+=2.0*MagickEpsilon;
  5330. linecap[2].point.x+=2.0*MagickEpsilon;
  5331. linecap[2].point.y+=2.0*MagickEpsilon;
  5332. linecap[3].point.y+=2.0*MagickEpsilon;
  5333. linecap[4].primitive=UndefinedPrimitive;
  5334. return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
  5335. }
  5336. static MagickBooleanType DrawStrokePolygon(Image *image,
  5337. const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  5338. ExceptionInfo *exception)
  5339. {
  5340. DrawInfo
  5341. *clone_info;
  5342. MagickBooleanType
  5343. closed_path;
  5344. MagickStatusType
  5345. status;
  5346. PrimitiveInfo
  5347. *stroke_polygon;
  5348. register const PrimitiveInfo
  5349. *p,
  5350. *q;
  5351. /*
  5352. Draw stroked polygon.
  5353. */
  5354. if (image->debug != MagickFalse)
  5355. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  5356. " begin draw-stroke-polygon");
  5357. clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
  5358. clone_info->fill=draw_info->stroke;
  5359. if (clone_info->fill_pattern != (Image *) NULL)
  5360. clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
  5361. if (clone_info->stroke_pattern != (Image *) NULL)
  5362. clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
  5363. MagickTrue,exception);
  5364. clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
  5365. clone_info->stroke_width=0.0;
  5366. clone_info->fill_rule=NonZeroRule;
  5367. status=MagickTrue;
  5368. for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
  5369. {
  5370. if (p->coordinates == 1)
  5371. continue;
  5372. stroke_polygon=TraceStrokePolygon(image,draw_info,p);
  5373. if (stroke_polygon == (PrimitiveInfo *) NULL)
  5374. {
  5375. status=0;
  5376. stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
  5377. break;
  5378. }
  5379. status&=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception);
  5380. stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
  5381. if (status == 0)
  5382. break;
  5383. q=p+p->coordinates-1;
  5384. closed_path=p->closed_subpath;
  5385. if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
  5386. {
  5387. status&=DrawRoundLinecap(image,draw_info,p,exception);
  5388. status&=DrawRoundLinecap(image,draw_info,q,exception);
  5389. }
  5390. }
  5391. clone_info=DestroyDrawInfo(clone_info);
  5392. if (image->debug != MagickFalse)
  5393. (void) LogMagickEvent(DrawEvent,GetMagickModule(),
  5394. " end draw-stroke-polygon");
  5395. return(status != 0 ? MagickTrue : MagickFalse);
  5396. }
  5397. /*
  5398. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5399. % %
  5400. % %
  5401. % %
  5402. % G e t A f f i n e M a t r i x %
  5403. % %
  5404. % %
  5405. % %
  5406. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5407. %
  5408. % GetAffineMatrix() returns an AffineMatrix initialized to the identity
  5409. % matrix.
  5410. %
  5411. % The format of the GetAffineMatrix method is:
  5412. %
  5413. % void GetAffineMatrix(AffineMatrix *affine_matrix)
  5414. %
  5415. % A description of each parameter follows:
  5416. %
  5417. % o affine_matrix: the affine matrix.
  5418. %
  5419. */
  5420. MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
  5421. {
  5422. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  5423. assert(affine_matrix != (AffineMatrix *) NULL);
  5424. (void) memset(affine_matrix,0,sizeof(*affine_matrix));
  5425. affine_matrix->sx=1.0;
  5426. affine_matrix->sy=1.0;
  5427. }
  5428. /*
  5429. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5430. % %
  5431. % %
  5432. % %
  5433. + G e t D r a w I n f o %
  5434. % %
  5435. % %
  5436. % %
  5437. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5438. %
  5439. % GetDrawInfo() initializes draw_info to default values from image_info.
  5440. %
  5441. % The format of the GetDrawInfo method is:
  5442. %
  5443. % void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
  5444. %
  5445. % A description of each parameter follows:
  5446. %
  5447. % o image_info: the image info..
  5448. %
  5449. % o draw_info: the draw info.
  5450. %
  5451. */
  5452. MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
  5453. {
  5454. char
  5455. *next_token;
  5456. const char
  5457. *option;
  5458. ExceptionInfo
  5459. *exception;
  5460. ImageInfo
  5461. *clone_info;
  5462. /*
  5463. Initialize draw attributes.
  5464. */
  5465. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  5466. assert(draw_info != (DrawInfo *) NULL);
  5467. (void) memset(draw_info,0,sizeof(*draw_info));
  5468. clone_info=CloneImageInfo(image_info);
  5469. GetAffineMatrix(&draw_info->affine);
  5470. exception=AcquireExceptionInfo();
  5471. (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
  5472. exception);
  5473. (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
  5474. exception);
  5475. draw_info->stroke_antialias=clone_info->antialias;
  5476. draw_info->stroke_width=1.0;
  5477. draw_info->fill_rule=EvenOddRule;
  5478. draw_info->alpha=OpaqueAlpha;
  5479. draw_info->fill_alpha=OpaqueAlpha;
  5480. draw_info->stroke_alpha=OpaqueAlpha;
  5481. draw_info->linecap=ButtCap;
  5482. draw_info->linejoin=MiterJoin;
  5483. draw_info->miterlimit=10;
  5484. draw_info->decorate=NoDecoration;
  5485. draw_info->pointsize=12.0;
  5486. draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
  5487. draw_info->compose=OverCompositeOp;
  5488. draw_info->render=MagickTrue;
  5489. draw_info->clip_path=MagickFalse;
  5490. draw_info->debug=IsEventLogging();
  5491. if (clone_info->font != (char *) NULL)
  5492. draw_info->font=AcquireString(clone_info->font);
  5493. if (clone_info->density != (char *) NULL)
  5494. draw_info->density=AcquireString(clone_info->density);
  5495. draw_info->text_antialias=clone_info->antialias;
  5496. if (fabs(clone_info->pointsize) >= MagickEpsilon)
  5497. draw_info->pointsize=clone_info->pointsize;
  5498. draw_info->border_color=clone_info->border_color;
  5499. if (clone_info->server_name != (char *) NULL)
  5500. draw_info->server_name=AcquireString(clone_info->server_name);
  5501. option=GetImageOption(clone_info,"direction");
  5502. if (option != (const char *) NULL)
  5503. draw_info->direction=(DirectionType) ParseCommandOption(
  5504. MagickDirectionOptions,MagickFalse,option);
  5505. else
  5506. draw_info->direction=UndefinedDirection;
  5507. option=GetImageOption(clone_info,"encoding");
  5508. if (option != (const char *) NULL)
  5509. (void) CloneString(&draw_info->encoding,option);
  5510. option=GetImageOption(clone_info,"family");
  5511. if (option != (const char *) NULL)
  5512. (void) CloneString(&draw_info->family,option);
  5513. option=GetImageOption(clone_info,"fill");
  5514. if (option != (const char *) NULL)
  5515. (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
  5516. exception);
  5517. option=GetImageOption(clone_info,"gravity");
  5518. if (option != (const char *) NULL)
  5519. draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
  5520. MagickFalse,option);
  5521. option=GetImageOption(clone_info,"interline-spacing");
  5522. if (option != (const char *) NULL)
  5523. draw_info->interline_spacing=StringToDouble(option,&next_token);
  5524. option=GetImageOption(clone_info,"interword-spacing");
  5525. if (option != (const char *) NULL)
  5526. draw_info->interword_spacing=StringToDouble(option,&next_token);
  5527. option=GetImageOption(clone_info,"kerning");
  5528. if (option != (const char *) NULL)
  5529. draw_info->kerning=StringToDouble(option,&next_token);
  5530. option=GetImageOption(clone_info,"stroke");
  5531. if (option != (const char *) NULL)
  5532. (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
  5533. exception);
  5534. option=GetImageOption(clone_info,"strokewidth");
  5535. if (option != (const char *) NULL)
  5536. draw_info->stroke_width=StringToDouble(option,&next_token);
  5537. option=GetImageOption(clone_info,"style");
  5538. if (option != (const char *) NULL)
  5539. draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
  5540. MagickFalse,option);
  5541. option=GetImageOption(clone_info,"undercolor");
  5542. if (option != (const char *) NULL)
  5543. (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
  5544. exception);
  5545. option=GetImageOption(clone_info,"weight");
  5546. if (option != (const char *) NULL)
  5547. {
  5548. ssize_t
  5549. weight;
  5550. weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
  5551. if (weight == -1)
  5552. weight=(ssize_t) StringToUnsignedLong(option);
  5553. draw_info->weight=(size_t) weight;
  5554. }
  5555. exception=DestroyExceptionInfo(exception);
  5556. draw_info->signature=MagickCoreSignature;
  5557. clone_info=DestroyImageInfo(clone_info);
  5558. }
  5559. /*
  5560. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5561. % %
  5562. % %
  5563. % %
  5564. + P e r m u t a t e %
  5565. % %
  5566. % %
  5567. % %
  5568. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5569. %
  5570. % Permutate() returns the permuation of the (n,k).
  5571. %
  5572. % The format of the Permutate method is:
  5573. %
  5574. % void Permutate(ssize_t n,ssize_t k)
  5575. %
  5576. % A description of each parameter follows:
  5577. %
  5578. % o n:
  5579. %
  5580. % o k:
  5581. %
  5582. %
  5583. */
  5584. static inline double Permutate(const ssize_t n,const ssize_t k)
  5585. {
  5586. double
  5587. r;
  5588. register ssize_t
  5589. i;
  5590. r=1.0;
  5591. for (i=k+1; i <= n; i++)
  5592. r*=i;
  5593. for (i=1; i <= (n-k); i++)
  5594. r/=i;
  5595. return(r);
  5596. }
  5597. /*
  5598. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5599. % %
  5600. % %
  5601. % %
  5602. + T r a c e P r i m i t i v e %
  5603. % %
  5604. % %
  5605. % %
  5606. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  5607. %
  5608. % TracePrimitive is a collection of methods for generating graphic
  5609. % primitives such as arcs, ellipses, paths, etc.
  5610. %
  5611. */
  5612. static MagickBooleanType TraceArc(MVGInfo *mvg_info,const PointInfo start,
  5613. const PointInfo end,const PointInfo degrees)
  5614. {
  5615. PointInfo
  5616. center,
  5617. radius;
  5618. center.x=0.5*(end.x+start.x);
  5619. center.y=0.5*(end.y+start.y);
  5620. radius.x=fabs(center.x-start.x);
  5621. radius.y=fabs(center.y-start.y);
  5622. return(TraceEllipse(mvg_info,center,radius,degrees));
  5623. }
  5624. static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,const PointInfo start,
  5625. const PointInfo end,const PointInfo arc,const double angle,
  5626. const MagickBooleanType large_arc,const MagickBooleanType sweep)
  5627. {
  5628. double
  5629. alpha,
  5630. beta,
  5631. delta,
  5632. factor,
  5633. gamma,
  5634. theta;
  5635. MagickStatusType
  5636. status;
  5637. PointInfo
  5638. center,
  5639. points[3],
  5640. radii;
  5641. register double
  5642. cosine,
  5643. sine;
  5644. PrimitiveInfo
  5645. *primitive_info;
  5646. register PrimitiveInfo
  5647. *p;
  5648. register ssize_t
  5649. i;
  5650. size_t
  5651. arc_segments;
  5652. ssize_t
  5653. offset;
  5654. offset=mvg_info->offset;
  5655. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5656. primitive_info->coordinates=0;
  5657. if ((fabs(start.x-end.x) < MagickEpsilon) &&
  5658. (fabs(start.y-end.y) < MagickEpsilon))
  5659. return(TracePoint(primitive_info,end));
  5660. radii.x=fabs(arc.x);
  5661. radii.y=fabs(arc.y);
  5662. if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
  5663. return(TraceLine(primitive_info,start,end));
  5664. cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
  5665. sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
  5666. center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
  5667. center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
  5668. delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
  5669. (radii.y*radii.y);
  5670. if (delta < MagickEpsilon)
  5671. return(TraceLine(primitive_info,start,end));
  5672. if (delta > 1.0)
  5673. {
  5674. radii.x*=sqrt((double) delta);
  5675. radii.y*=sqrt((double) delta);
  5676. }
  5677. points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
  5678. points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
  5679. points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
  5680. points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
  5681. alpha=points[1].x-points[0].x;
  5682. beta=points[1].y-points[0].y;
  5683. factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25;
  5684. if (factor <= 0.0)
  5685. factor=0.0;
  5686. else
  5687. {
  5688. factor=sqrt((double) factor);
  5689. if (sweep == large_arc)
  5690. factor=(-factor);
  5691. }
  5692. center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
  5693. center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
  5694. alpha=atan2(points[0].y-center.y,points[0].x-center.x);
  5695. theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
  5696. if ((theta < 0.0) && (sweep != MagickFalse))
  5697. theta+=2.0*MagickPI;
  5698. else
  5699. if ((theta > 0.0) && (sweep == MagickFalse))
  5700. theta-=2.0*MagickPI;
  5701. arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
  5702. MagickEpsilon))));
  5703. status=MagickTrue;
  5704. p=primitive_info;
  5705. for (i=0; i < (ssize_t) arc_segments; i++)
  5706. {
  5707. beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
  5708. gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
  5709. sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
  5710. sin(fmod((double) beta,DegreesToRadians(360.0)));
  5711. points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
  5712. arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
  5713. (double) i*theta/arc_segments),DegreesToRadians(360.0))));
  5714. points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
  5715. arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
  5716. (double) i*theta/arc_segments),DegreesToRadians(360.0))));
  5717. points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
  5718. theta/arc_segments),DegreesToRadians(360.0))));
  5719. points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
  5720. theta/arc_segments),DegreesToRadians(360.0))));
  5721. points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
  5722. (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
  5723. points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
  5724. (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
  5725. p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
  5726. p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
  5727. (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
  5728. points[0].y);
  5729. (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
  5730. points[0].y);
  5731. (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
  5732. points[1].y);
  5733. (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
  5734. points[1].y);
  5735. (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
  5736. points[2].y);
  5737. (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
  5738. points[2].y);
  5739. if (i == (ssize_t) (arc_segments-1))
  5740. (p+3)->point=end;
  5741. status&=TraceBezier(mvg_info,4);
  5742. p=(*mvg_info->primitive_info)+mvg_info->offset;
  5743. mvg_info->offset+=p->coordinates;
  5744. p+=p->coordinates;
  5745. }
  5746. mvg_info->offset=offset;
  5747. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5748. primitive_info->coordinates=(size_t) (p-primitive_info);
  5749. primitive_info->closed_subpath=MagickFalse;
  5750. for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  5751. {
  5752. p->primitive=primitive_info->primitive;
  5753. p--;
  5754. }
  5755. return(status == 0 ? MagickFalse : MagickTrue);
  5756. }
  5757. static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
  5758. const size_t number_coordinates)
  5759. {
  5760. double
  5761. alpha,
  5762. *coefficients,
  5763. weight;
  5764. PointInfo
  5765. end,
  5766. point,
  5767. *points;
  5768. PrimitiveInfo
  5769. *primitive_info;
  5770. register PrimitiveInfo
  5771. *p;
  5772. register ssize_t
  5773. i,
  5774. j;
  5775. size_t
  5776. control_points,
  5777. quantum;
  5778. /*
  5779. Allocate coefficients.
  5780. */
  5781. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5782. quantum=number_coordinates;
  5783. for (i=0; i < (ssize_t) number_coordinates; i++)
  5784. {
  5785. for (j=i+1; j < (ssize_t) number_coordinates; j++)
  5786. {
  5787. alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
  5788. if (alpha > (double) quantum)
  5789. quantum=(size_t) alpha;
  5790. alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
  5791. if (alpha > (double) quantum)
  5792. quantum=(size_t) alpha;
  5793. }
  5794. }
  5795. quantum=(size_t) MagickMin((double) quantum/number_coordinates,
  5796. (double) BezierQuantum);
  5797. control_points=quantum*number_coordinates;
  5798. if (CheckPrimitiveExtent(mvg_info,control_points+1) == MagickFalse)
  5799. return(MagickFalse);
  5800. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5801. coefficients=(double *) AcquireQuantumMemory((size_t)
  5802. number_coordinates,sizeof(*coefficients));
  5803. points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
  5804. sizeof(*points));
  5805. if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
  5806. ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
  5807. /*
  5808. Compute bezier points.
  5809. */
  5810. end=primitive_info[number_coordinates-1].point;
  5811. for (i=0; i < (ssize_t) number_coordinates; i++)
  5812. coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
  5813. weight=0.0;
  5814. for (i=0; i < (ssize_t) control_points; i++)
  5815. {
  5816. p=primitive_info;
  5817. point.x=0.0;
  5818. point.y=0.0;
  5819. alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
  5820. for (j=0; j < (ssize_t) number_coordinates; j++)
  5821. {
  5822. point.x+=alpha*coefficients[j]*p->point.x;
  5823. point.y+=alpha*coefficients[j]*p->point.y;
  5824. alpha*=weight/(1.0-weight);
  5825. p++;
  5826. }
  5827. points[i]=point;
  5828. weight+=1.0/control_points;
  5829. }
  5830. /*
  5831. Bezier curves are just short segmented polys.
  5832. */
  5833. p=primitive_info;
  5834. for (i=0; i < (ssize_t) control_points; i++)
  5835. {
  5836. if (TracePoint(p,points[i]) == MagickFalse)
  5837. return(MagickFalse);
  5838. p+=p->coordinates;
  5839. }
  5840. if (TracePoint(p,end) == MagickFalse)
  5841. return(MagickFalse);
  5842. p+=p->coordinates;
  5843. primitive_info->coordinates=(size_t) (p-primitive_info);
  5844. primitive_info->closed_subpath=MagickFalse;
  5845. for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  5846. {
  5847. p->primitive=primitive_info->primitive;
  5848. p--;
  5849. }
  5850. points=(PointInfo *) RelinquishMagickMemory(points);
  5851. coefficients=(double *) RelinquishMagickMemory(coefficients);
  5852. return(MagickTrue);
  5853. }
  5854. static MagickBooleanType TraceCircle(MVGInfo *mvg_info,const PointInfo start,
  5855. const PointInfo end)
  5856. {
  5857. double
  5858. alpha,
  5859. beta,
  5860. radius;
  5861. PointInfo
  5862. offset,
  5863. degrees;
  5864. alpha=end.x-start.x;
  5865. beta=end.y-start.y;
  5866. radius=hypot((double) alpha,(double) beta);
  5867. offset.x=(double) radius;
  5868. offset.y=(double) radius;
  5869. degrees.x=0.0;
  5870. degrees.y=360.0;
  5871. return(TraceEllipse(mvg_info,start,offset,degrees));
  5872. }
  5873. static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
  5874. const PointInfo radii,const PointInfo arc)
  5875. {
  5876. double
  5877. coordinates,
  5878. delta,
  5879. step,
  5880. x,
  5881. y;
  5882. PointInfo
  5883. angle,
  5884. point;
  5885. PrimitiveInfo
  5886. *primitive_info;
  5887. register PrimitiveInfo
  5888. *p;
  5889. register ssize_t
  5890. i;
  5891. /*
  5892. Ellipses are just short segmented polys.
  5893. */
  5894. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5895. primitive_info->coordinates=0;
  5896. if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
  5897. return(MagickTrue);
  5898. delta=2.0*PerceptibleReciprocal(MagickMax(radii.x,radii.y));
  5899. step=MagickPI/8.0;
  5900. if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
  5901. step=MagickPI/4.0/(MagickPI*PerceptibleReciprocal(delta)/2.0);
  5902. angle.x=DegreesToRadians(arc.x);
  5903. y=arc.y;
  5904. while (y < arc.x)
  5905. y+=360.0;
  5906. angle.y=DegreesToRadians(y);
  5907. coordinates=ceil((angle.y-angle.x)/step+1.0);
  5908. if ((coordinates > (double) SSIZE_MAX) ||
  5909. (coordinates > (double) GetMaxMemoryRequest()))
  5910. {
  5911. (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
  5912. ResourceLimitError,"MemoryAllocationFailed","`%s'","");
  5913. return(MagickFalse);
  5914. }
  5915. if (CheckPrimitiveExtent(mvg_info,(size_t) coordinates) == MagickFalse)
  5916. return(MagickFalse);
  5917. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  5918. for (p=primitive_info; angle.x < angle.y; angle.x+=step)
  5919. {
  5920. point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
  5921. point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
  5922. if (TracePoint(p,point) == MagickFalse)
  5923. return(MagickFalse);
  5924. p+=p->coordinates;
  5925. }
  5926. point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
  5927. point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
  5928. if (TracePoint(p,point) == MagickFalse)
  5929. return(MagickFalse);
  5930. p+=p->coordinates;
  5931. primitive_info->coordinates=(size_t) (p-primitive_info);
  5932. primitive_info->closed_subpath=MagickFalse;
  5933. x=fabs(primitive_info[0].point.x-
  5934. primitive_info[primitive_info->coordinates-1].point.x);
  5935. y=fabs(primitive_info[0].point.y-
  5936. primitive_info[primitive_info->coordinates-1].point.y);
  5937. if ((x < MagickEpsilon) && (y < MagickEpsilon))
  5938. primitive_info->closed_subpath=MagickTrue;
  5939. for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  5940. {
  5941. p->primitive=primitive_info->primitive;
  5942. p--;
  5943. }
  5944. return(MagickTrue);
  5945. }
  5946. static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
  5947. const PointInfo start,const PointInfo end)
  5948. {
  5949. if (TracePoint(primitive_info,start) == MagickFalse)
  5950. return(MagickFalse);
  5951. if ((fabs(start.x-end.x) < MagickEpsilon) &&
  5952. (fabs(start.y-end.y) < MagickEpsilon))
  5953. {
  5954. primitive_info->primitive=PointPrimitive;
  5955. primitive_info->coordinates=1;
  5956. return(MagickTrue);
  5957. }
  5958. if (TracePoint(primitive_info+1,end) == MagickFalse)
  5959. return(MagickFalse);
  5960. (primitive_info+1)->primitive=primitive_info->primitive;
  5961. primitive_info->coordinates=2;
  5962. primitive_info->closed_subpath=MagickFalse;
  5963. return(MagickTrue);
  5964. }
  5965. static size_t TracePath(MVGInfo *mvg_info,const char *path,
  5966. ExceptionInfo *exception)
  5967. {
  5968. char
  5969. *next_token,
  5970. token[MagickPathExtent];
  5971. const char
  5972. *p;
  5973. double
  5974. x,
  5975. y;
  5976. int
  5977. attribute,
  5978. last_attribute;
  5979. MagickBooleanType
  5980. status;
  5981. PointInfo
  5982. end = {0.0, 0.0},
  5983. points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
  5984. point = {0.0, 0.0},
  5985. start = {0.0, 0.0};
  5986. PrimitiveInfo
  5987. *primitive_info;
  5988. PrimitiveType
  5989. primitive_type;
  5990. register PrimitiveInfo
  5991. *q;
  5992. register ssize_t
  5993. i;
  5994. size_t
  5995. number_coordinates,
  5996. z_count;
  5997. ssize_t
  5998. subpath_offset;
  5999. subpath_offset=mvg_info->offset;
  6000. primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  6001. status=MagickTrue;
  6002. attribute=0;
  6003. number_coordinates=0;
  6004. z_count=0;
  6005. primitive_type=primitive_info->primitive;
  6006. q=primitive_info;
  6007. for (p=path; *p != '\0'; )
  6008. {
  6009. if (status == MagickFalse)
  6010. break;
  6011. while (isspace((int) ((unsigned char) *p)) != 0)
  6012. p++;
  6013. if (*p == '\0')
  6014. break;
  6015. last_attribute=attribute;
  6016. attribute=(int) (*p++);
  6017. switch (attribute)
  6018. {
  6019. case 'a':
  6020. case 'A':
  6021. {
  6022. double
  6023. angle = 0.0;
  6024. MagickBooleanType
  6025. large_arc = MagickFalse,
  6026. sweep = MagickFalse;
  6027. PointInfo
  6028. arc = {0.0, 0.0};
  6029. /*
  6030. Elliptical arc.
  6031. */
  6032. do
  6033. {
  6034. GetNextToken(p,&p,MagickPathExtent,token);
  6035. if (*token == ',')
  6036. GetNextToken(p,&p,MagickPathExtent,token);
  6037. arc.x=StringToDouble(token,&next_token);
  6038. if (token == next_token)
  6039. ThrowPointExpectedException(token,exception);
  6040. GetNextToken(p,&p,MagickPathExtent,token);
  6041. if (*token == ',')
  6042. GetNextToken(p,&p,MagickPathExtent,token);
  6043. arc.y=StringToDouble(token,&next_token);
  6044. if (token == next_token)
  6045. ThrowPointExpectedException(token,exception);
  6046. GetNextToken(p,&p,MagickPathExtent,token);
  6047. if (*token == ',')
  6048. GetNextToken(p,&p,MagickPathExtent,token);
  6049. angle=StringToDouble(token,&next_token);
  6050. if (token == next_token)
  6051. ThrowPointExpectedException(token,exception);
  6052. GetNextToken(p,&p,MagickPathExtent,token);
  6053. if (*token == ',')
  6054. GetNextToken(p,&p,MagickPathExtent,token);
  6055. large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
  6056. GetNextToken(p,&p,MagickPathExtent,token);
  6057. if (*token == ',')
  6058. GetNextToken(p,&p,MagickPathExtent,token);
  6059. sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
  6060. if (*token == ',')
  6061. GetNextToken(p,&p,MagickPathExtent,token);
  6062. GetNextToken(p,&p,MagickPathExtent,token);
  6063. if (*token == ',')
  6064. GetNextToken(p,&p,MagickPathExtent,token);
  6065. x=StringToDouble(token,&next_token);
  6066. if (token == next_token)
  6067. ThrowPointExpectedException(token,exception);
  6068. GetNextToken(p,&p,MagickPathExtent,token);
  6069. if (*token == ',')
  6070. GetNextToken(p,&p,MagickPathExtent,token);
  6071. y=StringToDouble(token,&next_token);
  6072. if (token == next_token)
  6073. ThrowPointExpectedException(token,exception);
  6074. end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
  6075. end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
  6076. if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
  6077. return(0);
  6078. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6079. mvg_info->offset+=q->coordinates;
  6080. q+=q->coordinates;
  6081. point=end;
  6082. while (isspace((int) ((unsigned char) *p)) != 0)
  6083. p++;
  6084. if (*p == ',')
  6085. p++;
  6086. } while (IsPoint(p) != MagickFalse);
  6087. break;
  6088. }
  6089. case 'c':
  6090. case 'C':
  6091. {
  6092. /*
  6093. Cubic Bézier curve.
  6094. */
  6095. do
  6096. {
  6097. points[0]=point;
  6098. for (i=1; i < 4; i++)
  6099. {
  6100. GetNextToken(p,&p,MagickPathExtent,token);
  6101. if (*token == ',')
  6102. GetNextToken(p,&p,MagickPathExtent,token);
  6103. x=StringToDouble(token,&next_token);
  6104. if (token == next_token)
  6105. ThrowPointExpectedException(token,exception);
  6106. GetNextToken(p,&p,MagickPathExtent,token);
  6107. if (*token == ',')
  6108. GetNextToken(p,&p,MagickPathExtent,token);
  6109. y=StringToDouble(token,&next_token);
  6110. if (token == next_token)
  6111. ThrowPointExpectedException(token,exception);
  6112. end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
  6113. end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
  6114. points[i]=end;
  6115. }
  6116. for (i=0; i < 4; i++)
  6117. (q+i)->point=points[i];
  6118. if (TraceBezier(mvg_info,4) == MagickFalse)
  6119. return(0);
  6120. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6121. mvg_info->offset+=q->coordinates;
  6122. q+=q->coordinates;
  6123. point=end;
  6124. while (isspace((int) ((unsigned char) *p)) != 0)
  6125. p++;
  6126. if (*p == ',')
  6127. p++;
  6128. } while (IsPoint(p) != MagickFalse);
  6129. break;
  6130. }
  6131. case 'H':
  6132. case 'h':
  6133. {
  6134. do
  6135. {
  6136. GetNextToken(p,&p,MagickPathExtent,token);
  6137. if (*token == ',')
  6138. GetNextToken(p,&p,MagickPathExtent,token);
  6139. x=StringToDouble(token,&next_token);
  6140. if (token == next_token)
  6141. ThrowPointExpectedException(token,exception);
  6142. point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
  6143. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6144. return(0);
  6145. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6146. if (TracePoint(q,point) == MagickFalse)
  6147. return(0);
  6148. mvg_info->offset+=q->coordinates;
  6149. q+=q->coordinates;
  6150. while (isspace((int) ((unsigned char) *p)) != 0)
  6151. p++;
  6152. if (*p == ',')
  6153. p++;
  6154. } while (IsPoint(p) != MagickFalse);
  6155. break;
  6156. }
  6157. case 'l':
  6158. case 'L':
  6159. {
  6160. /*
  6161. Line to.
  6162. */
  6163. do
  6164. {
  6165. GetNextToken(p,&p,MagickPathExtent,token);
  6166. if (*token == ',')
  6167. GetNextToken(p,&p,MagickPathExtent,token);
  6168. x=StringToDouble(token,&next_token);
  6169. if (token == next_token)
  6170. ThrowPointExpectedException(token,exception);
  6171. GetNextToken(p,&p,MagickPathExtent,token);
  6172. if (*token == ',')
  6173. GetNextToken(p,&p,MagickPathExtent,token);
  6174. y=StringToDouble(token,&next_token);
  6175. if (token == next_token)
  6176. ThrowPointExpectedException(token,exception);
  6177. point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
  6178. point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
  6179. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6180. return(0);
  6181. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6182. if (TracePoint(q,point) == MagickFalse)
  6183. return(0);
  6184. mvg_info->offset+=q->coordinates;
  6185. q+=q->coordinates;
  6186. while (isspace((int) ((unsigned char) *p)) != 0)
  6187. p++;
  6188. if (*p == ',')
  6189. p++;
  6190. } while (IsPoint(p) != MagickFalse);
  6191. break;
  6192. }
  6193. case 'M':
  6194. case 'm':
  6195. {
  6196. /*
  6197. Move to.
  6198. */
  6199. if (mvg_info->offset != subpath_offset)
  6200. {
  6201. primitive_info=(*mvg_info->primitive_info)+subpath_offset;
  6202. primitive_info->coordinates=(size_t) (q-primitive_info);
  6203. number_coordinates+=primitive_info->coordinates;
  6204. primitive_info=q;
  6205. subpath_offset=mvg_info->offset;
  6206. }
  6207. i=0;
  6208. do
  6209. {
  6210. GetNextToken(p,&p,MagickPathExtent,token);
  6211. if (*token == ',')
  6212. GetNextToken(p,&p,MagickPathExtent,token);
  6213. x=StringToDouble(token,&next_token);
  6214. if (token == next_token)
  6215. ThrowPointExpectedException(token,exception);
  6216. GetNextToken(p,&p,MagickPathExtent,token);
  6217. if (*token == ',')
  6218. GetNextToken(p,&p,MagickPathExtent,token);
  6219. y=StringToDouble(token,&next_token);
  6220. if (token == next_token)
  6221. ThrowPointExpectedException(token,exception);
  6222. point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
  6223. point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
  6224. if (i == 0)
  6225. start=point;
  6226. i++;
  6227. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6228. return(0);
  6229. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6230. if (TracePoint(q,point) == MagickFalse)
  6231. return(0);
  6232. mvg_info->offset+=q->coordinates;
  6233. q+=q->coordinates;
  6234. while (isspace((int) ((unsigned char) *p)) != 0)
  6235. p++;
  6236. if (*p == ',')
  6237. p++;
  6238. } while (IsPoint(p) != MagickFalse);
  6239. break;
  6240. }
  6241. case 'q':
  6242. case 'Q':
  6243. {
  6244. /*
  6245. Quadratic Bézier curve.
  6246. */
  6247. do
  6248. {
  6249. points[0]=point;
  6250. for (i=1; i < 3; i++)
  6251. {
  6252. GetNextToken(p,&p,MagickPathExtent,token);
  6253. if (*token == ',')
  6254. GetNextToken(p,&p,MagickPathExtent,token);
  6255. x=StringToDouble(token,&next_token);
  6256. if (token == next_token)
  6257. ThrowPointExpectedException(token,exception);
  6258. GetNextToken(p,&p,MagickPathExtent,token);
  6259. if (*token == ',')
  6260. GetNextToken(p,&p,MagickPathExtent,token);
  6261. y=StringToDouble(token,&next_token);
  6262. if (token == next_token)
  6263. ThrowPointExpectedException(token,exception);
  6264. if (*p == ',')
  6265. p++;
  6266. end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
  6267. end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
  6268. points[i]=end;
  6269. }
  6270. for (i=0; i < 3; i++)
  6271. (q+i)->point=points[i];
  6272. if (TraceBezier(mvg_info,3) == MagickFalse)
  6273. return(0);
  6274. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6275. mvg_info->offset+=q->coordinates;
  6276. q+=q->coordinates;
  6277. point=end;
  6278. while (isspace((int) ((unsigned char) *p)) != 0)
  6279. p++;
  6280. if (*p == ',')
  6281. p++;
  6282. } while (IsPoint(p) != MagickFalse);
  6283. break;
  6284. }
  6285. case 's':
  6286. case 'S':
  6287. {
  6288. /*
  6289. Cubic Bézier curve.
  6290. */
  6291. do
  6292. {
  6293. points[0]=points[3];
  6294. points[1].x=2.0*points[3].x-points[2].x;
  6295. points[1].y=2.0*points[3].y-points[2].y;
  6296. for (i=2; i < 4; i++)
  6297. {
  6298. GetNextToken(p,&p,MagickPathExtent,token);
  6299. if (*token == ',')
  6300. GetNextToken(p,&p,MagickPathExtent,token);
  6301. x=StringToDouble(token,&next_token);
  6302. if (token == next_token)
  6303. ThrowPointExpectedException(token,exception);
  6304. GetNextToken(p,&p,MagickPathExtent,token);
  6305. if (*token == ',')
  6306. GetNextToken(p,&p,MagickPathExtent,token);
  6307. y=StringToDouble(token,&next_token);
  6308. if (token == next_token)
  6309. ThrowPointExpectedException(token,exception);
  6310. if (*p == ',')
  6311. p++;
  6312. end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
  6313. end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
  6314. points[i]=end;
  6315. }
  6316. if (strchr("CcSs",last_attribute) == (char *) NULL)
  6317. {
  6318. points[0]=point;
  6319. points[1]=point;
  6320. }
  6321. for (i=0; i < 4; i++)
  6322. (q+i)->point=points[i];
  6323. if (TraceBezier(mvg_info,4) == MagickFalse)
  6324. return(0);
  6325. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6326. mvg_info->offset+=q->coordinates;
  6327. q+=q->coordinates;
  6328. point=end;
  6329. last_attribute=attribute;
  6330. while (isspace((int) ((unsigned char) *p)) != 0)
  6331. p++;
  6332. if (*p == ',')
  6333. p++;
  6334. } while (IsPoint(p) != MagickFalse);
  6335. break;
  6336. }
  6337. case 't':
  6338. case 'T':
  6339. {
  6340. /*
  6341. Quadratic Bézier curve.
  6342. */
  6343. do
  6344. {
  6345. points[0]=points[2];
  6346. points[1].x=2.0*points[2].x-points[1].x;
  6347. points[1].y=2.0*points[2].y-points[1].y;
  6348. for (i=2; i < 3; i++)
  6349. {
  6350. GetNextToken(p,&p,MagickPathExtent,token);
  6351. if (*token == ',')
  6352. GetNextToken(p,&p,MagickPathExtent,token);
  6353. x=StringToDouble(token,&next_token);
  6354. if (token == next_token)
  6355. ThrowPointExpectedException(token,exception);
  6356. GetNextToken(p,&p,MagickPathExtent,token);
  6357. if (*token == ',')
  6358. GetNextToken(p,&p,MagickPathExtent,token);
  6359. y=StringToDouble(token,&next_token);
  6360. if (token == next_token)
  6361. ThrowPointExpectedException(token,exception);
  6362. end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
  6363. end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
  6364. points[i]=end;
  6365. }
  6366. if (status == MagickFalse)
  6367. break;
  6368. if (strchr("QqTt",last_attribute) == (char *) NULL)
  6369. {
  6370. points[0]=point;
  6371. points[1]=point;
  6372. }
  6373. for (i=0; i < 3; i++)
  6374. (q+i)->point=points[i];
  6375. if (TraceBezier(mvg_info,3) == MagickFalse)
  6376. return(0);
  6377. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6378. mvg_info->offset+=q->coordinates;
  6379. q+=q->coordinates;
  6380. point=end;
  6381. last_attribute=attribute;
  6382. while (isspace((int) ((unsigned char) *p)) != 0)
  6383. p++;
  6384. if (*p == ',')
  6385. p++;
  6386. } while (IsPoint(p) != MagickFalse);
  6387. break;
  6388. }
  6389. case 'v':
  6390. case 'V':
  6391. {
  6392. /*
  6393. Line to.
  6394. */
  6395. do
  6396. {
  6397. GetNextToken(p,&p,MagickPathExtent,token);
  6398. if (*token == ',')
  6399. GetNextToken(p,&p,MagickPathExtent,token);
  6400. y=StringToDouble(token,&next_token);
  6401. if (token == next_token)
  6402. ThrowPointExpectedException(token,exception);
  6403. point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
  6404. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6405. return(0);
  6406. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6407. if (TracePoint(q,point) == MagickFalse)
  6408. return(0);
  6409. mvg_info->offset+=q->coordinates;
  6410. q+=q->coordinates;
  6411. while (isspace((int) ((unsigned char) *p)) != 0)
  6412. p++;
  6413. if (*p == ',')
  6414. p++;
  6415. } while (IsPoint(p) != MagickFalse);
  6416. break;
  6417. }
  6418. case 'z':
  6419. case 'Z':
  6420. {
  6421. /*
  6422. Close path.
  6423. */
  6424. point=start;
  6425. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6426. return(0);
  6427. q=(*mvg_info->primitive_info)+mvg_info->offset;
  6428. if (TracePoint(q,point) == MagickFalse)
  6429. return(0);
  6430. mvg_info->offset+=q->coordinates;
  6431. q+=q->coordinates;
  6432. primitive_info=(*mvg_info->primitive_info)+subpath_offset;
  6433. primitive_info->coordinates=(size_t) (q-primitive_info);
  6434. primitive_info->closed_subpath=MagickTrue;
  6435. number_coordinates+=primitive_info->coordinates;
  6436. primitive_info=q;
  6437. subpath_offset=mvg_info->offset;
  6438. z_count++;
  6439. break;
  6440. }
  6441. default:
  6442. {
  6443. ThrowPointExpectedException(token,exception);
  6444. break;
  6445. }
  6446. }
  6447. }
  6448. if (status == MagickFalse)
  6449. return(0);
  6450. primitive_info=(*mvg_info->primitive_info)+subpath_offset;
  6451. primitive_info->coordinates=(size_t) (q-primitive_info);
  6452. number_coordinates+=primitive_info->coordinates;
  6453. for (i=0; i < (ssize_t) number_coordinates; i++)
  6454. {
  6455. q--;
  6456. q->primitive=primitive_type;
  6457. if (z_count > 1)
  6458. q->method=FillToBorderMethod;
  6459. }
  6460. q=primitive_info;
  6461. return(number_coordinates);
  6462. }
  6463. static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
  6464. const PointInfo start,const PointInfo end)
  6465. {
  6466. PointInfo
  6467. point;
  6468. register PrimitiveInfo
  6469. *p;
  6470. register ssize_t
  6471. i;
  6472. p=primitive_info;
  6473. if (TracePoint(p,start) == MagickFalse)
  6474. return(MagickFalse);
  6475. p+=p->coordinates;
  6476. point.x=start.x;
  6477. point.y=end.y;
  6478. if (TracePoint(p,point) == MagickFalse)
  6479. return(MagickFalse);
  6480. p+=p->coordinates;
  6481. if (TracePoint(p,end) == MagickFalse)
  6482. return(MagickFalse);
  6483. p+=p->coordinates;
  6484. point.x=end.x;
  6485. point.y=start.y;
  6486. if (TracePoint(p,point) == MagickFalse)
  6487. return(MagickFalse);
  6488. p+=p->coordinates;
  6489. if (TracePoint(p,start) == MagickFalse)
  6490. return(MagickFalse);
  6491. p+=p->coordinates;
  6492. primitive_info->coordinates=(size_t) (p-primitive_info);
  6493. primitive_info->closed_subpath=MagickTrue;
  6494. for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  6495. {
  6496. p->primitive=primitive_info->primitive;
  6497. p--;
  6498. }
  6499. return(MagickTrue);
  6500. }
  6501. static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
  6502. const PointInfo start,const PointInfo end,PointInfo arc)
  6503. {
  6504. PointInfo
  6505. degrees,
  6506. point,
  6507. segment;
  6508. PrimitiveInfo
  6509. *primitive_info;
  6510. register PrimitiveInfo
  6511. *p;
  6512. register ssize_t
  6513. i;
  6514. ssize_t
  6515. offset;
  6516. offset=mvg_info->offset;
  6517. segment.x=fabs(end.x-start.x);
  6518. segment.y=fabs(end.y-start.y);
  6519. if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
  6520. {
  6521. (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
  6522. return(MagickTrue);
  6523. }
  6524. if (arc.x > (0.5*segment.x))
  6525. arc.x=0.5*segment.x;
  6526. if (arc.y > (0.5*segment.y))
  6527. arc.y=0.5*segment.y;
  6528. point.x=start.x+segment.x-arc.x;
  6529. point.y=start.y+arc.y;
  6530. degrees.x=270.0;
  6531. degrees.y=360.0;
  6532. if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
  6533. return(MagickFalse);
  6534. p=(*mvg_info->primitive_info)+mvg_info->offset;
  6535. mvg_info->offset+=p->coordinates;
  6536. point.x=start.x+segment.x-arc.x;
  6537. point.y=start.y+segment.y-arc.y;
  6538. degrees.x=0.0;
  6539. degrees.y=90.0;
  6540. if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
  6541. return(MagickFalse);
  6542. p=(*mvg_info->primitive_info)+mvg_info->offset;
  6543. mvg_info->offset+=p->coordinates;
  6544. point.x=start.x+arc.x;
  6545. point.y=start.y+segment.y-arc.y;
  6546. degrees.x=90.0;
  6547. degrees.y=180.0;
  6548. if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
  6549. return(MagickFalse);
  6550. p=(*mvg_info->primitive_info)+mvg_info->offset;
  6551. mvg_info->offset+=p->coordinates;
  6552. point.x=start.x+arc.x;
  6553. point.y=start.y+arc.y;
  6554. degrees.x=180.0;
  6555. degrees.y=270.0;
  6556. if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
  6557. return(MagickFalse);
  6558. p=(*mvg_info->primitive_info)+mvg_info->offset;
  6559. mvg_info->offset+=p->coordinates;
  6560. if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
  6561. return(MagickFalse);
  6562. p=(*mvg_info->primitive_info)+mvg_info->offset;
  6563. if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
  6564. return(MagickFalse);
  6565. p+=p->coordinates;
  6566. mvg_info->offset=offset;
  6567. primitive_info=(*mvg_info->primitive_info)+offset;
  6568. primitive_info->coordinates=(size_t) (p-primitive_info);
  6569. primitive_info->closed_subpath=MagickTrue;
  6570. for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  6571. {
  6572. p->primitive=primitive_info->primitive;
  6573. p--;
  6574. }
  6575. return(MagickTrue);
  6576. }
  6577. static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
  6578. const size_t number_vertices,const double offset)
  6579. {
  6580. double
  6581. distance;
  6582. register double
  6583. dx,
  6584. dy;
  6585. register ssize_t
  6586. i;
  6587. ssize_t
  6588. j;
  6589. dx=0.0;
  6590. dy=0.0;
  6591. for (i=1; i < (ssize_t) number_vertices; i++)
  6592. {
  6593. dx=primitive_info[0].point.x-primitive_info[i].point.x;
  6594. dy=primitive_info[0].point.y-primitive_info[i].point.y;
  6595. if ((fabs((double) dx) >= MagickEpsilon) ||
  6596. (fabs((double) dy) >= MagickEpsilon))
  6597. break;
  6598. }
  6599. if (i == (ssize_t) number_vertices)
  6600. i=(ssize_t) number_vertices-1L;
  6601. distance=hypot((double) dx,(double) dy);
  6602. primitive_info[0].point.x=(double) (primitive_info[i].point.x+
  6603. dx*(distance+offset)/distance);
  6604. primitive_info[0].point.y=(double) (primitive_info[i].point.y+
  6605. dy*(distance+offset)/distance);
  6606. for (j=(ssize_t) number_vertices-2; j >= 0; j--)
  6607. {
  6608. dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
  6609. dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
  6610. if ((fabs((double) dx) >= MagickEpsilon) ||
  6611. (fabs((double) dy) >= MagickEpsilon))
  6612. break;
  6613. }
  6614. distance=hypot((double) dx,(double) dy);
  6615. primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
  6616. dx*(distance+offset)/distance);
  6617. primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
  6618. dy*(distance+offset)/distance);
  6619. return(MagickTrue);
  6620. }
  6621. static PrimitiveInfo *TraceStrokePolygon(const Image *image,
  6622. const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
  6623. {
  6624. #define CheckPathExtent(pad) \
  6625. if ((ssize_t) (q+(pad)) >= (ssize_t) max_strokes) \
  6626. { \
  6627. if (~max_strokes < (pad)) \
  6628. { \
  6629. path_p=(PointInfo *) RelinquishMagickMemory(path_p); \
  6630. path_q=(PointInfo *) RelinquishMagickMemory(path_q); \
  6631. } \
  6632. else \
  6633. { \
  6634. max_strokes+=(pad); \
  6635. path_p=(PointInfo *) ResizeQuantumMemory(path_p,max_strokes, \
  6636. sizeof(*path_p)); \
  6637. path_q=(PointInfo *) ResizeQuantumMemory(path_q,max_strokes, \
  6638. sizeof(*path_q)); \
  6639. } \
  6640. if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL)) \
  6641. { \
  6642. if (path_p != (PointInfo *) NULL) \
  6643. path_p=(PointInfo *) RelinquishMagickMemory(path_p); \
  6644. if (path_q != (PointInfo *) NULL) \
  6645. path_q=(PointInfo *) RelinquishMagickMemory(path_q); \
  6646. polygon_primitive=(PrimitiveInfo *) \
  6647. RelinquishMagickMemory(polygon_primitive); \
  6648. return((PrimitiveInfo *) NULL); \
  6649. } \
  6650. }
  6651. typedef struct _LineSegment
  6652. {
  6653. double
  6654. p,
  6655. q;
  6656. } LineSegment;
  6657. double
  6658. delta_theta,
  6659. dot_product,
  6660. mid,
  6661. miterlimit;
  6662. LineSegment
  6663. dx = {0,0},
  6664. dy = {0,0},
  6665. inverse_slope = {0,0},
  6666. slope = {0,0},
  6667. theta = {0,0};
  6668. MagickBooleanType
  6669. closed_path;
  6670. PointInfo
  6671. box_p[5],
  6672. box_q[5],
  6673. center,
  6674. offset,
  6675. *path_p,
  6676. *path_q;
  6677. PrimitiveInfo
  6678. *polygon_primitive,
  6679. *stroke_polygon;
  6680. register ssize_t
  6681. i;
  6682. size_t
  6683. arc_segments,
  6684. max_strokes,
  6685. number_vertices;
  6686. ssize_t
  6687. j,
  6688. n,
  6689. p,
  6690. q;
  6691. /*
  6692. Allocate paths.
  6693. */
  6694. number_vertices=primitive_info->coordinates;
  6695. max_strokes=2*number_vertices+6*BezierQuantum+360;
  6696. polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
  6697. number_vertices+2UL,sizeof(*polygon_primitive));
  6698. if (polygon_primitive == (PrimitiveInfo *) NULL)
  6699. return((PrimitiveInfo *) NULL);
  6700. (void) memcpy(polygon_primitive,primitive_info,(size_t) number_vertices*
  6701. sizeof(*polygon_primitive));
  6702. closed_path=primitive_info[0].closed_subpath;
  6703. if (((draw_info->linejoin == RoundJoin) ||
  6704. (draw_info->linejoin == MiterJoin)) && (closed_path != MagickFalse))
  6705. {
  6706. polygon_primitive[number_vertices]=primitive_info[1];
  6707. number_vertices++;
  6708. }
  6709. polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
  6710. /*
  6711. Compute the slope for the first line segment, p.
  6712. */
  6713. dx.p=0.0;
  6714. dy.p=0.0;
  6715. for (n=1; n < (ssize_t) number_vertices; n++)
  6716. {
  6717. dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
  6718. dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
  6719. if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
  6720. break;
  6721. }
  6722. if (n == (ssize_t) number_vertices)
  6723. {
  6724. if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
  6725. {
  6726. /*
  6727. Zero length subpath.
  6728. */
  6729. stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
  6730. sizeof(*stroke_polygon));
  6731. stroke_polygon[0]=polygon_primitive[0];
  6732. stroke_polygon[0].coordinates=0;
  6733. polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
  6734. polygon_primitive);
  6735. return(stroke_polygon);
  6736. }
  6737. n=(ssize_t) number_vertices-1L;
  6738. }
  6739. path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
  6740. sizeof(*path_p));
  6741. if (path_p == (PointInfo *) NULL)
  6742. {
  6743. polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
  6744. polygon_primitive);
  6745. return((PrimitiveInfo *) NULL);
  6746. }
  6747. path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
  6748. sizeof(*path_q));
  6749. if (path_q == (PointInfo *) NULL)
  6750. {
  6751. path_p=(PointInfo *) RelinquishMagickMemory(path_p);
  6752. polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
  6753. polygon_primitive);
  6754. return((PrimitiveInfo *) NULL);
  6755. }
  6756. slope.p=0.0;
  6757. inverse_slope.p=0.0;
  6758. if (fabs(dx.p) < MagickEpsilon)
  6759. {
  6760. if (dx.p >= 0.0)
  6761. slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
  6762. else
  6763. slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
  6764. }
  6765. else
  6766. if (fabs(dy.p) < MagickEpsilon)
  6767. {
  6768. if (dy.p >= 0.0)
  6769. inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
  6770. else
  6771. inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
  6772. }
  6773. else
  6774. {
  6775. slope.p=dy.p/dx.p;
  6776. inverse_slope.p=(-1.0/slope.p);
  6777. }
  6778. mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
  6779. miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
  6780. if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
  6781. (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
  6782. offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
  6783. offset.y=(double) (offset.x*inverse_slope.p);
  6784. if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
  6785. {
  6786. box_p[0].x=polygon_primitive[0].point.x-offset.x;
  6787. box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
  6788. box_p[1].x=polygon_primitive[n].point.x-offset.x;
  6789. box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
  6790. box_q[0].x=polygon_primitive[0].point.x+offset.x;
  6791. box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
  6792. box_q[1].x=polygon_primitive[n].point.x+offset.x;
  6793. box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
  6794. }
  6795. else
  6796. {
  6797. box_p[0].x=polygon_primitive[0].point.x+offset.x;
  6798. box_p[0].y=polygon_primitive[0].point.y+offset.y;
  6799. box_p[1].x=polygon_primitive[n].point.x+offset.x;
  6800. box_p[1].y=polygon_primitive[n].point.y+offset.y;
  6801. box_q[0].x=polygon_primitive[0].point.x-offset.x;
  6802. box_q[0].y=polygon_primitive[0].point.y-offset.y;
  6803. box_q[1].x=polygon_primitive[n].point.x-offset.x;
  6804. box_q[1].y=polygon_primitive[n].point.y-offset.y;
  6805. }
  6806. /*
  6807. Create strokes for the line join attribute: bevel, miter, round.
  6808. */
  6809. p=0;
  6810. q=0;
  6811. path_q[p++]=box_q[0];
  6812. path_p[q++]=box_p[0];
  6813. for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
  6814. {
  6815. /*
  6816. Compute the slope for this line segment, q.
  6817. */
  6818. dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
  6819. dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
  6820. dot_product=dx.q*dx.q+dy.q*dy.q;
  6821. if (dot_product < 0.25)
  6822. continue;
  6823. slope.q=0.0;
  6824. inverse_slope.q=0.0;
  6825. if (fabs(dx.q) < MagickEpsilon)
  6826. {
  6827. if (dx.q >= 0.0)
  6828. slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
  6829. else
  6830. slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
  6831. }
  6832. else
  6833. if (fabs(dy.q) < MagickEpsilon)
  6834. {
  6835. if (dy.q >= 0.0)
  6836. inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
  6837. else
  6838. inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
  6839. }
  6840. else
  6841. {
  6842. slope.q=dy.q/dx.q;
  6843. inverse_slope.q=(-1.0/slope.q);
  6844. }
  6845. offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
  6846. offset.y=(double) (offset.x*inverse_slope.q);
  6847. dot_product=dy.q*offset.x-dx.q*offset.y;
  6848. if (dot_product > 0.0)
  6849. {
  6850. box_p[2].x=polygon_primitive[n].point.x-offset.x;
  6851. box_p[2].y=polygon_primitive[n].point.y-offset.y;
  6852. box_p[3].x=polygon_primitive[i].point.x-offset.x;
  6853. box_p[3].y=polygon_primitive[i].point.y-offset.y;
  6854. box_q[2].x=polygon_primitive[n].point.x+offset.x;
  6855. box_q[2].y=polygon_primitive[n].point.y+offset.y;
  6856. box_q[3].x=polygon_primitive[i].point.x+offset.x;
  6857. box_q[3].y=polygon_primitive[i].point.y+offset.y;
  6858. }
  6859. else
  6860. {
  6861. box_p[2].x=polygon_primitive[n].point.x+offset.x;
  6862. box_p[2].y=polygon_primitive[n].point.y+offset.y;
  6863. box_p[3].x=polygon_primitive[i].point.x+offset.x;
  6864. box_p[3].y=polygon_primitive[i].point.y+offset.y;
  6865. box_q[2].x=polygon_primitive[n].point.x-offset.x;
  6866. box_q[2].y=polygon_primitive[n].point.y-offset.y;
  6867. box_q[3].x=polygon_primitive[i].point.x-offset.x;
  6868. box_q[3].y=polygon_primitive[i].point.y-offset.y;
  6869. }
  6870. if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
  6871. {
  6872. box_p[4]=box_p[1];
  6873. box_q[4]=box_q[1];
  6874. }
  6875. else
  6876. {
  6877. box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
  6878. box_p[3].y)/(slope.p-slope.q));
  6879. box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
  6880. box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
  6881. box_q[3].y)/(slope.p-slope.q));
  6882. box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
  6883. }
  6884. CheckPathExtent(6*BezierQuantum+360);
  6885. dot_product=dx.q*dy.p-dx.p*dy.q;
  6886. if (dot_product <= 0.0)
  6887. switch (draw_info->linejoin)
  6888. {
  6889. case BevelJoin:
  6890. {
  6891. path_q[q++]=box_q[1];
  6892. path_q[q++]=box_q[2];
  6893. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6894. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6895. if (dot_product <= miterlimit)
  6896. path_p[p++]=box_p[4];
  6897. else
  6898. {
  6899. path_p[p++]=box_p[1];
  6900. path_p[p++]=box_p[2];
  6901. }
  6902. break;
  6903. }
  6904. case MiterJoin:
  6905. {
  6906. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6907. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6908. if (dot_product <= miterlimit)
  6909. {
  6910. path_q[q++]=box_q[4];
  6911. path_p[p++]=box_p[4];
  6912. }
  6913. else
  6914. {
  6915. path_q[q++]=box_q[1];
  6916. path_q[q++]=box_q[2];
  6917. path_p[p++]=box_p[1];
  6918. path_p[p++]=box_p[2];
  6919. }
  6920. break;
  6921. }
  6922. case RoundJoin:
  6923. {
  6924. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6925. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6926. if (dot_product <= miterlimit)
  6927. path_p[p++]=box_p[4];
  6928. else
  6929. {
  6930. path_p[p++]=box_p[1];
  6931. path_p[p++]=box_p[2];
  6932. }
  6933. center=polygon_primitive[n].point;
  6934. theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
  6935. theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
  6936. if (theta.q < theta.p)
  6937. theta.q+=2.0*MagickPI;
  6938. arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
  6939. (2.0*sqrt((double) (1.0/mid)))));
  6940. CheckPathExtent(arc_segments+6*BezierQuantum+360);
  6941. path_q[q].x=box_q[1].x;
  6942. path_q[q].y=box_q[1].y;
  6943. q++;
  6944. for (j=1; j < (ssize_t) arc_segments; j++)
  6945. {
  6946. delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
  6947. path_q[q].x=(double) (center.x+mid*cos(fmod((double)
  6948. (theta.p+delta_theta),DegreesToRadians(360.0))));
  6949. path_q[q].y=(double) (center.y+mid*sin(fmod((double)
  6950. (theta.p+delta_theta),DegreesToRadians(360.0))));
  6951. q++;
  6952. }
  6953. path_q[q++]=box_q[2];
  6954. break;
  6955. }
  6956. default:
  6957. break;
  6958. }
  6959. else
  6960. switch (draw_info->linejoin)
  6961. {
  6962. case BevelJoin:
  6963. {
  6964. path_p[p++]=box_p[1];
  6965. path_p[p++]=box_p[2];
  6966. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6967. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6968. if (dot_product <= miterlimit)
  6969. path_q[q++]=box_q[4];
  6970. else
  6971. {
  6972. path_q[q++]=box_q[1];
  6973. path_q[q++]=box_q[2];
  6974. }
  6975. break;
  6976. }
  6977. case MiterJoin:
  6978. {
  6979. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6980. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6981. if (dot_product <= miterlimit)
  6982. {
  6983. path_q[q++]=box_q[4];
  6984. path_p[p++]=box_p[4];
  6985. }
  6986. else
  6987. {
  6988. path_q[q++]=box_q[1];
  6989. path_q[q++]=box_q[2];
  6990. path_p[p++]=box_p[1];
  6991. path_p[p++]=box_p[2];
  6992. }
  6993. break;
  6994. }
  6995. case RoundJoin:
  6996. {
  6997. dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
  6998. (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
  6999. if (dot_product <= miterlimit)
  7000. path_q[q++]=box_q[4];
  7001. else
  7002. {
  7003. path_q[q++]=box_q[1];
  7004. path_q[q++]=box_q[2];
  7005. }
  7006. center=polygon_primitive[n].point;
  7007. theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
  7008. theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
  7009. if (theta.p < theta.q)
  7010. theta.p+=2.0*MagickPI;
  7011. arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
  7012. (2.0*sqrt((double) (1.0/mid)))));
  7013. CheckPathExtent(arc_segments+6*BezierQuantum+360);
  7014. path_p[p++]=box_p[1];
  7015. for (j=1; j < (ssize_t) arc_segments; j++)
  7016. {
  7017. delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
  7018. path_p[p].x=(double) (center.x+mid*cos(fmod((double)
  7019. (theta.p+delta_theta),DegreesToRadians(360.0))));
  7020. path_p[p].y=(double) (center.y+mid*sin(fmod((double)
  7021. (theta.p+delta_theta),DegreesToRadians(360.0))));
  7022. p++;
  7023. }
  7024. path_p[p++]=box_p[2];
  7025. break;
  7026. }
  7027. default:
  7028. break;
  7029. }
  7030. slope.p=slope.q;
  7031. inverse_slope.p=inverse_slope.q;
  7032. box_p[0]=box_p[2];
  7033. box_p[1]=box_p[3];
  7034. box_q[0]=box_q[2];
  7035. box_q[1]=box_q[3];
  7036. dx.p=dx.q;
  7037. dy.p=dy.q;
  7038. n=i;
  7039. }
  7040. path_p[p++]=box_p[1];
  7041. path_q[q++]=box_q[1];
  7042. /*
  7043. Trace stroked polygon.
  7044. */
  7045. stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
  7046. (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
  7047. if (stroke_polygon != (PrimitiveInfo *) NULL)
  7048. {
  7049. for (i=0; i < (ssize_t) p; i++)
  7050. {
  7051. stroke_polygon[i]=polygon_primitive[0];
  7052. stroke_polygon[i].point=path_p[i];
  7053. }
  7054. if (closed_path != MagickFalse)
  7055. {
  7056. stroke_polygon[i]=polygon_primitive[0];
  7057. stroke_polygon[i].point=stroke_polygon[0].point;
  7058. i++;
  7059. }
  7060. for ( ; i < (ssize_t) (p+q+closed_path); i++)
  7061. {
  7062. stroke_polygon[i]=polygon_primitive[0];
  7063. stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
  7064. }
  7065. if (closed_path != MagickFalse)
  7066. {
  7067. stroke_polygon[i]=polygon_primitive[0];
  7068. stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
  7069. i++;
  7070. }
  7071. stroke_polygon[i]=polygon_primitive[0];
  7072. stroke_polygon[i].point=stroke_polygon[0].point;
  7073. i++;
  7074. stroke_polygon[i].primitive=UndefinedPrimitive;
  7075. stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
  7076. }
  7077. path_p=(PointInfo *) RelinquishMagickMemory(path_p);
  7078. path_q=(PointInfo *) RelinquishMagickMemory(path_q);
  7079. polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
  7080. return(stroke_polygon);
  7081. }