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