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