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