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