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