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