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