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