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