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