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