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