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