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