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