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