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