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