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