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