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