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