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