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