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