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