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