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