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