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