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