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