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