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