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