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