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