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*/
98typedef 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
122typedef struct _ElementInfo
123{
124  double
125    cx,
126    cy,
127    major,
128    minor,
129    angle;
130} ElementInfo;
131
132typedef struct _PolygonInfo
133{
134  EdgeInfo
135    *edges;
136
137  size_t
138    number_edges;
139} PolygonInfo;
140
141typedef enum
142{
143  MoveToCode,
144  OpenCode,
145  GhostlineCode,
146  LineToCode,
147  EndCode
148} PathInfoCode;
149
150typedef struct _PathInfo
151{
152  PointInfo
153    point;
154
155  PathInfoCode
156    code;
157} PathInfo;
158
159/*
160  Forward declarations.
161*/
162static MagickBooleanType
163  DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
164    ExceptionInfo *);
165
166static PrimitiveInfo
167  *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
168
169static size_t
170  TracePath(PrimitiveInfo *,const char *);
171
172static 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*/
204MagickExport 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*/
243MagickExport 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)
384extern "C" {
385#endif
386
387static 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
416static 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
446static 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
462static 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
695static 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
710static 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*/
825MagickExport 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*/
891static 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*/
926static 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
969static 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
1055static 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
1076MagickExport 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*/
1253static 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*/
1394MagickExport 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*/
1477static 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
1652static 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
1664static inline void TracePoint(PrimitiveInfo *primitive_info,
1665  const PointInfo point)
1666{
1667  primitive_info->coordinates=1;
1668  primitive_info->point=point;
1669}
1670
1671MagickExport 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
3303static 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
3359static 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
3374MagickExport 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*/
3637MagickExport 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
3731static 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
3744static 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
3778static 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
3949static 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);
3995DisableMSCWarning(4127)
3996  if (0)
3997    DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
3998RestoreMSCWarning
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
4202static 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
4304MagickExport 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
4796static 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
4816static 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*/
4899MagickExport 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*/
4932MagickExport 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*/
5068static 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
5100static 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
5114static 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
5247static 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
5340static 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
5362static 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
5416static 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
5432static 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
5802static 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
5837static 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
5892static 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
5940static 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
5946static 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