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