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