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]+(n == 0 ? -0.5 : 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 % %
1662 % D r a w I m a g e %
1663 % %
1664 % %
1665 % %
1666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1667 %
1668 % DrawImage() draws a graphic primitive on your image. The primitive
1669 % may be represented as a string or filename. Precede the filename with an
1670 % "at" sign (@) and the contents of the file are drawn on the image. You
1671 % can affect how text is drawn by setting one or more members of the draw
1672 % info structure.
1673 %
1674 % The format of the DrawImage method is:
1675 %
1676 % MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
1677 %
1678 % A description of each parameter follows:
1679 %
1680 % o image: the image.
1681 %
1682 % o draw_info: the draw info.
1683 %
1684 */
1685 
1686 static inline MagickBooleanType IsPoint(const char *point)
1687 {
1688  char
1689  *p;
1690 
1691  double
1692  value;
1693 
1694  value=StringToDouble(point,&p);
1695  return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1696 }
1697 
1698 static inline void TracePoint(PrimitiveInfo *primitive_info,
1699  const PointInfo point)
1700 {
1701  primitive_info->coordinates=1;
1702  primitive_info->point=point;
1703 }
1704 
1706 {
1707 #define RenderImageTag "Render/Image"
1708 
1709  AffineMatrix
1710  affine,
1711  current;
1712 
1713  char
1714  key[2*MaxTextExtent],
1715  keyword[MaxTextExtent],
1716  geometry[MaxTextExtent],
1717  name[MaxTextExtent],
1718  pattern[MaxTextExtent],
1719  *primitive,
1720  *token;
1721 
1722  const char
1723  *q;
1724 
1725  double
1726  angle,
1727  factor,
1728  primitive_extent;
1729 
1730  DrawInfo
1731  **graphic_context;
1732 
1734  proceed;
1735 
1737  status;
1738 
1739  PointInfo
1740  point;
1741 
1742  PixelPacket
1743  start_color;
1744 
1746  *primitive_info;
1747 
1749  primitive_type;
1750 
1751  register const char
1752  *p;
1753 
1754  register ssize_t
1755  i,
1756  x;
1757 
1758  SegmentInfo
1759  bounds;
1760 
1761  size_t
1762  length,
1763  number_points;
1764 
1765  ssize_t
1766  j,
1767  k,
1768  n;
1769 
1770  /*
1771  Ensure the annotation info is valid.
1772  */
1773  assert(image != (Image *) NULL);
1774  assert(image->signature == MagickSignature);
1775  if (image->debug != MagickFalse)
1776  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1777  assert(draw_info != (DrawInfo *) NULL);
1778  assert(draw_info->signature == MagickSignature);
1779  if (image->debug != MagickFalse)
1780  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1781  if ((draw_info->primitive == (char *) NULL) ||
1782  (*draw_info->primitive == '\0'))
1783  return(MagickFalse);
1784  if (image->debug != MagickFalse)
1785  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
1786  if (*draw_info->primitive != '@')
1787  primitive=AcquireString(draw_info->primitive);
1788  else
1789  primitive=FileToString(draw_info->primitive+1,~0UL,&image->exception);
1790  if (primitive == (char *) NULL)
1791  return(MagickFalse);
1792  primitive_extent=(double) strlen(primitive);
1793  (void) SetImageArtifact(image,"MVG",primitive);
1794  n=0;
1795  /*
1796  Allocate primitive info memory.
1797  */
1798  graphic_context=(DrawInfo **) AcquireMagickMemory(
1799  sizeof(*graphic_context));
1800  if (graphic_context == (DrawInfo **) NULL)
1801  {
1802  primitive=DestroyString(primitive);
1803  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1804  image->filename);
1805  }
1806  number_points=6553;
1807  primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
1808  sizeof(*primitive_info));
1809  if (primitive_info == (PrimitiveInfo *) NULL)
1810  {
1811  primitive=DestroyString(primitive);
1812  for ( ; n >= 0; n--)
1813  graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
1814  graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
1815  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1816  image->filename);
1817  }
1818  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1819  graphic_context[n]->viewbox=image->page;
1820  if ((image->page.width == 0) || (image->page.height == 0))
1821  {
1822  graphic_context[n]->viewbox.width=image->columns;
1823  graphic_context[n]->viewbox.height=image->rows;
1824  }
1825  token=AcquireString(primitive);
1826  (void) QueryColorDatabase("#000000",&start_color,&image->exception);
1828  return(MagickFalse);
1829  status=MagickTrue;
1830  for (q=primitive; *q != '\0'; )
1831  {
1832  /*
1833  Interpret graphic primitive.
1834  */
1835  GetMagickToken(q,&q,keyword);
1836  if (*keyword == '\0')
1837  break;
1838  if (*keyword == '#')
1839  {
1840  /*
1841  Comment.
1842  */
1843  while ((*q != '\n') && (*q != '\0'))
1844  q++;
1845  continue;
1846  }
1847  p=q-strlen(keyword)-1;
1848  primitive_type=UndefinedPrimitive;
1849  current=graphic_context[n]->affine;
1850  GetAffineMatrix(&affine);
1851  switch (*keyword)
1852  {
1853  case ';':
1854  break;
1855  case 'a':
1856  case 'A':
1857  {
1858  if (LocaleCompare("affine",keyword) == 0)
1859  {
1860  GetMagickToken(q,&q,token);
1861  affine.sx=StringToDouble(token,(char **) NULL);
1862  GetMagickToken(q,&q,token);
1863  if (*token == ',')
1864  GetMagickToken(q,&q,token);
1865  affine.rx=StringToDouble(token,(char **) NULL);
1866  GetMagickToken(q,&q,token);
1867  if (*token == ',')
1868  GetMagickToken(q,&q,token);
1869  affine.ry=StringToDouble(token,(char **) NULL);
1870  GetMagickToken(q,&q,token);
1871  if (*token == ',')
1872  GetMagickToken(q,&q,token);
1873  affine.sy=StringToDouble(token,(char **) NULL);
1874  GetMagickToken(q,&q,token);
1875  if (*token == ',')
1876  GetMagickToken(q,&q,token);
1877  affine.tx=StringToDouble(token,(char **) NULL);
1878  GetMagickToken(q,&q,token);
1879  if (*token == ',')
1880  GetMagickToken(q,&q,token);
1881  affine.ty=StringToDouble(token,(char **) NULL);
1882  break;
1883  }
1884  if (LocaleCompare("arc",keyword) == 0)
1885  {
1886  primitive_type=ArcPrimitive;
1887  break;
1888  }
1889  status=MagickFalse;
1890  break;
1891  }
1892  case 'b':
1893  case 'B':
1894  {
1895  if (LocaleCompare("bezier",keyword) == 0)
1896  {
1897  primitive_type=BezierPrimitive;
1898  break;
1899  }
1900  if (LocaleCompare("border-color",keyword) == 0)
1901  {
1902  GetMagickToken(q,&q,token);
1903  (void) QueryColorDatabase(token,&graphic_context[n]->border_color,
1904  &image->exception);
1905  break;
1906  }
1907  status=MagickFalse;
1908  break;
1909  }
1910  case 'c':
1911  case 'C':
1912  {
1913  if (LocaleCompare("clip-path",keyword) == 0)
1914  {
1915  /*
1916  Create clip mask.
1917  */
1918  GetMagickToken(q,&q,token);
1919  (void) CloneString(&graphic_context[n]->clip_mask,token);
1920  (void) DrawClipPath(image,graphic_context[n],
1921  graphic_context[n]->clip_mask);
1922  break;
1923  }
1924  if (LocaleCompare("clip-rule",keyword) == 0)
1925  {
1926  ssize_t
1927  fill_rule;
1928 
1929  GetMagickToken(q,&q,token);
1931  token);
1932  if (fill_rule == -1)
1933  {
1934  status=MagickFalse;
1935  break;
1936  }
1937  graphic_context[n]->fill_rule=(FillRule) fill_rule;
1938  break;
1939  }
1940  if (LocaleCompare("clip-units",keyword) == 0)
1941  {
1942  ssize_t
1943  clip_units;
1944 
1945  GetMagickToken(q,&q,token);
1947  token);
1948  if (clip_units == -1)
1949  {
1950  status=MagickFalse;
1951  break;
1952  }
1953  graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1954  if (clip_units == ObjectBoundingBox)
1955  {
1956  GetAffineMatrix(&current);
1957  affine.sx=draw_info->bounds.x2;
1958  affine.sy=draw_info->bounds.y2;
1959  affine.tx=draw_info->bounds.x1;
1960  affine.ty=draw_info->bounds.y1;
1961  break;
1962  }
1963  break;
1964  }
1965  if (LocaleCompare("circle",keyword) == 0)
1966  {
1967  primitive_type=CirclePrimitive;
1968  break;
1969  }
1970  if (LocaleCompare("color",keyword) == 0)
1971  {
1972  primitive_type=ColorPrimitive;
1973  break;
1974  }
1975  status=MagickFalse;
1976  break;
1977  }
1978  case 'd':
1979  case 'D':
1980  {
1981  if (LocaleCompare("decorate",keyword) == 0)
1982  {
1983  ssize_t
1984  decorate;
1985 
1986  GetMagickToken(q,&q,token);
1988  token);
1989  if (decorate == -1)
1990  {
1991  status=MagickFalse;
1992  break;
1993  }
1994  graphic_context[n]->decorate=(DecorationType) decorate;
1995  break;
1996  }
1997  if (LocaleCompare("direction",keyword) == 0)
1998  {
1999  ssize_t
2000  direction;
2001 
2002  GetMagickToken(q,&q,token);
2004  token);
2005  if (direction == -1)
2006  status=MagickFalse;
2007  else
2008  graphic_context[n]->direction=(DirectionType) direction;
2009  break;
2010  }
2011  status=MagickFalse;
2012  break;
2013  }
2014  case 'e':
2015  case 'E':
2016  {
2017  if (LocaleCompare("ellipse",keyword) == 0)
2018  {
2019  primitive_type=EllipsePrimitive;
2020  break;
2021  }
2022  if (LocaleCompare("encoding",keyword) == 0)
2023  {
2024  GetMagickToken(q,&q,token);
2025  (void) CloneString(&graphic_context[n]->encoding,token);
2026  break;
2027  }
2028  status=MagickFalse;
2029  break;
2030  }
2031  case 'f':
2032  case 'F':
2033  {
2034  if (LocaleCompare("fill",keyword) == 0)
2035  {
2036  GetMagickToken(q,&q,token);
2037  (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
2038  if (GetImageArtifact(image,pattern) != (const char *) NULL)
2039  (void) DrawPatternPath(image,draw_info,token,
2040  &graphic_context[n]->fill_pattern);
2041  else
2042  {
2043  status&=QueryColorDatabase(token,&graphic_context[n]->fill,
2044  &image->exception);
2045  if (status == MagickFalse)
2046  {
2047  ImageInfo
2048  *pattern_info;
2049 
2050  pattern_info=AcquireImageInfo();
2051  (void) CopyMagickString(pattern_info->filename,token,
2052  MaxTextExtent);
2053  graphic_context[n]->fill_pattern=
2054  ReadImage(pattern_info,&image->exception);
2055  CatchException(&image->exception);
2056  pattern_info=DestroyImageInfo(pattern_info);
2057  }
2058  }
2059  break;
2060  }
2061  if (LocaleCompare("fill-opacity",keyword) == 0)
2062  {
2063  GetMagickToken(q,&q,token);
2064  factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
2065  graphic_context[n]->fill.opacity=ClampToQuantum((MagickRealType)
2066  QuantumRange*(1.0-factor*StringToDouble(token,(char **) NULL)));
2067  break;
2068  }
2069  if (LocaleCompare("fill-rule",keyword) == 0)
2070  {
2071  ssize_t
2072  fill_rule;
2073 
2074  GetMagickToken(q,&q,token);
2076  token);
2077  if (fill_rule == -1)
2078  {
2079  status=MagickFalse;
2080  break;
2081  }
2082  graphic_context[n]->fill_rule=(FillRule) fill_rule;
2083  break;
2084  }
2085  if (LocaleCompare("font",keyword) == 0)
2086  {
2087  GetMagickToken(q,&q,token);
2088  (void) CloneString(&graphic_context[n]->font,token);
2089  if (LocaleCompare("none",token) == 0)
2090  graphic_context[n]->font=(char *)
2091  RelinquishMagickMemory(graphic_context[n]->font);
2092  break;
2093  }
2094  if (LocaleCompare("font-family",keyword) == 0)
2095  {
2096  GetMagickToken(q,&q,token);
2097  (void) CloneString(&graphic_context[n]->family,token);
2098  break;
2099  }
2100  if (LocaleCompare("font-size",keyword) == 0)
2101  {
2102  GetMagickToken(q,&q,token);
2103  graphic_context[n]->pointsize=StringToDouble(token,(char **) NULL);
2104  break;
2105  }
2106  if (LocaleCompare("font-stretch",keyword) == 0)
2107  {
2108  ssize_t
2109  stretch;
2110 
2111  GetMagickToken(q,&q,token);
2113  if (stretch == -1)
2114  {
2115  status=MagickFalse;
2116  break;
2117  }
2118  graphic_context[n]->stretch=(StretchType) stretch;
2119  break;
2120  }
2121  if (LocaleCompare("font-style",keyword) == 0)
2122  {
2123  ssize_t
2124  style;
2125 
2126  GetMagickToken(q,&q,token);
2128  if (style == -1)
2129  {
2130  status=MagickFalse;
2131  break;
2132  }
2133  graphic_context[n]->style=(StyleType) style;
2134  break;
2135  }
2136  if (LocaleCompare("font-weight",keyword) == 0)
2137  {
2138  GetMagickToken(q,&q,token);
2139  graphic_context[n]->weight=StringToUnsignedLong(token);
2140  if (LocaleCompare(token,"all") == 0)
2141  graphic_context[n]->weight=0;
2142  if (LocaleCompare(token,"bold") == 0)
2143  graphic_context[n]->weight=700;
2144  if (LocaleCompare(token,"bolder") == 0)
2145  if (graphic_context[n]->weight <= 800)
2146  graphic_context[n]->weight+=100;
2147  if (LocaleCompare(token,"lighter") == 0)
2148  if (graphic_context[n]->weight >= 100)
2149  graphic_context[n]->weight-=100;
2150  if (LocaleCompare(token,"normal") == 0)
2151  graphic_context[n]->weight=400;
2152  break;
2153  }
2154  status=MagickFalse;
2155  break;
2156  }
2157  case 'g':
2158  case 'G':
2159  {
2160  if (LocaleCompare("gradient-units",keyword) == 0)
2161  {
2162  GetMagickToken(q,&q,token);
2163  break;
2164  }
2165  if (LocaleCompare("gravity",keyword) == 0)
2166  {
2167  ssize_t
2168  gravity;
2169 
2170  GetMagickToken(q,&q,token);
2172  if (gravity == -1)
2173  {
2174  status=MagickFalse;
2175  break;
2176  }
2177  graphic_context[n]->gravity=(GravityType) gravity;
2178  break;
2179  }
2180  status=MagickFalse;
2181  break;
2182  }
2183  case 'i':
2184  case 'I':
2185  {
2186  if (LocaleCompare("image",keyword) == 0)
2187  {
2188  ssize_t
2189  compose;
2190 
2191  primitive_type=ImagePrimitive;
2192  GetMagickToken(q,&q,token);
2194  if (compose == -1)
2195  {
2196  status=MagickFalse;
2197  break;
2198  }
2199  graphic_context[n]->compose=(CompositeOperator) compose;
2200  break;
2201  }
2202  if (LocaleCompare("interline-spacing",keyword) == 0)
2203  {
2204  GetMagickToken(q,&q,token);
2205  graphic_context[n]->interline_spacing=StringToDouble(token,
2206  (char **) NULL);
2207  break;
2208  }
2209  if (LocaleCompare("interword-spacing",keyword) == 0)
2210  {
2211  GetMagickToken(q,&q,token);
2212  graphic_context[n]->interword_spacing=StringToDouble(token,
2213  (char **) NULL);
2214  break;
2215  }
2216  status=MagickFalse;
2217  break;
2218  }
2219  case 'k':
2220  case 'K':
2221  {
2222  if (LocaleCompare("kerning",keyword) == 0)
2223  {
2224  GetMagickToken(q,&q,token);
2225  graphic_context[n]->kerning=StringToDouble(token,(char **) NULL);
2226  break;
2227  }
2228  status=MagickFalse;
2229  break;
2230  }
2231  case 'l':
2232  case 'L':
2233  {
2234  if (LocaleCompare("line",keyword) == 0)
2235  {
2236  primitive_type=LinePrimitive;
2237  break;
2238  }
2239  status=MagickFalse;
2240  break;
2241  }
2242  case 'm':
2243  case 'M':
2244  {
2245  if (LocaleCompare("matte",keyword) == 0)
2246  {
2247  primitive_type=MattePrimitive;
2248  break;
2249  }
2250  status=MagickFalse;
2251  break;
2252  }
2253  case 'o':
2254  case 'O':
2255  {
2256  if (LocaleCompare("offset",keyword) == 0)
2257  {
2258  GetMagickToken(q,&q,token);
2259  break;
2260  }
2261  if (LocaleCompare("opacity",keyword) == 0)
2262  {
2263  GetMagickToken(q,&q,token);
2264  factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
2265  graphic_context[n]->opacity=ClampToQuantum((MagickRealType)
2266  QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->opacity)*
2267  factor*StringToDouble(token,(char **) NULL))));
2268  graphic_context[n]->fill.opacity=graphic_context[n]->opacity;
2269  graphic_context[n]->stroke.opacity=graphic_context[n]->opacity;
2270  break;
2271  }
2272  status=MagickFalse;
2273  break;
2274  }
2275  case 'p':
2276  case 'P':
2277  {
2278  if (LocaleCompare("path",keyword) == 0)
2279  {
2280  primitive_type=PathPrimitive;
2281  break;
2282  }
2283  if (LocaleCompare("point",keyword) == 0)
2284  {
2285  primitive_type=PointPrimitive;
2286  break;
2287  }
2288  if (LocaleCompare("polyline",keyword) == 0)
2289  {
2290  primitive_type=PolylinePrimitive;
2291  break;
2292  }
2293  if (LocaleCompare("polygon",keyword) == 0)
2294  {
2295  primitive_type=PolygonPrimitive;
2296  break;
2297  }
2298  if (LocaleCompare("pop",keyword) == 0)
2299  {
2300  GetMagickToken(q,&q,token);
2301  if (LocaleCompare("clip-path",token) == 0)
2302  break;
2303  if (LocaleCompare("defs",token) == 0)
2304  break;
2305  if (LocaleCompare("gradient",token) == 0)
2306  break;
2307  if (LocaleCompare("graphic-context",token) == 0)
2308  {
2309  if (n <= 0)
2310  {
2311  (void) ThrowMagickException(&image->exception,
2313  "UnbalancedGraphicContextPushPop","`%s'",token);
2314  n=0;
2315  break;
2316  }
2317  if (graphic_context[n]->clip_mask != (char *) NULL)
2318  if (LocaleCompare(graphic_context[n]->clip_mask,
2319  graphic_context[n-1]->clip_mask) != 0)
2320  (void) SetImageClipMask(image,(Image *) NULL);
2321  graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2322  n--;
2323  break;
2324  }
2325  if (LocaleCompare("pattern",token) == 0)
2326  break;
2327  status=MagickFalse;
2328  break;
2329  }
2330  if (LocaleCompare("push",keyword) == 0)
2331  {
2332  GetMagickToken(q,&q,token);
2333  if (LocaleCompare("clip-path",token) == 0)
2334  {
2335  char
2336  name[MaxTextExtent];
2337 
2338  GetMagickToken(q,&q,token);
2339  (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
2340  for (p=q; *q != '\0'; )
2341  {
2342  GetMagickToken(q,&q,token);
2343  if (LocaleCompare(token,"pop") != 0)
2344  continue;
2345  GetMagickToken(q,(const char **) NULL,token);
2346  if (LocaleCompare(token,"clip-path") != 0)
2347  continue;
2348  break;
2349  }
2350  (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2351  (void) SetImageArtifact(image,name,token);
2352  GetMagickToken(q,&q,token);
2353  break;
2354  }
2355  if (LocaleCompare("gradient",token) == 0)
2356  {
2357  char
2358  key[2*MaxTextExtent],
2359  name[MaxTextExtent],
2360  type[MaxTextExtent];
2361 
2362  SegmentInfo
2363  segment;
2364 
2365  GetMagickToken(q,&q,token);
2366  (void) CopyMagickString(name,token,MaxTextExtent);
2367  GetMagickToken(q,&q,token);
2368  (void) CopyMagickString(type,token,MaxTextExtent);
2369  GetMagickToken(q,&q,token);
2370  segment.x1=StringToDouble(token,(char **) NULL);
2371  GetMagickToken(q,&q,token);
2372  if (*token == ',')
2373  GetMagickToken(q,&q,token);
2374  segment.y1=StringToDouble(token,(char **) NULL);
2375  GetMagickToken(q,&q,token);
2376  if (*token == ',')
2377  GetMagickToken(q,&q,token);
2378  segment.x2=StringToDouble(token,(char **) NULL);
2379  GetMagickToken(q,&q,token);
2380  if (*token == ',')
2381  GetMagickToken(q,&q,token);
2382  segment.y2=StringToDouble(token,(char **) NULL);
2383  if (LocaleCompare(type,"radial") == 0)
2384  {
2385  GetMagickToken(q,&q,token);
2386  if (*token == ',')
2387  GetMagickToken(q,&q,token);
2388  }
2389  for (p=q; *q != '\0'; )
2390  {
2391  GetMagickToken(q,&q,token);
2392  if (LocaleCompare(token,"pop") != 0)
2393  continue;
2394  GetMagickToken(q,(const char **) NULL,token);
2395  if (LocaleCompare(token,"gradient") != 0)
2396  continue;
2397  break;
2398  }
2399  (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2400  bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2401  graphic_context[n]->affine.ry*segment.y1+
2402  graphic_context[n]->affine.tx;
2403  bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2404  graphic_context[n]->affine.sy*segment.y1+
2405  graphic_context[n]->affine.ty;
2406  bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2407  graphic_context[n]->affine.ry*segment.y2+
2408  graphic_context[n]->affine.tx;
2409  bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2410  graphic_context[n]->affine.sy*segment.y2+
2411  graphic_context[n]->affine.ty;
2412  (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
2413  (void) SetImageArtifact(image,key,token);
2414  (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2415  (void) FormatLocaleString(geometry,MaxTextExtent,
2416  "%gx%g%+.15g%+.15g",
2417  MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2418  MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2419  bounds.x1,bounds.y1);
2420  (void) SetImageArtifact(image,key,geometry);
2421  GetMagickToken(q,&q,token);
2422  break;
2423  }
2424  if (LocaleCompare("pattern",token) == 0)
2425  {
2427  bounds;
2428 
2429  GetMagickToken(q,&q,token);
2430  (void) CopyMagickString(name,token,MaxTextExtent);
2431  GetMagickToken(q,&q,token);
2432  bounds.x=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2433  0.5);
2434  GetMagickToken(q,&q,token);
2435  if (*token == ',')
2436  GetMagickToken(q,&q,token);
2437  bounds.y=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2438  0.5);
2439  GetMagickToken(q,&q,token);
2440  if (*token == ',')
2441  GetMagickToken(q,&q,token);
2442  bounds.width=(size_t) floor(StringToDouble(token,
2443  (char **) NULL)+0.5);
2444  GetMagickToken(q,&q,token);
2445  if (*token == ',')
2446  GetMagickToken(q,&q,token);
2447  bounds.height=(size_t) floor(StringToDouble(token,
2448  (char **) NULL)+0.5);
2449  for (p=q; *q != '\0'; )
2450  {
2451  GetMagickToken(q,&q,token);
2452  if (LocaleCompare(token,"pop") != 0)
2453  continue;
2454  GetMagickToken(q,(const char **) NULL,token);
2455  if (LocaleCompare(token,"pattern") != 0)
2456  continue;
2457  break;
2458  }
2459  (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2460  (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
2461  (void) SetImageArtifact(image,key,token);
2462  (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2463  (void) FormatLocaleString(geometry,MaxTextExtent,
2464  "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
2465  bounds.height,(double) bounds.x,(double) bounds.y);
2466  (void) SetImageArtifact(image,key,geometry);
2467  GetMagickToken(q,&q,token);
2468  break;
2469  }
2470  if (LocaleCompare("graphic-context",token) == 0)
2471  {
2472  n++;
2473  graphic_context=(DrawInfo **) ResizeQuantumMemory(
2474  graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2475  if (graphic_context == (DrawInfo **) NULL)
2476  {
2477  (void) ThrowMagickException(&image->exception,
2479  "MemoryAllocationFailed","`%s'",image->filename);
2480  break;
2481  }
2482  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2483  graphic_context[n-1]);
2484  break;
2485  }
2486  if (LocaleCompare("defs",token) == 0)
2487  break;
2488  status=MagickFalse;
2489  break;
2490  }
2491  status=MagickFalse;
2492  break;
2493  }
2494  case 'r':
2495  case 'R':
2496  {
2497  if (LocaleCompare("rectangle",keyword) == 0)
2498  {
2499  primitive_type=RectanglePrimitive;
2500  break;
2501  }
2502  if (LocaleCompare("rotate",keyword) == 0)
2503  {
2504  GetMagickToken(q,&q,token);
2505  angle=StringToDouble(token,(char **) NULL);
2506  affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2507  affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2508  affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2509  affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2510  break;
2511  }
2512  if (LocaleCompare("roundRectangle",keyword) == 0)
2513  {
2514  primitive_type=RoundRectanglePrimitive;
2515  break;
2516  }
2517  status=MagickFalse;
2518  break;
2519  }
2520  case 's':
2521  case 'S':
2522  {
2523  if (LocaleCompare("scale",keyword) == 0)
2524  {
2525  GetMagickToken(q,&q,token);
2526  affine.sx=StringToDouble(token,(char **) NULL);
2527  GetMagickToken(q,&q,token);
2528  if (*token == ',')
2529  GetMagickToken(q,&q,token);
2530  affine.sy=StringToDouble(token,(char **) NULL);
2531  break;
2532  }
2533  if (LocaleCompare("skewX",keyword) == 0)
2534  {
2535  GetMagickToken(q,&q,token);
2536  angle=StringToDouble(token,(char **) NULL);
2537  affine.ry=sin(DegreesToRadians(angle));
2538  break;
2539  }
2540  if (LocaleCompare("skewY",keyword) == 0)
2541  {
2542  GetMagickToken(q,&q,token);
2543  angle=StringToDouble(token,(char **) NULL);
2544  affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2545  break;
2546  }
2547  if (LocaleCompare("stop-color",keyword) == 0)
2548  {
2549  PixelPacket
2550  stop_color;
2551 
2552  GetMagickToken(q,&q,token);
2553  (void) QueryColorDatabase(token,&stop_color,&image->exception);
2555  &start_color,&stop_color);
2556  start_color=stop_color;
2557  GetMagickToken(q,&q,token);
2558  break;
2559  }
2560  if (LocaleCompare("stroke",keyword) == 0)
2561  {
2562  GetMagickToken(q,&q,token);
2563  (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
2564  if (GetImageArtifact(image,pattern) != (const char *) NULL)
2565  (void) DrawPatternPath(image,draw_info,token,
2566  &graphic_context[n]->stroke_pattern);
2567  else
2568  {
2569  status&=QueryColorDatabase(token,&graphic_context[n]->stroke,
2570  &image->exception);
2571  if (status == MagickFalse)
2572  {
2573  ImageInfo
2574  *pattern_info;
2575 
2576  pattern_info=AcquireImageInfo();
2577  (void) CopyMagickString(pattern_info->filename,token,
2578  MaxTextExtent);
2579  graphic_context[n]->stroke_pattern=
2580  ReadImage(pattern_info,&image->exception);
2581  CatchException(&image->exception);
2582  pattern_info=DestroyImageInfo(pattern_info);
2583  }
2584  }
2585  break;
2586  }
2587  if (LocaleCompare("stroke-antialias",keyword) == 0)
2588  {
2589  GetMagickToken(q,&q,token);
2590  graphic_context[n]->stroke_antialias=
2591  StringToLong(token) != 0 ? MagickTrue : MagickFalse;
2592  break;
2593  }
2594  if (LocaleCompare("stroke-dasharray",keyword) == 0)
2595  {
2596  if (graphic_context[n]->dash_pattern != (double *) NULL)
2597  graphic_context[n]->dash_pattern=(double *)
2598  RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2599  if (IsPoint(q) != MagickFalse)
2600  {
2601  const char
2602  *p;
2603 
2604  p=q;
2605  GetMagickToken(p,&p,token);
2606  if (*token == ',')
2607  GetMagickToken(p,&p,token);
2608  for (x=0; IsPoint(token) != MagickFalse; x++)
2609  {
2610  GetMagickToken(p,&p,token);
2611  if (*token == ',')
2612  GetMagickToken(p,&p,token);
2613  }
2614  graphic_context[n]->dash_pattern=(double *)
2615  AcquireQuantumMemory((size_t) (2UL*x+1UL),
2616  sizeof(*graphic_context[n]->dash_pattern));
2617  if (graphic_context[n]->dash_pattern == (double *) NULL)
2618  {
2619  (void) ThrowMagickException(&image->exception,
2621  "MemoryAllocationFailed","`%s'",image->filename);
2622  break;
2623  }
2624  for (j=0; j < x; j++)
2625  {
2626  GetMagickToken(q,&q,token);
2627  if (*token == ',')
2628  GetMagickToken(q,&q,token);
2629  graphic_context[n]->dash_pattern[j]=StringToDouble(token,
2630  (char **) NULL);
2631  }
2632  if ((x & 0x01) != 0)
2633  for ( ; j < (2*x); j++)
2634  graphic_context[n]->dash_pattern[j]=
2635  graphic_context[n]->dash_pattern[j-x];
2636  graphic_context[n]->dash_pattern[j]=0.0;
2637  break;
2638  }
2639  GetMagickToken(q,&q,token);
2640  break;
2641  }
2642  if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2643  {
2644  GetMagickToken(q,&q,token);
2645  graphic_context[n]->dash_offset=StringToDouble(token,
2646  (char **) NULL);
2647  break;
2648  }
2649  if (LocaleCompare("stroke-linecap",keyword) == 0)
2650  {
2651  ssize_t
2652  linecap;
2653 
2654  GetMagickToken(q,&q,token);
2656  if (linecap == -1)
2657  {
2658  status=MagickFalse;
2659  break;
2660  }
2661  graphic_context[n]->linecap=(LineCap) linecap;
2662  break;
2663  }
2664  if (LocaleCompare("stroke-linejoin",keyword) == 0)
2665  {
2666  ssize_t
2667  linejoin;
2668 
2669  GetMagickToken(q,&q,token);
2671  if (linejoin == -1)
2672  {
2673  status=MagickFalse;
2674  break;
2675  }
2676  graphic_context[n]->linejoin=(LineJoin) linejoin;
2677  break;
2678  }
2679  if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2680  {
2681  GetMagickToken(q,&q,token);
2682  graphic_context[n]->miterlimit=StringToUnsignedLong(token);
2683  break;
2684  }
2685  if (LocaleCompare("stroke-opacity",keyword) == 0)
2686  {
2687  GetMagickToken(q,&q,token);
2688  factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
2689  graphic_context[n]->stroke.opacity=ClampToQuantum((MagickRealType)
2690  QuantumRange*(1.0-factor*StringToDouble(token,(char **) NULL)));
2691  break;
2692  }
2693  if (LocaleCompare("stroke-width",keyword) == 0)
2694  {
2695  GetMagickToken(q,&q,token);
2696  graphic_context[n]->stroke_width=StringToDouble(token,
2697  (char **) NULL);
2698  break;
2699  }
2700  status=MagickFalse;
2701  break;
2702  }
2703  case 't':
2704  case 'T':
2705  {
2706  if (LocaleCompare("text",keyword) == 0)
2707  {
2708  primitive_type=TextPrimitive;
2709  break;
2710  }
2711  if (LocaleCompare("text-align",keyword) == 0)
2712  {
2713  ssize_t
2714  align;
2715 
2716  GetMagickToken(q,&q,token);
2718  if (align == -1)
2719  {
2720  status=MagickFalse;
2721  break;
2722  }
2723  graphic_context[n]->align=(AlignType) align;
2724  break;
2725  }
2726  if (LocaleCompare("text-anchor",keyword) == 0)
2727  {
2728  ssize_t
2729  align;
2730 
2731  GetMagickToken(q,&q,token);
2733  if (align == -1)
2734  {
2735  status=MagickFalse;
2736  break;
2737  }
2738  graphic_context[n]->align=(AlignType) align;
2739  break;
2740  }
2741  if (LocaleCompare("text-antialias",keyword) == 0)
2742  {
2743  GetMagickToken(q,&q,token);
2744  graphic_context[n]->text_antialias=
2745  StringToLong(token) != 0 ? MagickTrue : MagickFalse;
2746  break;
2747  }
2748  if (LocaleCompare("text-undercolor",keyword) == 0)
2749  {
2750  GetMagickToken(q,&q,token);
2751  (void) QueryColorDatabase(token,&graphic_context[n]->undercolor,
2752  &image->exception);
2753  break;
2754  }
2755  if (LocaleCompare("translate",keyword) == 0)
2756  {
2757  GetMagickToken(q,&q,token);
2758  affine.tx=StringToDouble(token,(char **) NULL);
2759  GetMagickToken(q,&q,token);
2760  if (*token == ',')
2761  GetMagickToken(q,&q,token);
2762  affine.ty=StringToDouble(token,(char **) NULL);
2763  break;
2764  }
2765  status=MagickFalse;
2766  break;
2767  }
2768  case 'v':
2769  case 'V':
2770  {
2771  if (LocaleCompare("viewbox",keyword) == 0)
2772  {
2773  GetMagickToken(q,&q,token);
2774  graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
2775  (char **) NULL)-0.5);
2776  GetMagickToken(q,&q,token);
2777  if (*token == ',')
2778  GetMagickToken(q,&q,token);
2779  graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
2780  (char **) NULL)-0.5);
2781  GetMagickToken(q,&q,token);
2782  if (*token == ',')
2783  GetMagickToken(q,&q,token);
2784  graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
2785  token,(char **) NULL)+0.5);
2786  GetMagickToken(q,&q,token);
2787  if (*token == ',')
2788  GetMagickToken(q,&q,token);
2789  graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
2790  token,(char **) NULL)+0.5);
2791  break;
2792  }
2793  status=MagickFalse;
2794  break;
2795  }
2796  default:
2797  {
2798  status=MagickFalse;
2799  break;
2800  }
2801  }
2802  if (status == MagickFalse)
2803  break;
2804  if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2805  (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2806  {
2807  graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2808  graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2809  graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2810  graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2811  graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2812  current.tx;
2813  graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2814  current.ty;
2815  }
2816  if (primitive_type == UndefinedPrimitive)
2817  {
2818  if (image->debug != MagickFalse)
2819  (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2820  (int) (q-p),p);
2821  continue;
2822  }
2823  /*
2824  Parse the primitive attributes.
2825  */
2826  i=0;
2827  j=0;
2828  primitive_info[0].point.x=0.0;
2829  primitive_info[0].point.y=0.0;
2830  for (x=0; *q != '\0'; x++)
2831  {
2832  /*
2833  Define points.
2834  */
2835  if (IsPoint(q) == MagickFalse)
2836  break;
2837  GetMagickToken(q,&q,token);
2838  point.x=StringToDouble(token,(char **) NULL);
2839  GetMagickToken(q,&q,token);
2840  if (*token == ',')
2841  GetMagickToken(q,&q,token);
2842  point.y=StringToDouble(token,(char **) NULL);
2843  GetMagickToken(q,(const char **) NULL,token);
2844  if (*token == ',')
2845  GetMagickToken(q,&q,token);
2846  primitive_info[i].primitive=primitive_type;
2847  primitive_info[i].point=point;
2848  primitive_info[i].coordinates=0;
2849  primitive_info[i].method=FloodfillMethod;
2850  i++;
2851  if (i < (ssize_t) number_points)
2852  continue;
2853  number_points<<=1;
2854  primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2855  (size_t) number_points,sizeof(*primitive_info));
2856  if (primitive_info == (PrimitiveInfo *) NULL)
2857  {
2859  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2860  break;
2861  }
2862  }
2863  primitive_info[j].primitive=primitive_type;
2864  primitive_info[j].coordinates=(size_t) x;
2865  primitive_info[j].method=FloodfillMethod;
2866  primitive_info[j].text=(char *) NULL;
2867  /*
2868  Circumscribe primitive within a circle.
2869  */
2870  bounds.x1=primitive_info[j].point.x;
2871  bounds.y1=primitive_info[j].point.y;
2872  bounds.x2=primitive_info[j].point.x;
2873  bounds.y2=primitive_info[j].point.y;
2874  for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
2875  {
2876  point=primitive_info[j+k].point;
2877  if (point.x < bounds.x1)
2878  bounds.x1=point.x;
2879  if (point.y < bounds.y1)
2880  bounds.y1=point.y;
2881  if (point.x > bounds.x2)
2882  bounds.x2=point.x;
2883  if (point.y > bounds.y2)
2884  bounds.y2=point.y;
2885  }
2886  /*
2887  Speculate how many points our primitive might consume.
2888  */
2889  length=primitive_info[j].coordinates;
2890  switch (primitive_type)
2891  {
2892  case RectanglePrimitive:
2893  {
2894  length*=5;
2895  break;
2896  }
2898  {
2899  length*=5+8*BezierQuantum;
2900  break;
2901  }
2902  case BezierPrimitive:
2903  {
2904  if (primitive_info[j].coordinates > 107)
2906  DrawError,"TooManyBezierCoordinates","`%s'",token);
2907  length=BezierQuantum*primitive_info[j].coordinates;
2908  break;
2909  }
2910  case PathPrimitive:
2911  {
2912  char
2913  *s,
2914  *t;
2915 
2916  GetMagickToken(q,&q,token);
2917  length=1;
2918  t=token;
2919  for (s=token; *s != '\0'; s=t)
2920  {
2921  double
2922  value;
2923 
2924  value=StringToDouble(s,&t);
2925  (void) value;
2926  if (s == t)
2927  {
2928  t++;
2929  continue;
2930  }
2931  length++;
2932  }
2933  length=length*BezierQuantum/2;
2934  break;
2935  }
2936  case CirclePrimitive:
2937  case ArcPrimitive:
2938  case EllipsePrimitive:
2939  {
2940  double
2941  alpha,
2942  beta,
2943  radius;
2944 
2945  alpha=bounds.x2-bounds.x1;
2946  beta=bounds.y2-bounds.y1;
2947  radius=hypot((double) alpha,(double) beta);
2948  length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
2949  break;
2950  }
2951  default:
2952  break;
2953  }
2954  if ((size_t) (i+length) >= number_points)
2955  {
2956  /*
2957  Resize based on speculative points required by primitive.
2958  */
2959  number_points+=length+1;
2960  primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2961  (size_t) number_points,sizeof(*primitive_info));
2962  if (primitive_info == (PrimitiveInfo *) NULL)
2963  {
2965  ResourceLimitError,"MemoryAllocationFailed","`%s'",
2966  image->filename);
2967  break;
2968  }
2969  }
2970  switch (primitive_type)
2971  {
2972  case PointPrimitive:
2973  default:
2974  {
2975  if (primitive_info[j].coordinates != 1)
2976  {
2977  status=MagickFalse;
2978  break;
2979  }
2980  TracePoint(primitive_info+j,primitive_info[j].point);
2981  i=(ssize_t) (j+primitive_info[j].coordinates);
2982  break;
2983  }
2984  case LinePrimitive:
2985  {
2986  if (primitive_info[j].coordinates != 2)
2987  {
2988  status=MagickFalse;
2989  break;
2990  }
2991  TraceLine(primitive_info+j,primitive_info[j].point,
2992  primitive_info[j+1].point);
2993  i=(ssize_t) (j+primitive_info[j].coordinates);
2994  break;
2995  }
2996  case RectanglePrimitive:
2997  {
2998  if (primitive_info[j].coordinates != 2)
2999  {
3000  status=MagickFalse;
3001  break;
3002  }
3003  TraceRectangle(primitive_info+j,primitive_info[j].point,
3004  primitive_info[j+1].point);
3005  i=(ssize_t) (j+primitive_info[j].coordinates);
3006  break;
3007  }
3009  {
3010  if (primitive_info[j].coordinates != 3)
3011  {
3012  status=MagickFalse;
3013  break;
3014  }
3015  TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
3016  primitive_info[j+1].point,primitive_info[j+2].point);
3017  i=(ssize_t) (j+primitive_info[j].coordinates);
3018  break;
3019  }
3020  case ArcPrimitive:
3021  {
3022  if (primitive_info[j].coordinates != 3)
3023  {
3024  primitive_type=UndefinedPrimitive;
3025  break;
3026  }
3027  TraceArc(primitive_info+j,primitive_info[j].point,
3028  primitive_info[j+1].point,primitive_info[j+2].point);
3029  i=(ssize_t) (j+primitive_info[j].coordinates);
3030  break;
3031  }
3032  case EllipsePrimitive:
3033  {
3034  if (primitive_info[j].coordinates != 3)
3035  {
3036  status=MagickFalse;
3037  break;
3038  }
3039  TraceEllipse(primitive_info+j,primitive_info[j].point,
3040  primitive_info[j+1].point,primitive_info[j+2].point);
3041  i=(ssize_t) (j+primitive_info[j].coordinates);
3042  break;
3043  }
3044  case CirclePrimitive:
3045  {
3046  if (primitive_info[j].coordinates != 2)
3047  {
3048  status=MagickFalse;
3049  break;
3050  }
3051  TraceCircle(primitive_info+j,primitive_info[j].point,
3052  primitive_info[j+1].point);
3053  i=(ssize_t) (j+primitive_info[j].coordinates);
3054  break;
3055  }
3056  case PolylinePrimitive:
3057  break;
3058  case PolygonPrimitive:
3059  {
3060  primitive_info[i]=primitive_info[j];
3061  primitive_info[i].coordinates=0;
3062  primitive_info[j].coordinates++;
3063  i++;
3064  break;
3065  }
3066  case BezierPrimitive:
3067  {
3068  if (primitive_info[j].coordinates < 3)
3069  {
3070  status=MagickFalse;
3071  break;
3072  }
3073  TraceBezier(primitive_info+j,primitive_info[j].coordinates);
3074  i=(ssize_t) (j+primitive_info[j].coordinates);
3075  break;
3076  }
3077  case PathPrimitive:
3078  {
3079  i=(ssize_t) (j+TracePath(primitive_info+j,token));
3080  break;
3081  }
3082  case ColorPrimitive:
3083  case MattePrimitive:
3084  {
3085  ssize_t
3086  method;
3087 
3088  if (primitive_info[j].coordinates != 1)
3089  {
3090  status=MagickFalse;
3091  break;
3092  }
3093  GetMagickToken(q,&q,token);
3095  if (method == -1)
3096  {
3097  status=MagickFalse;
3098  break;
3099  }
3100  primitive_info[j].method=(PaintMethod) method;
3101  break;
3102  }
3103  case TextPrimitive:
3104  {
3105  if (primitive_info[j].coordinates != 1)
3106  {
3107  status=MagickFalse;
3108  break;
3109  }
3110  if (*token != ',')
3111  GetMagickToken(q,&q,token);
3112  primitive_info[j].text=AcquireString(token);
3113  break;
3114  }
3115  case ImagePrimitive:
3116  {
3117  if (primitive_info[j].coordinates != 2)
3118  {
3119  status=MagickFalse;
3120  break;
3121  }
3122  GetMagickToken(q,&q,token);
3123  primitive_info[j].text=AcquireString(token);
3124  break;
3125  }
3126  }
3127  if (primitive_info == (PrimitiveInfo *) NULL)
3128  break;
3129  if (image->debug != MagickFalse)
3130  (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3131  if (status == MagickFalse)
3132  break;
3133  primitive_info[i].primitive=UndefinedPrimitive;
3134  if (i == 0)
3135  continue;
3136  /*
3137  Transform points.
3138  */
3139  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3140  {
3141  point=primitive_info[i].point;
3142  primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3143  graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3144  primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3145  graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3146  point=primitive_info[i].point;
3147  if (point.x < graphic_context[n]->bounds.x1)
3148  graphic_context[n]->bounds.x1=point.x;
3149  if (point.y < graphic_context[n]->bounds.y1)
3150  graphic_context[n]->bounds.y1=point.y;
3151  if (point.x > graphic_context[n]->bounds.x2)
3152  graphic_context[n]->bounds.x2=point.x;
3153  if (point.y > graphic_context[n]->bounds.y2)
3154  graphic_context[n]->bounds.y2=point.y;
3155  if (primitive_info[i].primitive == ImagePrimitive)
3156  break;
3157  if (i >= (ssize_t) number_points)
3158  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
3159  }
3160  if (graphic_context[n]->render != MagickFalse)
3161  {
3162  if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3163  (LocaleCompare(graphic_context[n]->clip_mask,
3164  graphic_context[n-1]->clip_mask) != 0))
3165  status&=DrawClipPath(image,graphic_context[n],
3166  graphic_context[n]->clip_mask);
3167  status&=DrawPrimitive(image,graphic_context[n],primitive_info);
3168  }
3169  if (primitive_info->text != (char *) NULL)
3170  primitive_info->text=(char *) RelinquishMagickMemory(
3171  primitive_info->text);
3172  proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3173  primitive_extent);
3174  if (proceed == MagickFalse)
3175  break;
3176  if (status == 0)
3177  break;
3178  }
3179  if (image->debug != MagickFalse)
3180  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3181  /*
3182  Relinquish resources.
3183  */
3184  token=DestroyString(token);
3185  if (primitive_info != (PrimitiveInfo *) NULL)
3186  primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3187  primitive=DestroyString(primitive);
3188  for ( ; n >= 0; n--)
3189  graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3190  graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3191  if (status == MagickFalse)
3192  ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3193  keyword);
3194  return(status != 0 ? MagickTrue : MagickFalse);
3195 }
3196 
3197 
3198 /*
3199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3200 % %
3201 % %
3202 % %
3203 % D r a w G r a d i e n t I m a g e %
3204 % %
3205 % %
3206 % %
3207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3208 %
3209 % DrawGradientImage() draws a linear gradient on the image.
3210 %
3211 % The format of the DrawGradientImage method is:
3212 %
3213 % MagickBooleanType DrawGradientImage(Image *image,
3214 % const DrawInfo *draw_info)
3215 %
3216 % A description of each parameter follows:
3217 %
3218 % o image: the image.
3219 %
3220 % o _info: the draw info.
3221 %
3222 */
3223 
3224 static inline double GetStopColorOffset(const GradientInfo *gradient,
3225  const ssize_t x,const ssize_t y)
3226 {
3227  switch (gradient->type)
3228  {
3229  case UndefinedGradient:
3230  case LinearGradient:
3231  {
3232  double
3233  gamma,
3234  length,
3235  offset,
3236  scale;
3237 
3238  PointInfo
3239  p,
3240  q;
3241 
3242  const SegmentInfo
3243  *gradient_vector;
3244 
3245  gradient_vector=(&gradient->gradient_vector);
3246  p.x=gradient_vector->x2-gradient_vector->x1;
3247  p.y=gradient_vector->y2-gradient_vector->y1;
3248  q.x=(double) x-gradient_vector->x1;
3249  q.y=(double) y-gradient_vector->y1;
3250  length=sqrt(q.x*q.x+q.y*q.y);
3251  gamma=sqrt(p.x*p.x+p.y*p.y)*length;
3252  gamma=PerceptibleReciprocal(gamma);
3253  scale=p.x*q.x+p.y*q.y;
3254  offset=gamma*scale*length;
3255  return(offset);
3256  }
3257  case RadialGradient:
3258  {
3259  double
3260  length,
3261  offset;
3262 
3263  PointInfo
3264  v;
3265 
3266  v.x=(double) x-gradient->center.x;
3267  v.y=(double) y-gradient->center.y;
3268  length=sqrt(v.x*v.x+v.y*v.y);
3269  if (gradient->spread == RepeatSpread)
3270  return(length);
3271  offset=length/gradient->radius;
3272  return(offset);
3273  }
3274  }
3275  return(0.0);
3276 }
3277 
3279  const DrawInfo *draw_info)
3280 {
3281  CacheView
3282  *image_view;
3283 
3284  const GradientInfo
3285  *gradient;
3286 
3287  const SegmentInfo
3288  *gradient_vector;
3289 
3290  double
3291  length;
3292 
3294  *exception;
3295 
3297  status;
3298 
3300  zero;
3301 
3302  PointInfo
3303  point;
3304 
3306  bounding_box;
3307 
3308  ssize_t
3309  y;
3310 
3311  /*
3312  Draw linear or radial gradient on image.
3313  */
3314  assert(image != (Image *) NULL);
3315  assert(image->signature == MagickSignature);
3316  if (image->debug != MagickFalse)
3317  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3318  assert(draw_info != (const DrawInfo *) NULL);
3319  gradient=(&draw_info->gradient);
3320  gradient_vector=(&gradient->gradient_vector);
3321  point.x=gradient_vector->x2-gradient_vector->x1;
3322  point.y=gradient_vector->y2-gradient_vector->y1;
3323  length=sqrt(point.x*point.x+point.y*point.y);
3324  bounding_box=gradient->bounding_box;
3325  status=MagickTrue;
3326  exception=(&image->exception);
3327  GetMagickPixelPacket(image,&zero);
3328  image_view=AcquireAuthenticCacheView(image,exception);
3329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3330  #pragma omp parallel for schedule(static,4) shared(status) \
3331  magick_threads(image,image,1,1)
3332 #endif
3333  for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
3334  {
3335  double
3336  alpha,
3337  offset;
3338 
3340  composite,
3341  pixel;
3342 
3343  register IndexPacket
3344  *restrict indexes;
3345 
3346  register ssize_t
3347  i,
3348  x;
3349 
3350  register PixelPacket
3351  *restrict q;
3352 
3353  ssize_t
3354  j;
3355 
3356  if (status == MagickFalse)
3357  continue;
3358  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3359  if (q == (PixelPacket *) NULL)
3360  {
3361  status=MagickFalse;
3362  continue;
3363  }
3364  indexes=GetCacheViewAuthenticIndexQueue(image_view);
3365  pixel=zero;
3366  composite=zero;
3367  offset=GetStopColorOffset(gradient,0,y);
3368  if (gradient->type != RadialGradient)
3369  offset/=length;
3370  for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
3371  {
3372  SetMagickPixelPacket(image,q,indexes+x,&pixel);
3373  switch (gradient->spread)
3374  {
3375  case UndefinedSpread:
3376  case PadSpread:
3377  {
3378  if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3379  (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
3380  {
3381  offset=GetStopColorOffset(gradient,x,y);
3382  if (gradient->type != RadialGradient)
3383  offset/=length;
3384  }
3385  for (i=0; i < (ssize_t) gradient->number_stops; i++)
3386  if (offset < gradient->stops[i].offset)
3387  break;
3388  if ((offset < 0.0) || (i == 0))
3389  composite=gradient->stops[0].color;
3390  else
3391  if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
3392  composite=gradient->stops[gradient->number_stops-1].color;
3393  else
3394  {
3395  j=i;
3396  i--;
3397  alpha=(offset-gradient->stops[i].offset)/
3398  (gradient->stops[j].offset-gradient->stops[i].offset);
3399  MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3400  &gradient->stops[j].color,alpha,&composite);
3401  }
3402  break;
3403  }
3404  case ReflectSpread:
3405  {
3406  if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3407  (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
3408  {
3409  offset=GetStopColorOffset(gradient,x,y);
3410  if (gradient->type != RadialGradient)
3411  offset/=length;
3412  }
3413  if (offset < 0.0)
3414  offset=(-offset);
3415  if ((ssize_t) fmod(offset,2.0) == 0)
3416  offset=fmod(offset,1.0);
3417  else
3418  offset=1.0-fmod(offset,1.0);
3419  for (i=0; i < (ssize_t) gradient->number_stops; i++)
3420  if (offset < gradient->stops[i].offset)
3421  break;
3422  if (i == 0)
3423  composite=gradient->stops[0].color;
3424  else
3425  if (i == (ssize_t) gradient->number_stops)
3426  composite=gradient->stops[gradient->number_stops-1].color;
3427  else
3428  {
3429  j=i;
3430  i--;
3431  alpha=(offset-gradient->stops[i].offset)/
3432  (gradient->stops[j].offset-gradient->stops[i].offset);
3433  MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3434  &gradient->stops[j].color,alpha,&composite);
3435  }
3436  break;
3437  }
3438  case RepeatSpread:
3439  {
3440  double
3441  repeat;
3442 
3444  antialias;
3445 
3446  antialias=MagickFalse;
3447  repeat=0.0;
3448  if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3449  (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
3450  {
3451  offset=GetStopColorOffset(gradient,x,y);
3452  if (gradient->type == LinearGradient)
3453  {
3454  repeat=fmod(offset,length);
3455  if (repeat < 0.0)
3456  repeat=length-fmod(-repeat,length);
3457  else
3458  repeat=fmod(offset,length);
3459  antialias=(repeat < length) && ((repeat+1.0) > length) ?
3461  offset=repeat/length;
3462  }
3463  else
3464  {
3465  repeat=fmod(offset,gradient->radius);
3466  if (repeat < 0.0)
3467  repeat=gradient->radius-fmod(-repeat,gradient->radius);
3468  else
3469  repeat=fmod(offset,gradient->radius);
3470  antialias=repeat+1.0 > gradient->radius ?
3472  offset=repeat/gradient->radius;
3473  }
3474  }
3475  for (i=0; i < (ssize_t) gradient->number_stops; i++)
3476  if (offset < gradient->stops[i].offset)
3477  break;
3478  if (i == 0)
3479  composite=gradient->stops[0].color;
3480  else
3481  if (i == (ssize_t) gradient->number_stops)
3482  composite=gradient->stops[gradient->number_stops-1].color;
3483  else
3484  {
3485  j=i;
3486  i--;
3487  alpha=(offset-gradient->stops[i].offset)/
3488  (gradient->stops[j].offset-gradient->stops[i].offset);
3489  if (antialias != MagickFalse)
3490  {
3491  if (gradient->type == LinearGradient)
3492  alpha=length-repeat;
3493  else
3494  alpha=gradient->radius-repeat;
3495  i=0;
3496  j=(ssize_t) gradient->number_stops-1L;
3497  }
3498  MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3499  &gradient->stops[j].color,alpha,&composite);
3500  }
3501  break;
3502  }
3503  }
3504  MagickPixelCompositeOver(&composite,composite.opacity,&pixel,
3505  pixel.opacity,&pixel);
3506  SetPixelPacket(image,&pixel,q,indexes+x);
3507  q++;
3508  }
3509  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3510  status=MagickFalse;
3511  }
3512  image_view=DestroyCacheView(image_view);
3513  return(status);
3514 }
3515 
3516 
3517 /*
3518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3519 % %
3520 % %
3521 % %
3522 % D r a w P a t t e r n P a t h %
3523 % %
3524 % %
3525 % %
3526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3527 %
3528 % DrawPatternPath() draws a pattern.
3529 %
3530 % The format of the DrawPatternPath method is:
3531 %
3532 % MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
3533 % const char *name,Image **pattern)
3534 %
3535 % A description of each parameter follows:
3536 %
3537 % o image: the image.
3538 %
3539 % o draw_info: the draw info.
3540 %
3541 % o name: the pattern name.
3542 %
3543 % o image: the image.
3544 %
3545 */
3547  const DrawInfo *draw_info,const char *name,Image **pattern)
3548 {
3549  char
3550  property[MaxTextExtent];
3551 
3552  const char
3553  *geometry,
3554  *path;
3555 
3556  DrawInfo
3557  *clone_info;
3558 
3559  ImageInfo
3560  *image_info;
3561 
3563  status;
3564 
3565  assert(image != (Image *) NULL);
3566  assert(image->signature == MagickSignature);
3567  if (image->debug != MagickFalse)
3568  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3569  assert(draw_info != (const DrawInfo *) NULL);
3570  assert(name != (const char *) NULL);
3571  (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
3572  path=GetImageArtifact(image,property);
3573  if (path == (const char *) NULL)
3574  return(MagickFalse);
3575  (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
3576  geometry=GetImageArtifact(image,property);
3577  if (geometry == (const char *) NULL)
3578  return(MagickFalse);
3579  if ((*pattern) != (Image *) NULL)
3580  *pattern=DestroyImage(*pattern);
3581  image_info=AcquireImageInfo();
3582  image_info->size=AcquireString(geometry);
3583  *pattern=AcquireImage(image_info);
3584  image_info=DestroyImageInfo(image_info);
3585  (void) QueryColorDatabase("#00000000",&(*pattern)->background_color,
3586  &image->exception);
3587  (void) SetImageBackgroundColor(*pattern);
3588  if (image->debug != MagickFalse)
3590  "begin pattern-path %s %s",name,geometry);
3591  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3592  clone_info->fill_pattern=NewImageList();
3593  clone_info->stroke_pattern=NewImageList();
3594  (void) CloneString(&clone_info->primitive,path);
3595  status=DrawImage(*pattern,clone_info);
3596  clone_info=DestroyDrawInfo(clone_info);
3597  if (image->debug != MagickFalse)
3598  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3599  return(status);
3600 }
3601 
3602 
3603 /*
3604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3605 % %
3606 % %
3607 % %
3608 + D r a w P o l y g o n P r i m i t i v e %
3609 % %
3610 % %
3611 % %
3612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3613 %
3614 % DrawPolygonPrimitive() draws a polygon on the image.
3615 %
3616 % The format of the DrawPolygonPrimitive method is:
3617 %
3618 % MagickBooleanType DrawPolygonPrimitive(Image *image,
3619 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3620 %
3621 % A description of each parameter follows:
3622 %
3623 % o image: the image.
3624 %
3625 % o draw_info: the draw info.
3626 %
3627 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3628 %
3629 */
3630 
3632 {
3633  register ssize_t
3634  i;
3635 
3636  assert(polygon_info != (PolygonInfo **) NULL);
3637  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3638  if (polygon_info[i] != (PolygonInfo *) NULL)
3639  polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
3640  polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
3641  return(polygon_info);
3642 }
3643 
3645  const PrimitiveInfo *primitive_info)
3646 {
3647  PathInfo
3648  *restrict path_info;
3649 
3650  PolygonInfo
3651  **polygon_info;
3652 
3653  register ssize_t
3654  i;
3655 
3656  size_t
3657  number_threads;
3658 
3659  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3660  polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
3661  sizeof(*polygon_info));
3662  if (polygon_info == (PolygonInfo **) NULL)
3663  return((PolygonInfo **) NULL);
3664  (void) ResetMagickMemory(polygon_info,0,(size_t)
3665  GetMagickResourceLimit(ThreadResource)*sizeof(*polygon_info));
3666  path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
3667  if (path_info == (PathInfo *) NULL)
3668  return(DestroyPolygonThreadSet(polygon_info));
3669  for (i=0; i < (ssize_t) number_threads; i++)
3670  {
3671  polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
3672  if (polygon_info[i] == (PolygonInfo *) NULL)
3673  return(DestroyPolygonThreadSet(polygon_info));
3674  }
3675  path_info=(PathInfo *) RelinquishMagickMemory(path_info);
3676  return(polygon_info);
3677 }
3678 
3679 static double GetOpacityPixel(PolygonInfo *polygon_info,const double mid,
3680  const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
3681  const ssize_t y,double *stroke_opacity)
3682 {
3683  double
3684  alpha,
3685  beta,
3686  distance,
3687  subpath_opacity;
3688 
3689  PointInfo
3690  delta;
3691 
3692  register EdgeInfo
3693  *p;
3694 
3695  register const PointInfo
3696  *q;
3697 
3698  register ssize_t
3699  i;
3700 
3701  ssize_t
3702  j,
3703  winding_number;
3704 
3705  /*
3706  Compute fill & stroke opacity for this (x,y) point.
3707  */
3708  *stroke_opacity=0.0;
3709  subpath_opacity=0.0;
3710  p=polygon_info->edges;
3711  for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
3712  {
3713  if ((double) y <= (p->bounds.y1-mid-0.5))
3714  break;
3715  if ((double) y > (p->bounds.y2+mid+0.5))
3716  {
3717  (void) DestroyEdge(polygon_info,(size_t) j);
3718  continue;
3719  }
3720  if (((double) x <= (p->bounds.x1-mid-0.5)) ||
3721  ((double) x > (p->bounds.x2+mid+0.5)))
3722  continue;
3723  i=(ssize_t) MagickMax((double) p->highwater,1.0);
3724  for ( ; i < (ssize_t) p->number_points; i++)
3725  {
3726  if ((double) y <= (p->points[i-1].y-mid-0.5))
3727  break;
3728  if ((double) y > (p->points[i].y+mid+0.5))
3729  continue;
3730  if (p->scanline != (double) y)
3731  {
3732  p->scanline=(double) y;
3733  p->highwater=(size_t) i;
3734  }
3735  /*
3736  Compute distance between a point and an edge.
3737  */
3738  q=p->points+i-1;
3739  delta.x=(q+1)->x-q->x;
3740  delta.y=(q+1)->y-q->y;
3741  beta=delta.x*(x-q->x)+delta.y*(y-q->y);
3742  if (beta < 0.0)
3743  {
3744  delta.x=(double) x-q->x;
3745  delta.y=(double) y-q->y;
3746  distance=delta.x*delta.x+delta.y*delta.y;
3747  }
3748  else
3749  {
3750  alpha=delta.x*delta.x+delta.y*delta.y;
3751  if (beta > alpha)
3752  {
3753  delta.x=(double) x-(q+1)->x;
3754  delta.y=(double) y-(q+1)->y;
3755  distance=delta.x*delta.x+delta.y*delta.y;
3756  }
3757  else
3758  {
3759  alpha=1.0/alpha;
3760  beta=delta.x*(y-q->y)-delta.y*(x-q->x);
3761  distance=alpha*beta*beta;
3762  }
3763  }
3764  /*
3765  Compute stroke & subpath opacity.
3766  */
3767  beta=0.0;
3768  if (p->ghostline == MagickFalse)
3769  {
3770  alpha=mid+0.5;
3771  if ((*stroke_opacity < 1.0) &&
3772  (distance <= ((alpha+0.25)*(alpha+0.25))))
3773  {
3774  alpha=mid-0.5;
3775  if (distance <= ((alpha+0.25)*(alpha+0.25)))
3776  *stroke_opacity=1.0;
3777  else
3778  {
3779  beta=1.0;
3780  if (distance != 1.0)
3781  beta=sqrt((double) distance);
3782  alpha=beta-mid-0.5;
3783  if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
3784  *stroke_opacity=(alpha-0.25)*(alpha-0.25);
3785  }
3786  }
3787  }
3788  if ((fill == MagickFalse) || (distance > 1.0) || (subpath_opacity >= 1.0))
3789  continue;
3790  if (distance <= 0.0)
3791  {
3792  subpath_opacity=1.0;
3793  continue;
3794  }
3795  if (distance > 1.0)
3796  continue;
3797  if (beta == 0.0)
3798  {
3799  beta=1.0;
3800  if (distance != 1.0)
3801  beta=sqrt(distance);
3802  }
3803  alpha=beta-1.0;
3804  if (subpath_opacity < (alpha*alpha))
3805  subpath_opacity=alpha*alpha;
3806  }
3807  }
3808  /*
3809  Compute fill opacity.
3810  */
3811  if (fill == MagickFalse)
3812  return(0.0);
3813  if (subpath_opacity >= 1.0)
3814  return(1.0);
3815  /*
3816  Determine winding number.
3817  */
3818  winding_number=0;
3819  p=polygon_info->edges;
3820  for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
3821  {
3822  if ((double) y <= p->bounds.y1)
3823  break;
3824  if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
3825  continue;
3826  if ((double) x > p->bounds.x2)
3827  {
3828  winding_number+=p->direction ? 1 : -1;
3829  continue;
3830  }
3831  i=(ssize_t) MagickMax((double) p->highwater,1.0);
3832  for ( ; i < (ssize_t) p->number_points; i++)
3833  if ((double) y <= p->points[i].y)
3834  break;
3835  q=p->points+i-1;
3836  if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
3837  winding_number+=p->direction ? 1 : -1;
3838  }
3839  if (fill_rule != NonZeroRule)
3840  {
3841  if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
3842  return(1.0);
3843  }
3844  else
3845  if (MagickAbsoluteValue(winding_number) != 0)
3846  return(1.0);
3847  return(subpath_opacity);
3848 }
3849 
3851  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3852 {
3853  CacheView
3854  *image_view;
3855 
3856  double
3857  mid;
3858 
3860  *exception;
3861 
3863  fill,
3864  status;
3865 
3866  PolygonInfo
3867  **restrict polygon_info;
3868 
3869  register EdgeInfo
3870  *p;
3871 
3872  register ssize_t
3873  i;
3874 
3875  SegmentInfo
3876  bounds;
3877 
3878  ssize_t
3879  start,
3880  stop,
3881  y;
3882 
3883  /*
3884  Compute bounding box.
3885  */
3886  assert(image != (Image *) NULL);
3887  assert(image->signature == MagickSignature);
3888  if (image->debug != MagickFalse)
3889  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3890  assert(draw_info != (DrawInfo *) NULL);
3891  assert(draw_info->signature == MagickSignature);
3892  assert(primitive_info != (PrimitiveInfo *) NULL);
3893  if (primitive_info->coordinates == 0)
3894  return(MagickTrue);
3895  polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3896  if (polygon_info == (PolygonInfo **) NULL)
3897  return(MagickFalse);
3898 DisableMSCWarning(4127)
3899  if (0)
3900  DrawBoundingRectangles(image,draw_info,polygon_info[0]);
3902  if (image->debug != MagickFalse)
3903  (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3904  fill=(primitive_info->method == FillToBorderMethod) ||
3905  (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3906  mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3907  bounds=polygon_info[0]->edges[0].bounds;
3908  for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
3909  {
3910  p=polygon_info[0]->edges+i;
3911  if (p->bounds.x1 < bounds.x1)
3912  bounds.x1=p->bounds.x1;
3913  if (p->bounds.y1 < bounds.y1)
3914  bounds.y1=p->bounds.y1;
3915  if (p->bounds.x2 > bounds.x2)
3916  bounds.x2=p->bounds.x2;
3917  if (p->bounds.y2 > bounds.y2)
3918  bounds.y2=p->bounds.y2;
3919  }
3920  bounds.x1-=(mid+1.0);
3921  bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
3922  image->columns ? (double) image->columns-1 : bounds.x1;
3923  bounds.y1-=(mid+1.0);
3924  bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
3925  image->rows ? (double) image->rows-1 : bounds.y1;
3926  bounds.x2+=(mid+1.0);
3927  bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
3928  image->columns ? (double) image->columns-1 : bounds.x2;
3929  bounds.y2+=(mid+1.0);
3930  bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
3931  image->rows ? (double) image->rows-1 : bounds.y2;
3932  status=MagickTrue;
3933  exception=(&image->exception);
3934  image_view=AcquireAuthenticCacheView(image,exception);
3935  if (primitive_info->coordinates == 1)
3936  {
3937  /*
3938  Draw point.
3939  */
3940  start=(ssize_t) ceil(bounds.y1-0.5);
3941  stop=(ssize_t) floor(bounds.y2+0.5);
3942 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3943  #pragma omp parallel for schedule(static,4) shared(status) \
3944  magick_threads(image,image,1,1)
3945 #endif
3946  for (y=start; y <= stop; y++)
3947  {
3949  sync;
3950 
3951  register PixelPacket
3952  *restrict q;
3953 
3954  register ssize_t
3955  x;
3956 
3957  ssize_t
3958  start,
3959  stop;
3960 
3961  if (status == MagickFalse)
3962  continue;
3963  start=(ssize_t) ceil(bounds.x1-0.5);
3964  stop=(ssize_t) floor(bounds.x2+0.5);
3965  x=start;
3966  q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),1,
3967  exception);
3968  if (q == (PixelPacket *) NULL)
3969  {
3970  status=MagickFalse;
3971  continue;
3972  }
3973  for ( ; x <= stop; x++)
3974  {
3975  if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3976  (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
3977  (void) GetStrokeColor(draw_info,x,y,q);
3978  q++;
3979  }
3980  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3981  if (sync == MagickFalse)
3982  status=MagickFalse;
3983  }
3984  image_view=DestroyCacheView(image_view);
3985  polygon_info=DestroyPolygonThreadSet(polygon_info);
3986  if (image->debug != MagickFalse)
3988  " end draw-polygon");
3989  return(status);
3990  }
3991  /*
3992  Draw polygon or line.
3993  */
3994  if (image->matte == MagickFalse)
3996  start=(ssize_t) ceil(bounds.y1-0.5);
3997  stop=(ssize_t) floor(bounds.y2+0.5);
3998 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3999  #pragma omp parallel for schedule(static,4) shared(status) \
4000  magick_threads(image,image,1,1)
4001 #endif
4002  for (y=start; y <= stop; y++)
4003  {
4004  const int
4005  id = GetOpenMPThreadId();
4006 
4007  double
4008  fill_opacity,
4009  stroke_opacity;
4010 
4011  PixelPacket
4012  fill_color,
4013  stroke_color;
4014 
4015  register PixelPacket
4016  *restrict q;
4017 
4018  register ssize_t
4019  x;
4020 
4021  ssize_t
4022  start,
4023  stop;
4024 
4025  if (status == MagickFalse)
4026  continue;
4027  start=(ssize_t) ceil(bounds.x1-0.5);
4028  stop=(ssize_t) floor(bounds.x2+0.5);
4029  q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-start+1),1,
4030  exception);
4031  if (q == (PixelPacket *) NULL)
4032  {
4033  status=MagickFalse;
4034  continue;
4035  }
4036  for (x=start; x <= stop; x++)
4037  {
4038  /*
4039  Fill and/or stroke.
4040  */
4041  fill_opacity=GetOpacityPixel(polygon_info[id],mid,fill,
4042  draw_info->fill_rule,x,y,&stroke_opacity);
4043  if (draw_info->stroke_antialias == MagickFalse)
4044  {
4045  fill_opacity=fill_opacity > 0.25 ? 1.0 : 0.0;
4046  stroke_opacity=stroke_opacity > 0.25 ? 1.0 : 0.0;
4047  }
4048  (void) GetFillColor(draw_info,x,y,&fill_color);
4049  fill_opacity=(double) (QuantumRange-fill_opacity*(QuantumRange-
4050  fill_color.opacity));
4051  MagickCompositeOver(&fill_color,(MagickRealType) fill_opacity,q,
4052  (MagickRealType) q->opacity,q);
4053  (void) GetStrokeColor(draw_info,x,y,&stroke_color);
4054  stroke_opacity=(double) (QuantumRange-stroke_opacity*(QuantumRange-
4055  stroke_color.opacity));
4056  MagickCompositeOver(&stroke_color,(MagickRealType) stroke_opacity,q,
4057  (MagickRealType) q->opacity,q);
4058  q++;
4059  }
4060  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4061  status=MagickFalse;
4062  }
4063  image_view=DestroyCacheView(image_view);
4064  polygon_info=DestroyPolygonThreadSet(polygon_info);
4065  if (image->debug != MagickFalse)
4066  (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
4067  return(status);
4068 }
4069 
4070 
4071 /*
4072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4073 % %
4074 % %
4075 % %
4076 % D r a w P r i m i t i v e %
4077 % %
4078 % %
4079 % %
4080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4081 %
4082 % DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
4083 %
4084 % The format of the DrawPrimitive method is:
4085 %
4086 % MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
4087 % PrimitiveInfo *primitive_info)
4088 %
4089 % A description of each parameter follows:
4090 %
4091 % o image: the image.
4092 %
4093 % o draw_info: the draw info.
4094 %
4095 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4096 %
4097 */
4098 
4099 static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4100 {
4101  const char
4102  *methods[] =
4103  {
4104  "point",
4105  "replace",
4106  "floodfill",
4107  "filltoborder",
4108  "reset",
4109  "?"
4110  };
4111 
4112  PointInfo
4113  p,
4114  q,
4115  point;
4116 
4117  register ssize_t
4118  i,
4119  x;
4120 
4121  ssize_t
4122  coordinates,
4123  y;
4124 
4125  x=(ssize_t) ceil(primitive_info->point.x-0.5);
4126  y=(ssize_t) ceil(primitive_info->point.y-0.5);
4127  switch (primitive_info->primitive)
4128  {
4129  case PointPrimitive:
4130  {
4132  "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
4133  methods[primitive_info->method]);
4134  return;
4135  }
4136  case ColorPrimitive:
4137  {
4139  "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
4140  methods[primitive_info->method]);
4141  return;
4142  }
4143  case MattePrimitive:
4144  {
4146  "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
4147  methods[primitive_info->method]);
4148  return;
4149  }
4150  case TextPrimitive:
4151  {
4153  "TextPrimitive %.20g,%.20g",(double) x,(double) y);
4154  return;
4155  }
4156  case ImagePrimitive:
4157  {
4159  "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
4160  return;
4161  }
4162  default:
4163  break;
4164  }
4165  coordinates=0;
4166  p=primitive_info[0].point;
4167  q.x=(-1.0);
4168  q.y=(-1.0);
4169  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4170  {
4171  point=primitive_info[i].point;
4172  if (coordinates <= 0)
4173  {
4174  coordinates=(ssize_t) primitive_info[i].coordinates;
4176  " begin open (%.20g)",(double) coordinates);
4177  p=point;
4178  }
4179  point=primitive_info[i].point;
4180  if ((fabs(q.x-point.x) >= MagickEpsilon) ||
4181  (fabs(q.y-point.y) >= MagickEpsilon))
4183  " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
4184  else
4186  " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
4187  q=point;
4188  coordinates--;
4189  if (coordinates > 0)
4190  continue;
4191  if ((fabs(p.x-point.x) >= MagickEpsilon) ||
4192  (fabs(p.y-point.y) >= MagickEpsilon))
4193  (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4194  (double) coordinates);
4195  else
4196  (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4197  (double) coordinates);
4198  }
4199 }
4200 
4202  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4203 {
4204  CacheView
4205  *image_view;
4206 
4208  *exception;
4209 
4211  status;
4212 
4213  register ssize_t
4214  i,
4215  x;
4216 
4217  ssize_t
4218  y;
4219 
4220  if (image->debug != MagickFalse)
4221  {
4223  " begin draw-primitive");
4225  " affine: %g %g %g %g %g %g",draw_info->affine.sx,
4226  draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4227  draw_info->affine.tx,draw_info->affine.ty);
4228  }
4229  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
4230  ((IsPixelGray(&draw_info->fill) == MagickFalse) ||
4231  (IsPixelGray(&draw_info->stroke) == MagickFalse)))
4232  (void) SetImageColorspace(image,sRGBColorspace);
4233  status=MagickTrue;
4234  exception=(&image->exception);
4235  x=(ssize_t) ceil(primitive_info->point.x-0.5);
4236  y=(ssize_t) ceil(primitive_info->point.y-0.5);
4237  image_view=AcquireAuthenticCacheView(image,exception);
4238  switch (primitive_info->primitive)
4239  {
4240  case PointPrimitive:
4241  {
4242  PixelPacket
4243  fill_color;
4244 
4245  PixelPacket
4246  *q;
4247 
4248  if ((y < 0) || (y >= (ssize_t) image->rows))
4249  break;
4250  if ((x < 0) || (x >= (ssize_t) image->columns))
4251  break;
4252  q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4253  if (q == (PixelPacket *) NULL)
4254  break;
4255  (void) GetFillColor(draw_info,x,y,&fill_color);
4256  MagickCompositeOver(&fill_color,(MagickRealType) fill_color.opacity,q,
4257  (MagickRealType) q->opacity,q);
4258  status&=SyncCacheViewAuthenticPixels(image_view,exception);
4259  break;
4260  }
4261  case ColorPrimitive:
4262  {
4263  switch (primitive_info->method)
4264  {
4265  case PointMethod:
4266  default:
4267  {
4268  PixelPacket
4269  *q;
4270 
4271  q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4272  if (q == (PixelPacket *) NULL)
4273  break;
4274  (void) GetFillColor(draw_info,x,y,q);
4275  status&=SyncCacheViewAuthenticPixels(image_view,exception);
4276  break;
4277  }
4278  case ReplaceMethod:
4279  {
4281  sync;
4282 
4283  PixelPacket
4284  target;
4285 
4286  status&=GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
4287  for (y=0; y < (ssize_t) image->rows; y++)
4288  {
4289  register PixelPacket
4290  *restrict q;
4291 
4292  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4293  exception);
4294  if (q == (PixelPacket *) NULL)
4295  break;
4296  for (x=0; x < (ssize_t) image->columns; x++)
4297  {
4298  if (IsColorSimilar(image,q,&target) == MagickFalse)
4299  {
4300  q++;
4301  continue;
4302  }
4303  (void) GetFillColor(draw_info,x,y,q);
4304  q++;
4305  }
4306  sync=SyncCacheViewAuthenticPixels(image_view,exception);
4307  if (sync == MagickFalse)
4308  break;
4309  }
4310  break;
4311  }
4312  case FloodfillMethod:
4313  case FillToBorderMethod:
4314  {
4316  target;
4317 
4318  (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4319  if (primitive_info->method == FillToBorderMethod)
4320  {
4321  target.red=(MagickRealType) draw_info->border_color.red;
4322  target.green=(MagickRealType) draw_info->border_color.green;
4323  target.blue=(MagickRealType) draw_info->border_color.blue;
4324  }
4325  status&=FloodfillPaintImage(image,DefaultChannels,draw_info,&target,x,
4326  y,primitive_info->method == FloodfillMethod ? MagickFalse :
4327  MagickTrue);
4328  break;
4329  }
4330  case ResetMethod:
4331  {
4333  sync;
4334 
4335  for (y=0; y < (ssize_t) image->rows; y++)
4336  {
4337  register PixelPacket
4338  *restrict q;
4339 
4340  register ssize_t
4341  x;
4342 
4343  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4344  exception);
4345  if (q == (PixelPacket *) NULL)
4346  break;
4347  for (x=0; x < (ssize_t) image->columns; x++)
4348  {
4349  (void) GetFillColor(draw_info,x,y,q);
4350  q++;
4351  }
4352  sync=SyncCacheViewAuthenticPixels(image_view,exception);
4353  if (sync == MagickFalse)
4354  break;
4355  }
4356  break;
4357  }
4358  }
4359  break;
4360  }
4361  case MattePrimitive:
4362  {
4363  if (image->matte == MagickFalse)
4365  switch (primitive_info->method)
4366  {
4367  case PointMethod:
4368  default:
4369  {
4370  PixelPacket
4371  pixel;
4372 
4373  PixelPacket
4374  *q;
4375 
4376  q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4377  if (q == (PixelPacket *) NULL)
4378  break;
4379  (void) GetFillColor(draw_info,x,y,&pixel);
4380  SetPixelOpacity(q,pixel.opacity);
4381  status&=SyncCacheViewAuthenticPixels(image_view,exception);
4382  break;
4383  }
4384  case ReplaceMethod:
4385  {
4387  sync;
4388 
4389  PixelPacket
4390  pixel,
4391  target;
4392 
4393  status&=GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
4394  for (y=0; y < (ssize_t) image->rows; y++)
4395  {
4396  register PixelPacket
4397  *restrict q;
4398 
4399  register ssize_t
4400  x;
4401 
4402  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4403  exception);
4404  if (q == (PixelPacket *) NULL)
4405  break;
4406  for (x=0; x < (ssize_t) image->columns; x++)
4407  {
4408  if (IsColorSimilar(image,q,&target) == MagickFalse)
4409  {
4410  q++;
4411  continue;
4412  }
4413  (void) GetFillColor(draw_info,x,y,&pixel);
4414  SetPixelOpacity(q,pixel.opacity);
4415  q++;
4416  }
4417  sync=SyncCacheViewAuthenticPixels(image_view,exception);
4418  if (sync == MagickFalse)
4419  break;
4420  }
4421  break;
4422  }
4423  case FloodfillMethod:
4424  case FillToBorderMethod:
4425  {
4427  target;
4428 
4429  (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4430  if (primitive_info->method == FillToBorderMethod)
4431  {
4432  target.red=(MagickRealType) draw_info->border_color.red;
4433  target.green=(MagickRealType) draw_info->border_color.green;
4434  target.blue=(MagickRealType) draw_info->border_color.blue;
4435  }
4436  status&=FloodfillPaintImage(image,OpacityChannel,draw_info,&target,x,
4437  y,primitive_info->method == FloodfillMethod ? MagickFalse :
4438  MagickTrue);
4439  break;
4440  }
4441  case ResetMethod:
4442  {
4444  sync;
4445 
4446  PixelPacket
4447  pixel;
4448 
4449  for (y=0; y < (ssize_t) image->rows; y++)
4450  {
4451  register PixelPacket
4452  *restrict q;
4453 
4454  register ssize_t
4455  x;
4456 
4457  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4458  exception);
4459  if (q == (PixelPacket *) NULL)
4460  break;
4461  for (x=0; x < (ssize_t) image->columns; x++)
4462  {
4463  (void) GetFillColor(draw_info,x,y,&pixel);
4464  SetPixelOpacity(q,pixel.opacity);
4465  q++;
4466  }
4467  sync=SyncCacheViewAuthenticPixels(image_view,exception);
4468  if (sync == MagickFalse)
4469  break;
4470  }
4471  break;
4472  }
4473  }
4474  break;
4475  }
4476  case TextPrimitive:
4477  {
4478  char
4479  geometry[MaxTextExtent];
4480 
4481  DrawInfo
4482  *clone_info;
4483 
4484  if (primitive_info->text == (char *) NULL)
4485  break;
4486  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4487  (void) CloneString(&clone_info->text,primitive_info->text);
4488  (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
4489  primitive_info->point.x,primitive_info->point.y);
4490  (void) CloneString(&clone_info->geometry,geometry);
4491  status&=AnnotateImage(image,clone_info);
4492  clone_info=DestroyDrawInfo(clone_info);
4493  break;
4494  }
4495  case ImagePrimitive:
4496  {
4497  AffineMatrix
4498  affine;
4499 
4500  char
4501  composite_geometry[MaxTextExtent];
4502 
4503  Image
4504  *composite_image;
4505 
4506  ImageInfo
4507  *clone_info;
4508 
4510  geometry;
4511 
4512  ssize_t
4513  x1,
4514  y1;
4515 
4516  if (primitive_info->text == (char *) NULL)
4517  break;
4518  clone_info=AcquireImageInfo();
4519  if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4520  composite_image=ReadInlineImage(clone_info,primitive_info->text,
4521  &image->exception);
4522  else
4523  {
4524  (void) CopyMagickString(clone_info->filename,primitive_info->text,
4525  MaxTextExtent);
4526  composite_image=ReadImage(clone_info,&image->exception);
4527  }
4528  clone_info=DestroyImageInfo(clone_info);
4529  if (composite_image == (Image *) NULL)
4530  break;
4531  (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4532  NULL,(void *) NULL);
4533  x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4534  y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4535  if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4536  ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
4537  {
4538  char
4539  geometry[MaxTextExtent];
4540 
4541  /*
4542  Resize image.
4543  */
4544  (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
4545  primitive_info[1].point.x,primitive_info[1].point.y);
4546  composite_image->filter=image->filter;
4547  (void) TransformImage(&composite_image,(char *) NULL,geometry);
4548  }
4549  if (composite_image->matte == MagickFalse)
4550  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4551  if (draw_info->opacity != OpaqueOpacity)
4552  (void) SetImageOpacity(composite_image,draw_info->opacity);
4553  SetGeometry(image,&geometry);
4554  image->gravity=draw_info->gravity;
4555  geometry.x=x;
4556  geometry.y=y;
4557  (void) FormatLocaleString(composite_geometry,MaxTextExtent,
4558  "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
4559  composite_image->rows,(double) geometry.x,(double) geometry.y);
4560  (void) ParseGravityGeometry(image,composite_geometry,&geometry,
4561  &image->exception);
4562  affine=draw_info->affine;
4563  affine.tx=(double) geometry.x;
4564  affine.ty=(double) geometry.y;
4565  composite_image->interpolate=image->interpolate;
4566  if (draw_info->compose == OverCompositeOp)
4567  (void) DrawAffineImage(image,composite_image,&affine);
4568  else
4569  (void) CompositeImage(image,draw_info->compose,composite_image,
4570  geometry.x,geometry.y);
4571  composite_image=DestroyImage(composite_image);
4572  break;
4573  }
4574  default:
4575  {
4576  double
4577  mid,
4578  scale;
4579 
4580  DrawInfo
4581  *clone_info;
4582 
4583  if (IsEventLogging() != MagickFalse)
4584  LogPrimitiveInfo(primitive_info);
4585  scale=ExpandAffine(&draw_info->affine);
4586  if ((draw_info->dash_pattern != (double *) NULL) &&
4587  (draw_info->dash_pattern[0] != 0.0) &&
4588  ((scale*draw_info->stroke_width) >= MagickEpsilon) &&
4589  (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
4590  {
4591  /*
4592  Draw dash polygon.
4593  */
4594  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4595  clone_info->stroke_width=0.0;
4596  clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4597  status&=DrawPolygonPrimitive(image,clone_info,primitive_info);
4598  clone_info=DestroyDrawInfo(clone_info);
4599  (void) DrawDashPolygon(draw_info,primitive_info,image);
4600  break;
4601  }
4602  mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4603  if ((mid > 1.0) &&
4604  (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
4605  {
4607  closed_path;
4608 
4609  /*
4610  Draw strokes while respecting line cap/join attributes.
4611  */
4612  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4613  closed_path=
4614  (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4615  (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4617  i=(ssize_t) primitive_info[0].coordinates;
4618  if ((((draw_info->linecap == RoundCap) ||
4619  (closed_path != MagickFalse)) &&
4620  (draw_info->linejoin == RoundJoin)) ||
4621  (primitive_info[i].primitive != UndefinedPrimitive))
4622  {
4623  (void) DrawPolygonPrimitive(image,draw_info,primitive_info);
4624  break;
4625  }
4626  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4627  clone_info->stroke_width=0.0;
4628  clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4629  status&=DrawPolygonPrimitive(image,clone_info,primitive_info);
4630  clone_info=DestroyDrawInfo(clone_info);
4631  status&=DrawStrokePolygon(image,draw_info,primitive_info);
4632  break;
4633  }
4634  status&=DrawPolygonPrimitive(image,draw_info,primitive_info);
4635  break;
4636  }
4637  }
4638  image_view=DestroyCacheView(image_view);
4639  if (image->debug != MagickFalse)
4640  (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4641  return(status != 0 ? MagickTrue : MagickFalse);
4642 }
4643 
4644 
4645 /*
4646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4647 % %
4648 % %
4649 % %
4650 + D r a w S t r o k e P o l y g o n %
4651 % %
4652 % %
4653 % %
4654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4655 %
4656 % DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4657 % the image while respecting the line cap and join attributes.
4658 %
4659 % The format of the DrawStrokePolygon method is:
4660 %
4661 % MagickBooleanType DrawStrokePolygon(Image *image,
4662 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4663 %
4664 % A description of each parameter follows:
4665 %
4666 % o image: the image.
4667 %
4668 % o draw_info: the draw info.
4669 %
4670 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4671 %
4672 %
4673 */
4674 
4675 static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
4676  const PrimitiveInfo *primitive_info)
4677 {
4679  linecap[5];
4680 
4681  register ssize_t
4682  i;
4683 
4684  for (i=0; i < 4; i++)
4685  linecap[i]=(*primitive_info);
4686  linecap[0].coordinates=4;
4687  linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4688  linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4689  linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4690  linecap[3].point.y+=(double) (10.0*MagickEpsilon);
4691  linecap[4].primitive=UndefinedPrimitive;
4692  (void) DrawPolygonPrimitive(image,draw_info,linecap);
4693 }
4694 
4696  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4697 {
4698  DrawInfo
4699  *clone_info;
4700 
4702  closed_path;
4703 
4705  status;
4706 
4708  *stroke_polygon;
4709 
4710  register const PrimitiveInfo
4711  *p,
4712  *q;
4713 
4714  /*
4715  Draw stroked polygon.
4716  */
4717  if (image->debug != MagickFalse)
4719  " begin draw-stroke-polygon");
4720  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4721  clone_info->fill=draw_info->stroke;
4722  if (clone_info->fill_pattern != (Image *) NULL)
4723  clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
4724  if (clone_info->stroke_pattern != (Image *) NULL)
4725  clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
4726  MagickTrue,&clone_info->stroke_pattern->exception);
4727  clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4728  clone_info->stroke_width=0.0;
4729  clone_info->fill_rule=NonZeroRule;
4730  status=MagickTrue;
4731  for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4732  {
4733  stroke_polygon=TraceStrokePolygon(draw_info,p);
4734  status&=DrawPolygonPrimitive(image,clone_info,stroke_polygon);
4735  if (status == 0)
4736  break;
4737  stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4738  q=p+p->coordinates-1;
4739  closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4741  if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4742  {
4743  DrawRoundLinecap(image,draw_info,p);
4744  DrawRoundLinecap(image,draw_info,q);
4745  }
4746  }
4747  clone_info=DestroyDrawInfo(clone_info);
4748  if (image->debug != MagickFalse)
4750  " end draw-stroke-polygon");
4751  return(status != 0 ? MagickTrue : MagickFalse);
4752 }
4753 
4754 
4755 /*
4756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4757 % %
4758 % %
4759 % %
4760 % G e t A f f i n e M a t r i x %
4761 % %
4762 % %
4763 % %
4764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4765 %
4766 % GetAffineMatrix() returns an AffineMatrix initialized to the identity
4767 % matrix.
4768 %
4769 % The format of the GetAffineMatrix method is:
4770 %
4771 % void GetAffineMatrix(AffineMatrix *affine_matrix)
4772 %
4773 % A description of each parameter follows:
4774 %
4775 % o affine_matrix: the affine matrix.
4776 %
4777 */
4779 {
4780  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4781  assert(affine_matrix != (AffineMatrix *) NULL);
4782  (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4783  affine_matrix->sx=1.0;
4784  affine_matrix->sy=1.0;
4785 }
4786 
4787 
4788 /*
4789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4790 % %
4791 % %
4792 % %
4793 + G e t D r a w I n f o %
4794 % %
4795 % %
4796 % %
4797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4798 %
4799 % GetDrawInfo() initializes draw_info to default values from image_info.
4800 %
4801 % The format of the GetDrawInfo method is:
4802 %
4803 % void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4804 %
4805 % A description of each parameter follows:
4806 %
4807 % o image_info: the image info..
4808 %
4809 % o draw_info: the draw info.
4810 %
4811 */
4812 MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4813 {
4814  const char
4815  *option;
4816 
4818  *exception;
4819 
4820  ImageInfo
4821  *clone_info;
4822 
4823  /*
4824  Initialize draw attributes.
4825  */
4826  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4827  assert(draw_info != (DrawInfo *) NULL);
4828  (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
4829  clone_info=CloneImageInfo(image_info);
4830  GetAffineMatrix(&draw_info->affine);
4831  exception=AcquireExceptionInfo();
4832  (void) QueryColorDatabase("#000F",&draw_info->fill,exception);
4833  (void) QueryColorDatabase("#FFF0",&draw_info->stroke,exception);
4834  draw_info->stroke_antialias=clone_info->antialias;
4835  draw_info->stroke_width=1.0;
4836  draw_info->opacity=OpaqueOpacity;
4837  draw_info->fill_rule=EvenOddRule;
4838  draw_info->linecap=ButtCap;
4839  draw_info->linejoin=MiterJoin;
4840  draw_info->miterlimit=10;
4841  draw_info->decorate=NoDecoration;
4842  if (clone_info->font != (char *) NULL)
4843  draw_info->font=AcquireString(clone_info->font);
4844  if (clone_info->density != (char *) NULL)
4845  draw_info->density=AcquireString(clone_info->density);
4846  draw_info->text_antialias=clone_info->antialias;
4847  draw_info->pointsize=12.0;
4848  if (clone_info->pointsize != 0.0)
4849  draw_info->pointsize=clone_info->pointsize;
4851  draw_info->border_color=clone_info->border_color;
4852  draw_info->compose=OverCompositeOp;
4853  if (clone_info->server_name != (char *) NULL)
4854  draw_info->server_name=AcquireString(clone_info->server_name);
4855  draw_info->render=MagickTrue;
4856  draw_info->debug=IsEventLogging();
4857  option=GetImageOption(clone_info,"encoding");
4858  if (option != (const char *) NULL)
4859  (void) CloneString(&draw_info->encoding,option);
4860  option=GetImageOption(clone_info,"kerning");
4861  if (option != (const char *) NULL)
4862  draw_info->kerning=StringToDouble(option,(char **) NULL);
4863  option=GetImageOption(clone_info,"interline-spacing");
4864  if (option != (const char *) NULL)
4865  draw_info->interline_spacing=StringToDouble(option,(char **) NULL);
4866  draw_info->direction=UndefinedDirection;
4867  option=GetImageOption(clone_info,"interword-spacing");
4868  if (option != (const char *) NULL)
4869  draw_info->interword_spacing=StringToDouble(option,(char **) NULL);
4870  option=GetImageOption(clone_info,"direction");
4871  if (option != (const char *) NULL)
4874  option=GetImageOption(clone_info,"fill");
4875  if (option != (const char *) NULL)
4876  (void) QueryColorDatabase(option,&draw_info->fill,exception);
4877  option=GetImageOption(clone_info,"stroke");
4878  if (option != (const char *) NULL)
4879  (void) QueryColorDatabase(option,&draw_info->stroke,exception);
4880  option=GetImageOption(clone_info,"strokewidth");
4881  if (option != (const char *) NULL)
4882  draw_info->stroke_width=StringToDouble(option,(char **) NULL);
4883  option=GetImageOption(clone_info,"undercolor");
4884  if (option != (const char *) NULL)
4885  (void) QueryColorDatabase(option,&draw_info->undercolor,exception);
4886  option=GetImageOption(clone_info,"gravity");
4887  if (option != (const char *) NULL)
4889  MagickFalse,option);
4890  exception=DestroyExceptionInfo(exception);
4891  draw_info->signature=MagickSignature;
4892  clone_info=DestroyImageInfo(clone_info);
4893 }
4894 
4895 
4896 /*
4897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4898 % %
4899 % %
4900 % %
4901 + P e r m u t a t e %
4902 % %
4903 % %
4904 % %
4905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4906 %
4907 % Permutate() returns the permuation of the (n,k).
4908 %
4909 % The format of the Permutate method is:
4910 %
4911 % void Permutate(ssize_t n,ssize_t k)
4912 %
4913 % A description of each parameter follows:
4914 %
4915 % o n:
4916 %
4917 % o k:
4918 %
4919 %
4920 */
4921 static inline double Permutate(const ssize_t n,const ssize_t k)
4922 {
4923  double
4924  r;
4925 
4926  register ssize_t
4927  i;
4928 
4929  r=1.0;
4930  for (i=k+1; i <= n; i++)
4931  r*=i;
4932  for (i=1; i <= (n-k); i++)
4933  r/=i;
4934  return(r);
4935 }
4936 
4937 
4938 /*
4939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4940 % %
4941 % %
4942 % %
4943 + T r a c e P r i m i t i v e %
4944 % %
4945 % %
4946 % %
4947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4948 %
4949 % TracePrimitive is a collection of methods for generating graphic
4950 % primitives such as arcs, ellipses, paths, etc.
4951 %
4952 */
4953 
4954 static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4955  const PointInfo end,const PointInfo degrees)
4956 {
4957  PointInfo
4958  center,
4959  radii;
4960 
4961  center.x=0.5*(end.x+start.x);
4962  center.y=0.5*(end.y+start.y);
4963  radii.x=fabs(center.x-start.x);
4964  radii.y=fabs(center.y-start.y);
4965  TraceEllipse(primitive_info,center,radii,degrees);
4966 }
4967 
4968 static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
4969  const PointInfo end,const PointInfo arc,const double angle,
4970  const MagickBooleanType large_arc,const MagickBooleanType sweep)
4971 {
4972  double
4973  alpha,
4974  beta,
4975  delta,
4976  factor,
4977  gamma,
4978  theta;
4979 
4980  PointInfo
4981  center,
4982  points[3],
4983  radii;
4984 
4985  register double
4986  cosine,
4987  sine;
4988 
4989  register PrimitiveInfo
4990  *p;
4991 
4992  register ssize_t
4993  i;
4994 
4995  size_t
4996  arc_segments;
4997 
4998  if ((start.x == end.x) && (start.y == end.y))
4999  {
5000  TracePoint(primitive_info,end);
5001  return;
5002  }
5003  radii.x=fabs(arc.x);
5004  radii.y=fabs(arc.y);
5005  if ((radii.x == 0.0) || (radii.y == 0.0))
5006  {
5007  TraceLine(primitive_info,start,end);
5008  return;
5009  }
5010  cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
5011  sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
5012  center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
5013  center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
5014  delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
5015  (radii.y*radii.y);
5016  if (delta < MagickEpsilon)
5017  {
5018  TraceLine(primitive_info,start,end);
5019  return;
5020  }
5021  if (delta > 1.0)
5022  {
5023  radii.x*=sqrt((double) delta);
5024  radii.y*=sqrt((double) delta);
5025  }
5026  points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
5027  points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
5028  points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
5029  points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
5030  alpha=points[1].x-points[0].x;
5031  beta=points[1].y-points[0].y;
5032  factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25;
5033  if (factor <= 0.0)
5034  factor=0.0;
5035  else
5036  {
5037  factor=sqrt((double) factor);
5038  if (sweep == large_arc)
5039  factor=(-factor);
5040  }
5041  center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
5042  center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
5043  alpha=atan2(points[0].y-center.y,points[0].x-center.x);
5044  theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
5045  if ((theta < 0.0) && (sweep != MagickFalse))
5046  theta+=2.0*MagickPI;
5047  else
5048  if ((theta > 0.0) && (sweep == MagickFalse))
5049  theta-=2.0*MagickPI;
5050  arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
5051  MagickEpsilon))));
5052  p=primitive_info;
5053  for (i=0; i < (ssize_t) arc_segments; i++)
5054  {
5055  beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
5056  gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
5057  sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
5058  sin(fmod((double) beta,DegreesToRadians(360.0)));
5059  points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
5060  arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
5061  (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5062  points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
5063  arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
5064  (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5065  points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
5066  theta/arc_segments),DegreesToRadians(360.0))));
5067  points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
5068  theta/arc_segments),DegreesToRadians(360.0))));
5069  points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
5070  (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5071  points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
5072  (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5073  p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
5074  p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
5075  (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
5076  points[0].y);
5077  (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
5078  points[0].y);
5079  (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
5080  points[1].y);
5081  (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
5082  points[1].y);
5083  (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
5084  points[2].y);
5085  (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
5086  points[2].y);
5087  if (i == (ssize_t) (arc_segments-1))
5088  (p+3)->point=end;
5089  TraceBezier(p,4);
5090  p+=p->coordinates;
5091  }
5092  primitive_info->coordinates=(size_t) (p-primitive_info);
5093  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
5094  {
5095  p->primitive=primitive_info->primitive;
5096  p--;
5097  }
5098 }
5099 
5100 static void TraceBezier(PrimitiveInfo *primitive_info,
5101  const size_t number_coordinates)
5102 {
5103  double
5104  alpha,
5105  *coefficients,
5106  weight;
5107 
5108  PointInfo
5109  end,
5110  point,
5111  *points;
5112 
5113  register PrimitiveInfo
5114  *p;
5115 
5116  register ssize_t
5117  i,
5118  j;
5119 
5120  size_t
5121  control_points,
5122  quantum;
5123 
5124  /*
5125  Allocate coeficients.
5126  */
5127  quantum=number_coordinates;
5128  for (i=0; i < (ssize_t) number_coordinates; i++)
5129  {
5130  for (j=i+1; j < (ssize_t) number_coordinates; j++)
5131  {
5132  alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
5133  if (alpha > (double) quantum)
5134  quantum=(size_t) alpha;
5135  alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
5136  if (alpha > (double) quantum)
5137  quantum=(size_t) alpha;
5138  }
5139  }
5140  quantum=(size_t) MagickMin((double) quantum/number_coordinates,
5141  (double) BezierQuantum);
5142  control_points=quantum*number_coordinates;
5143  coefficients=(double *) AcquireQuantumMemory((size_t)
5144  number_coordinates,sizeof(*coefficients));
5145  points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5146  sizeof(*points));
5147  if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
5148  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5149  /*
5150  Compute bezier points.
5151  */
5152  end=primitive_info[number_coordinates-1].point;
5153  for (i=0; i < (ssize_t) number_coordinates; i++)
5154  coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
5155  weight=0.0;
5156  for (i=0; i < (ssize_t) control_points; i++)
5157  {
5158  p=primitive_info;
5159  point.x=0.0;
5160  point.y=0.0;
5161  alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
5162  for (j=0; j < (ssize_t) number_coordinates; j++)
5163  {
5164  point.x+=alpha*coefficients[j]*p->point.x;
5165  point.y+=alpha*coefficients[j]*p->point.y;
5166  alpha*=weight/(1.0-weight);
5167  p++;
5168  }
5169  points[i]=point;
5170  weight+=1.0/control_points;
5171  }
5172  /*
5173  Bezier curves are just short segmented polys.
5174  */
5175  p=primitive_info;
5176  for (i=0; i < (ssize_t) control_points; i++)
5177  {
5178  TracePoint(p,points[i]);
5179  p+=p->coordinates;
5180  }
5181  TracePoint(p,end);
5182  p+=p->coordinates;
5183  primitive_info->coordinates=(size_t) (p-primitive_info);
5184  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
5185  {
5186  p->primitive=primitive_info->primitive;
5187  p--;
5188  }
5189  points=(PointInfo *) RelinquishMagickMemory(points);
5190  coefficients=(double *) RelinquishMagickMemory(coefficients);
5191 }
5192 
5193 static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5194  const PointInfo end)
5195 {
5196  double
5197  alpha,
5198  beta,
5199  radius;
5200 
5201  PointInfo
5202  offset,
5203  degrees;
5204 
5205  alpha=end.x-start.x;
5206  beta=end.y-start.y;
5207  radius=hypot((double) alpha,(double) beta);
5208  offset.x=(double) radius;
5209  offset.y=(double) radius;
5210  degrees.x=0.0;
5211  degrees.y=360.0;
5212  TraceEllipse(primitive_info,start,offset,degrees);
5213 }
5214 
5215 static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5216  const PointInfo stop,const PointInfo degrees)
5217 {
5218  double
5219  delta,
5220  step,
5221  y;
5222 
5223  PointInfo
5224  angle,
5225  point;
5226 
5227  register PrimitiveInfo
5228  *p;
5229 
5230  register ssize_t
5231  i;
5232 
5233  /*
5234  Ellipses are just short segmented polys.
5235  */
5236  if ((stop.x == 0.0) && (stop.y == 0.0))
5237  {
5238  TracePoint(primitive_info,start);
5239  return;
5240  }
5241  delta=2.0/MagickMax(stop.x,stop.y);
5242  step=MagickPI/8.0;
5243  if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
5244  step=MagickPI/(4*(MagickPI/delta/2+0.5));
5245  angle.x=DegreesToRadians(degrees.x);
5246  y=degrees.y;
5247  while (y < degrees.x)
5248  y+=360.0;
5249  angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
5250  for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5251  {
5252  point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5253  point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5254  TracePoint(p,point);
5255  p+=p->coordinates;
5256  }
5257  point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5258  point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5259  TracePoint(p,point);
5260  p+=p->coordinates;
5261  primitive_info->coordinates=(size_t) (p-primitive_info);
5262  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
5263  {
5264  p->primitive=primitive_info->primitive;
5265  p--;
5266  }
5267 }
5268 
5269 static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5270  const PointInfo end)
5271 {
5272  TracePoint(primitive_info,start);
5273  if ((fabs(start.x-end.x) < MagickEpsilon) &&
5274  (fabs(start.y-end.y) < MagickEpsilon))
5275  {
5276  primitive_info->primitive=PointPrimitive;
5277  primitive_info->coordinates=1;
5278  return;
5279  }
5280  TracePoint(primitive_info+1,end);
5281  (primitive_info+1)->primitive=primitive_info->primitive;
5282  primitive_info->coordinates=2;
5283 }
5284 
5285 static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
5286 {
5287  char
5288  token[MaxTextExtent];
5289 
5290  const char
5291  *p;
5292 
5293  double
5294  x,
5295  y;
5296 
5297  int
5298  attribute,
5299  last_attribute;
5300 
5301  PointInfo
5302  end,
5303  points[4],
5304  point,
5305  start;
5306 
5308  primitive_type;
5309 
5310  register PrimitiveInfo
5311  *q;
5312 
5313  register ssize_t
5314  i;
5315 
5316  size_t
5317  number_coordinates,
5318  z_count;
5319 
5320  attribute=0;
5321  end.x=0.0;
5322  end.y=0.0;
5323  point.x=0.0;
5324  point.y=0.0;
5325  start.x=0.0;
5326  start.y=0.0;
5327  number_coordinates=0;
5328  z_count=0;
5329  (void) ResetMagickMemory(points,0,sizeof(*points));
5330  primitive_type=primitive_info->primitive;
5331  q=primitive_info;
5332  for (p=path; *p != '\0'; )
5333  {
5334  while (isspace((int) ((unsigned char) *p)) != 0)
5335  p++;
5336  if (*p == '\0')
5337  break;
5338  last_attribute=attribute;
5339  attribute=(int) (*p++);
5340  switch (attribute)
5341  {
5342  case 'a':
5343  case 'A':
5344  {
5345  double
5346  angle;
5347 
5349  large_arc,
5350  sweep;
5351 
5352  PointInfo
5353  arc;
5354 
5355  /*
5356  Compute arc points.
5357  */
5358  do
5359  {
5360  GetMagickToken(p,&p,token);
5361  if (*token == ',')
5362  GetMagickToken(p,&p,token);
5363  arc.x=StringToDouble(token,(char **) NULL);
5364  GetMagickToken(p,&p,token);
5365  if (*token == ',')
5366  GetMagickToken(p,&p,token);
5367  arc.y=StringToDouble(token,(char **) NULL);
5368  GetMagickToken(p,&p,token);
5369  if (*token == ',')
5370  GetMagickToken(p,&p,token);
5371  angle=StringToDouble(token,(char **) NULL);
5372  GetMagickToken(p,&p,token);
5373  if (*token == ',')
5374  GetMagickToken(p,&p,token);
5375  large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
5376  GetMagickToken(p,&p,token);
5377  if (*token == ',')
5378  GetMagickToken(p,&p,token);
5379  sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
5380  GetMagickToken(p,&p,token);
5381  if (*token == ',')
5382  GetMagickToken(p,&p,token);
5383  x=StringToDouble(token,(char **) NULL);
5384  GetMagickToken(p,&p,token);
5385  if (*token == ',')
5386  GetMagickToken(p,&p,token);
5387  y=StringToDouble(token,(char **) NULL);
5388  end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5389  end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5390  TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5391  q+=q->coordinates;
5392  point=end;
5393  while (isspace((int) ((unsigned char) *p)) != 0)
5394  p++;
5395  if (*p == ',')
5396  p++;
5397  } while (IsPoint(p) != MagickFalse);
5398  break;
5399  }
5400  case 'c':
5401  case 'C':
5402  {
5403  /*
5404  Compute bezier points.
5405  */
5406  do
5407  {
5408  points[0]=point;
5409  for (i=1; i < 4; i++)
5410  {
5411  GetMagickToken(p,&p,token);
5412  if (*token == ',')
5413  GetMagickToken(p,&p,token);
5414  x=StringToDouble(token,(char **) NULL);
5415  GetMagickToken(p,&p,token);
5416  if (*token == ',')
5417  GetMagickToken(p,&p,token);
5418  y=StringToDouble(token,(char **) NULL);
5419  end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5420  end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5421  points[i]=end;
5422  }
5423  for (i=0; i < 4; i++)
5424  (q+i)->point=points[i];
5425  TraceBezier(q,4);
5426  q+=q->coordinates;
5427  point=end;
5428  } while (IsPoint(p) != MagickFalse);
5429  break;
5430  }
5431  case 'H':
5432  case 'h':
5433  {
5434  do
5435  {
5436  GetMagickToken(p,&p,tok