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