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