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