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