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