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