2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8 % D D RRRR AAAAA W W W %
13 % MagickCore Image Drawing Methods %
21 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://imagemagick.org/script/license.php %
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. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
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"
53 #include "MagickCore/cache-private.h"
54 #include "MagickCore/cache-view.h"
55 #include "MagickCore/channel.h"
56 #include "MagickCore/color.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/composite.h"
59 #include "MagickCore/composite-private.h"
60 #include "MagickCore/constitute.h"
61 #include "MagickCore/draw.h"
62 #include "MagickCore/draw-private.h"
63 #include "MagickCore/enhance.h"
64 #include "MagickCore/exception.h"
65 #include "MagickCore/exception-private.h"
66 #include "MagickCore/gem.h"
67 #include "MagickCore/geometry.h"
68 #include "MagickCore/image-private.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/memory-private.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/paint.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/pixel-private.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/resample.h"
80 #include "MagickCore/resample-private.h"
81 #include "MagickCore/resource_.h"
82 #include "MagickCore/splay-tree.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/thread-private.h"
86 #include "MagickCore/token.h"
87 #include "MagickCore/transform-private.h"
88 #include "MagickCore/utility.h"
93 #define BezierQuantum 200
94 #define PrimitiveExtentPad 128
95 #define MaxBezierCoordinates 4194304
96 #define ThrowPointExpectedException(token,exception) \
98 (void) ThrowMagickException(exception,GetMagickModule(),DrawError, \
99 "NonconformingDrawingPrimitiveDefinition","`%s'",token); \
100 status=MagickFalse; \
105 Typedef declarations.
107 typedef struct _EdgeInfo
131 typedef struct _ElementInfo
141 typedef struct _MVGInfo
159 typedef struct _PolygonInfo
177 typedef struct _PathInfo
187 Forward declarations.
190 *DrawClippingMask(Image *,const DrawInfo *,const char *,const char *,
193 static MagickBooleanType
194 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
196 RenderMVGContent(Image *,const DrawInfo *,const size_t,ExceptionInfo *),
197 TraceArc(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
198 TraceArcPath(MVGInfo *,const PointInfo,const PointInfo,const PointInfo,
199 const double,const MagickBooleanType,const MagickBooleanType),
200 TraceBezier(MVGInfo *,const size_t),
201 TraceCircle(MVGInfo *,const PointInfo,const PointInfo),
202 TraceEllipse(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
203 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
204 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
205 TraceRoundRectangle(MVGInfo *,const PointInfo,const PointInfo,PointInfo),
206 TraceSquareLinecap(PrimitiveInfo *,const size_t,const double);
209 *TraceStrokePolygon(const Image *,const DrawInfo *,const PrimitiveInfo *);
212 TracePath(MVGInfo *,const char *,ExceptionInfo *);
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
219 % A c q u i r e D r a w I n f o %
223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225 % AcquireDrawInfo() returns a DrawInfo structure properly initialized.
227 % The format of the AcquireDrawInfo method is:
229 % DrawInfo *AcquireDrawInfo(void)
232 MagickExport DrawInfo *AcquireDrawInfo(void)
237 draw_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*draw_info));
238 GetDrawInfo((ImageInfo *) NULL,draw_info);
243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 % C l o n e D r a w I n f o %
251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 % CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
254 % is specified, a new DrawInfo structure is created initialized to default
257 % The format of the CloneDrawInfo method is:
259 % DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
260 % const DrawInfo *draw_info)
262 % A description of each parameter follows:
264 % o image_info: the image info.
266 % o draw_info: the draw info.
269 MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
270 const DrawInfo *draw_info)
278 clone_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*clone_info));
279 GetDrawInfo(image_info,clone_info);
280 if (draw_info == (DrawInfo *) NULL)
282 exception=AcquireExceptionInfo();
283 if (draw_info->primitive != (char *) NULL)
284 (void) CloneString(&clone_info->primitive,draw_info->primitive);
285 if (draw_info->geometry != (char *) NULL)
286 (void) CloneString(&clone_info->geometry,draw_info->geometry);
287 clone_info->compliance=draw_info->compliance;
288 clone_info->viewbox=draw_info->viewbox;
289 clone_info->affine=draw_info->affine;
290 clone_info->gravity=draw_info->gravity;
291 clone_info->fill=draw_info->fill;
292 clone_info->stroke=draw_info->stroke;
293 clone_info->stroke_width=draw_info->stroke_width;
294 if (draw_info->fill_pattern != (Image *) NULL)
295 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
297 if (draw_info->stroke_pattern != (Image *) NULL)
298 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
299 MagickTrue,exception);
300 clone_info->stroke_antialias=draw_info->stroke_antialias;
301 clone_info->text_antialias=draw_info->text_antialias;
302 clone_info->fill_rule=draw_info->fill_rule;
303 clone_info->linecap=draw_info->linecap;
304 clone_info->linejoin=draw_info->linejoin;
305 clone_info->miterlimit=draw_info->miterlimit;
306 clone_info->dash_offset=draw_info->dash_offset;
307 clone_info->decorate=draw_info->decorate;
308 clone_info->compose=draw_info->compose;
309 if (draw_info->text != (char *) NULL)
310 (void) CloneString(&clone_info->text,draw_info->text);
311 if (draw_info->font != (char *) NULL)
312 (void) CloneString(&clone_info->font,draw_info->font);
313 if (draw_info->metrics != (char *) NULL)
314 (void) CloneString(&clone_info->metrics,draw_info->metrics);
315 if (draw_info->family != (char *) NULL)
316 (void) CloneString(&clone_info->family,draw_info->family);
317 clone_info->style=draw_info->style;
318 clone_info->stretch=draw_info->stretch;
319 clone_info->weight=draw_info->weight;
320 if (draw_info->encoding != (char *) NULL)
321 (void) CloneString(&clone_info->encoding,draw_info->encoding);
322 clone_info->pointsize=draw_info->pointsize;
323 clone_info->kerning=draw_info->kerning;
324 clone_info->interline_spacing=draw_info->interline_spacing;
325 clone_info->interword_spacing=draw_info->interword_spacing;
326 clone_info->direction=draw_info->direction;
327 if (draw_info->density != (char *) NULL)
328 (void) CloneString(&clone_info->density,draw_info->density);
329 clone_info->align=draw_info->align;
330 clone_info->undercolor=draw_info->undercolor;
331 clone_info->border_color=draw_info->border_color;
332 if (draw_info->server_name != (char *) NULL)
333 (void) CloneString(&clone_info->server_name,draw_info->server_name);
334 if (draw_info->dash_pattern != (double *) NULL)
339 for (x=0; fabs(draw_info->dash_pattern[x]) >= MagickEpsilon; x++) ;
340 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) (x+4),
341 sizeof(*clone_info->dash_pattern));
342 if (clone_info->dash_pattern == (double *) NULL)
343 ThrowFatalException(ResourceLimitFatalError,
344 "UnableToAllocateDashPattern");
345 (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(size_t)
346 (x+4)*sizeof(*clone_info->dash_pattern));
348 clone_info->gradient=draw_info->gradient;
349 if (draw_info->gradient.stops != (StopInfo *) NULL)
354 number_stops=clone_info->gradient.number_stops;
355 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
356 number_stops,sizeof(*clone_info->gradient.stops));
357 if (clone_info->gradient.stops == (StopInfo *) NULL)
358 ThrowFatalException(ResourceLimitFatalError,
359 "UnableToAllocateDashPattern");
360 (void) memcpy(clone_info->gradient.stops,draw_info->gradient.stops,
361 (size_t) number_stops*sizeof(*clone_info->gradient.stops));
363 clone_info->bounds=draw_info->bounds;
364 clone_info->fill_alpha=draw_info->fill_alpha;
365 clone_info->stroke_alpha=draw_info->stroke_alpha;
366 clone_info->element_reference=draw_info->element_reference;
367 clone_info->clip_path=draw_info->clip_path;
368 clone_info->clip_units=draw_info->clip_units;
369 if (draw_info->clip_mask != (char *) NULL)
370 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
371 if (draw_info->clipping_mask != (Image *) NULL)
372 clone_info->clipping_mask=CloneImage(draw_info->clipping_mask,0,0,
373 MagickTrue,exception);
374 if (draw_info->composite_mask != (Image *) NULL)
375 clone_info->composite_mask=CloneImage(draw_info->composite_mask,0,0,
376 MagickTrue,exception);
377 clone_info->render=draw_info->render;
378 clone_info->debug=IsEventLogging();
379 exception=DestroyExceptionInfo(exception);
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388 + C o n v e r t P a t h T o P o l y g o n %
392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394 % ConvertPathToPolygon() converts a path to the more efficient sorted
397 % The format of the ConvertPathToPolygon method is:
399 % PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info)
401 % A description of each parameter follows:
403 % o Method ConvertPathToPolygon returns the path in a more efficient sorted
404 % rendering form of type PolygonInfo.
406 % o draw_info: Specifies a pointer to an DrawInfo structure.
408 % o path_info: Specifies a pointer to an PathInfo structure.
413 #if defined(__cplusplus) || defined(c_plusplus)
417 static int DrawCompareEdges(const void *p_edge,const void *q_edge)
419 #define DrawCompareEdge(p,q) \
421 if (((p)-(q)) < 0.0) \
423 if (((p)-(q)) > 0.0) \
427 register const PointInfo
432 Edge sorting for right-handed coordinate system.
434 p=((const EdgeInfo *) p_edge)->points;
435 q=((const EdgeInfo *) q_edge)->points;
436 DrawCompareEdge(p[0].y,q[0].y);
437 DrawCompareEdge(p[0].x,q[0].x);
438 DrawCompareEdge((p[1].x-p[0].x)*(q[1].y-q[0].y),(p[1].y-p[0].y)*
440 DrawCompareEdge(p[1].y,q[1].y);
441 DrawCompareEdge(p[1].x,q[1].x);
445 #if defined(__cplusplus) || defined(c_plusplus)
449 static void LogPolygonInfo(const PolygonInfo *polygon_info)
458 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
459 p=polygon_info->edges;
460 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
462 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
464 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
465 p->direction != MagickFalse ? "down" : "up");
466 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
467 p->ghostline != MagickFalse ? "transparent" : "opaque");
468 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
469 " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
470 p->bounds.x2,p->bounds.y2);
471 for (j=0; j < (ssize_t) p->number_points; j++)
472 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g",
473 p->points[j].x,p->points[j].y);
476 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
479 static void ReversePoints(PointInfo *points,const size_t number_points)
487 for (i=0; i < (ssize_t) (number_points >> 1); i++)
490 points[i]=points[number_points-(i+1)];
491 points[number_points-(i+1)]=point;
495 static PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info)
524 Convert a path to the more efficient sorted rendering form.
526 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
527 if (polygon_info == (PolygonInfo *) NULL)
528 return((PolygonInfo *) NULL);
530 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges,
531 sizeof(*polygon_info->edges));
532 if (polygon_info->edges == (EdgeInfo *) NULL)
533 return((PolygonInfo *) NULL);
534 (void) memset(polygon_info->edges,0,number_edges*
535 sizeof(*polygon_info->edges));
538 ghostline=MagickFalse;
541 points=(PointInfo *) NULL;
542 (void) memset(&point,0,sizeof(point));
543 (void) memset(&bounds,0,sizeof(bounds));
544 polygon_info->edges[edge].number_points=(size_t) n;
545 polygon_info->edges[edge].scanline=0.0;
546 polygon_info->edges[edge].highwater=0;
547 polygon_info->edges[edge].ghostline=ghostline;
548 polygon_info->edges[edge].direction=(ssize_t) direction;
549 polygon_info->edges[edge].points=points;
550 polygon_info->edges[edge].bounds=bounds;
551 polygon_info->number_edges=0;
552 for (i=0; path_info[i].code != EndCode; i++)
554 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
555 (path_info[i].code == GhostlineCode))
560 if ((points != (PointInfo *) NULL) && (n >= 2))
562 if (edge == number_edges)
565 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
566 polygon_info->edges,(size_t) number_edges,
567 sizeof(*polygon_info->edges));
568 if (polygon_info->edges == (EdgeInfo *) NULL)
569 return((PolygonInfo *) NULL);
571 polygon_info->edges[edge].number_points=(size_t) n;
572 polygon_info->edges[edge].scanline=(-1.0);
573 polygon_info->edges[edge].highwater=0;
574 polygon_info->edges[edge].ghostline=ghostline;
575 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
577 ReversePoints(points,(size_t) n);
578 polygon_info->edges[edge].points=points;
579 polygon_info->edges[edge].bounds=bounds;
580 polygon_info->edges[edge].bounds.y1=points[0].y;
581 polygon_info->edges[edge].bounds.y2=points[n-1].y;
582 points=(PointInfo *) NULL;
583 ghostline=MagickFalse;
586 if (points == (PointInfo *) NULL)
589 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
591 if (points == (PointInfo *) NULL)
592 return((PolygonInfo *) NULL);
594 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
595 point=path_info[i].point;
606 next_direction=((path_info[i].point.y > point.y) ||
607 ((fabs(path_info[i].point.y-point.y) < MagickEpsilon) &&
608 (path_info[i].point.x > point.x))) ? 1 : -1;
609 if ((points != (PointInfo *) NULL) && (direction != 0) &&
610 (direction != next_direction))
616 if (edge == number_edges)
619 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
620 polygon_info->edges,(size_t) number_edges,
621 sizeof(*polygon_info->edges));
622 if (polygon_info->edges == (EdgeInfo *) NULL)
623 return((PolygonInfo *) NULL);
625 polygon_info->edges[edge].number_points=(size_t) n;
626 polygon_info->edges[edge].scanline=(-1.0);
627 polygon_info->edges[edge].highwater=0;
628 polygon_info->edges[edge].ghostline=ghostline;
629 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
631 ReversePoints(points,(size_t) n);
632 polygon_info->edges[edge].points=points;
633 polygon_info->edges[edge].bounds=bounds;
634 polygon_info->edges[edge].bounds.y1=points[0].y;
635 polygon_info->edges[edge].bounds.y2=points[n-1].y;
637 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
639 if (points == (PointInfo *) NULL)
640 return((PolygonInfo *) NULL);
642 ghostline=MagickFalse;
648 direction=next_direction;
649 if (points == (PointInfo *) NULL)
651 if (n == (ssize_t) number_points)
654 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
656 if (points == (PointInfo *) NULL)
657 return((PolygonInfo *) NULL);
659 point=path_info[i].point;
661 if (point.x < bounds.x1)
663 if (point.x > bounds.x2)
667 if (points != (PointInfo *) NULL)
670 points=(PointInfo *) RelinquishMagickMemory(points);
673 if (edge == number_edges)
676 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
677 polygon_info->edges,(size_t) number_edges,
678 sizeof(*polygon_info->edges));
679 if (polygon_info->edges == (EdgeInfo *) NULL)
680 return((PolygonInfo *) NULL);
682 polygon_info->edges[edge].number_points=(size_t) n;
683 polygon_info->edges[edge].scanline=(-1.0);
684 polygon_info->edges[edge].highwater=0;
685 polygon_info->edges[edge].ghostline=ghostline;
686 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
688 ReversePoints(points,(size_t) n);
689 polygon_info->edges[edge].points=points;
690 polygon_info->edges[edge].bounds=bounds;
691 polygon_info->edges[edge].bounds.y1=points[0].y;
692 polygon_info->edges[edge].bounds.y2=points[n-1].y;
693 ghostline=MagickFalse;
697 polygon_info->number_edges=edge;
698 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
699 sizeof(*polygon_info->edges),DrawCompareEdges);
700 if (IsEventLogging() != MagickFalse)
701 LogPolygonInfo(polygon_info);
702 return(polygon_info);
706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 + C o n v e r t P r i m i t i v e T o P a t h %
714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716 % ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
719 % The format of the ConvertPrimitiveToPath method is:
721 % PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
722 % const PrimitiveInfo *primitive_info)
724 % A description of each parameter follows:
726 % o Method ConvertPrimitiveToPath returns a vector path structure of type
729 % o draw_info: a structure of type DrawInfo.
731 % o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
736 static void LogPathInfo(const PathInfo *path_info)
738 register const PathInfo
741 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
742 for (p=path_info; p->code != EndCode; p++)
743 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
744 " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
745 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
746 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
748 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
751 static PathInfo *ConvertPrimitiveToPath(const PrimitiveInfo *primitive_info)
775 Converts a PrimitiveInfo structure into a vector path structure.
777 switch (primitive_info->primitive)
784 return((PathInfo *) NULL);
788 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
789 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (3UL*i+1UL),
791 if (path_info == (PathInfo *) NULL)
792 return((PathInfo *) NULL);
794 closed_subpath=MagickFalse;
801 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
804 if (coordinates <= 0)
809 coordinates=(ssize_t) primitive_info[i].coordinates;
810 p=primitive_info[i].point;
813 closed_subpath=primitive_info[i].closed_subpath;
816 if ((code == MoveToCode) || (coordinates <= 0) ||
817 (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
818 (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
821 Eliminate duplicate points.
823 path_info[n].code=code;
824 path_info[n].point=primitive_info[i].point;
825 q=primitive_info[i].point;
829 continue; /* next point in current subpath */
830 if (closed_subpath != MagickFalse)
832 closed_subpath=MagickFalse;
836 Mark the p point as open if the subpath is not closed.
838 path_info[start].code=OpenCode;
839 path_info[n].code=GhostlineCode;
840 path_info[n].point=primitive_info[i].point;
842 path_info[n].code=LineToCode;
843 path_info[n].point=p;
846 path_info[n].code=EndCode;
847 path_info[n].point.x=0.0;
848 path_info[n].point.y=0.0;
849 if (IsEventLogging() != MagickFalse)
850 LogPathInfo(path_info);
851 path_info=(PathInfo *) ResizeQuantumMemory(path_info,(size_t) (n+1),
857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861 % D e s t r o y D r a w I n f o %
865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
867 % DestroyDrawInfo() deallocates memory associated with an DrawInfo structure.
869 % The format of the DestroyDrawInfo method is:
871 % DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
873 % A description of each parameter follows:
875 % o draw_info: the draw info.
878 MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
880 assert(draw_info != (DrawInfo *) NULL);
881 if (draw_info->debug != MagickFalse)
882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
883 assert(draw_info->signature == MagickCoreSignature);
884 if (draw_info->primitive != (char *) NULL)
885 draw_info->primitive=DestroyString(draw_info->primitive);
886 if (draw_info->text != (char *) NULL)
887 draw_info->text=DestroyString(draw_info->text);
888 if (draw_info->geometry != (char *) NULL)
889 draw_info->geometry=DestroyString(draw_info->geometry);
890 if (draw_info->fill_pattern != (Image *) NULL)
891 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
892 if (draw_info->stroke_pattern != (Image *) NULL)
893 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
894 if (draw_info->font != (char *) NULL)
895 draw_info->font=DestroyString(draw_info->font);
896 if (draw_info->metrics != (char *) NULL)
897 draw_info->metrics=DestroyString(draw_info->metrics);
898 if (draw_info->family != (char *) NULL)
899 draw_info->family=DestroyString(draw_info->family);
900 if (draw_info->encoding != (char *) NULL)
901 draw_info->encoding=DestroyString(draw_info->encoding);
902 if (draw_info->density != (char *) NULL)
903 draw_info->density=DestroyString(draw_info->density);
904 if (draw_info->server_name != (char *) NULL)
905 draw_info->server_name=(char *)
906 RelinquishMagickMemory(draw_info->server_name);
907 if (draw_info->dash_pattern != (double *) NULL)
908 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
909 draw_info->dash_pattern);
910 if (draw_info->gradient.stops != (StopInfo *) NULL)
911 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
912 draw_info->gradient.stops);
913 if (draw_info->clip_mask != (char *) NULL)
914 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
915 if (draw_info->clipping_mask != (Image *) NULL)
916 draw_info->clipping_mask=DestroyImage(draw_info->clipping_mask);
917 if (draw_info->composite_mask != (Image *) NULL)
918 draw_info->composite_mask=DestroyImage(draw_info->composite_mask);
919 draw_info->signature=(~MagickCoreSignature);
920 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
925 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
929 + D e s t r o y E d g e %
933 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 % DestroyEdge() destroys the specified polygon edge.
937 % The format of the DestroyEdge method is:
939 % ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
941 % A description of each parameter follows:
943 % o polygon_info: Specifies a pointer to an PolygonInfo structure.
945 % o edge: the polygon edge number to destroy.
948 static size_t DestroyEdge(PolygonInfo *polygon_info,
951 assert(edge < polygon_info->number_edges);
952 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
953 polygon_info->edges[edge].points);
954 polygon_info->number_edges--;
955 if (edge < polygon_info->number_edges)
956 (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
957 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
958 return(polygon_info->number_edges);
962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966 + D e s t r o y P o l y g o n I n f o %
970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
972 % DestroyPolygonInfo() destroys the PolygonInfo data structure.
974 % The format of the DestroyPolygonInfo method is:
976 % PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
978 % A description of each parameter follows:
980 % o polygon_info: Specifies a pointer to an PolygonInfo structure.
983 static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
988 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
989 polygon_info->edges[i].points=(PointInfo *)
990 RelinquishMagickMemory(polygon_info->edges[i].points);
991 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
992 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1000 % D r a w A f f i n e I m a g e %
1004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1006 % DrawAffineImage() composites the source over the destination image as
1007 % dictated by the affine transform.
1009 % The format of the DrawAffineImage method is:
1011 % MagickBooleanType DrawAffineImage(Image *image,const Image *source,
1012 % const AffineMatrix *affine,ExceptionInfo *exception)
1014 % A description of each parameter follows:
1016 % o image: the image.
1018 % o source: the source image.
1020 % o affine: the affine transform.
1022 % o exception: return any errors or warnings in this structure.
1026 static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
1027 const double y,const SegmentInfo *edge)
1040 Determine left and right edges.
1042 inverse_edge.x1=edge->x1;
1043 inverse_edge.y1=edge->y1;
1044 inverse_edge.x2=edge->x2;
1045 inverse_edge.y2=edge->y2;
1046 z=affine->ry*y+affine->tx;
1047 if (affine->sx >= MagickEpsilon)
1049 intercept=(-z/affine->sx);
1051 if (x > inverse_edge.x1)
1053 intercept=(-z+(double) image->columns)/affine->sx;
1055 if (x < inverse_edge.x2)
1059 if (affine->sx < -MagickEpsilon)
1061 intercept=(-z+(double) image->columns)/affine->sx;
1063 if (x > inverse_edge.x1)
1065 intercept=(-z/affine->sx);
1067 if (x < inverse_edge.x2)
1071 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
1073 inverse_edge.x2=edge->x1;
1074 return(inverse_edge);
1077 Determine top and bottom edges.
1079 z=affine->sy*y+affine->ty;
1080 if (affine->rx >= MagickEpsilon)
1082 intercept=(-z/affine->rx);
1084 if (x > inverse_edge.x1)
1086 intercept=(-z+(double) image->rows)/affine->rx;
1088 if (x < inverse_edge.x2)
1092 if (affine->rx < -MagickEpsilon)
1094 intercept=(-z+(double) image->rows)/affine->rx;
1096 if (x > inverse_edge.x1)
1098 intercept=(-z/affine->rx);
1100 if (x < inverse_edge.x2)
1104 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
1106 inverse_edge.x2=edge->x2;
1107 return(inverse_edge);
1109 return(inverse_edge);
1112 static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1120 determinant=PerceptibleReciprocal(affine->sx*affine->sy-affine->rx*
1122 inverse_affine.sx=determinant*affine->sy;
1123 inverse_affine.rx=determinant*(-affine->rx);
1124 inverse_affine.ry=determinant*(-affine->ry);
1125 inverse_affine.sy=determinant*affine->sx;
1126 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1128 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1130 return(inverse_affine);
1133 MagickExport MagickBooleanType DrawAffineImage(Image *image,
1134 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
1166 Determine bounding box.
1168 assert(image != (Image *) NULL);
1169 assert(image->signature == MagickCoreSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1172 assert(source != (const Image *) NULL);
1173 assert(source->signature == MagickCoreSignature);
1174 assert(affine != (AffineMatrix *) NULL);
1177 extent[1].x=(double) source->columns-1.0;
1179 extent[2].x=(double) source->columns-1.0;
1180 extent[2].y=(double) source->rows-1.0;
1182 extent[3].y=(double) source->rows-1.0;
1183 for (i=0; i < 4; i++)
1189 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1190 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1194 for (i=1; i < 4; i++)
1196 if (min.x > extent[i].x)
1198 if (min.y > extent[i].y)
1200 if (max.x < extent[i].x)
1202 if (max.y < extent[i].y)
1206 Affine transform image.
1208 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1209 return(MagickFalse);
1211 edge.x1=MagickMax(min.x,0.0);
1212 edge.y1=MagickMax(min.y,0.0);
1213 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1214 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1215 inverse_affine=InverseAffineMatrix(affine);
1216 GetPixelInfo(image,&zero);
1217 start=(ssize_t) ceil(edge.y1-0.5);
1218 stop=(ssize_t) floor(edge.y2+0.5);
1219 source_view=AcquireVirtualCacheView(source,exception);
1220 image_view=AcquireAuthenticCacheView(image,exception);
1221 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1222 #pragma omp parallel for schedule(static) shared(status) \
1223 magick_number_threads(source,image,stop-start,1)
1225 for (y=start; y <= stop; y++)
1246 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1247 if (inverse_edge.x2 < inverse_edge.x1)
1249 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
1250 0.5),y,(size_t) (floor(inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),
1252 if (q == (Quantum *) NULL)
1257 for (x=(ssize_t) ceil(inverse_edge.x1-0.5); x <= (ssize_t) floor(inverse_edge.x2+0.5); x++)
1259 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1261 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1263 status=InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1264 point.x,point.y,&pixel,exception);
1265 if (status == MagickFalse)
1267 GetPixelInfoPixel(image,q,&composite);
1268 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1270 SetPixelViaPixelInfo(image,&composite,q);
1272 q+=GetPixelChannels(image);
1274 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1277 source_view=DestroyCacheView(source_view);
1278 image_view=DestroyCacheView(image_view);
1283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287 + D r a w B o u n d i n g R e c t a n g l e s %
1291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293 % DrawBoundingRectangles() draws the bounding rectangles on the image. This
1294 % is only useful for developers debugging the rendering algorithm.
1296 % The format of the DrawBoundingRectangles method is:
1298 % MagickBooleanType DrawBoundingRectangles(Image *image,
1299 % const DrawInfo *draw_info,PolygonInfo *polygon_info,
1300 % ExceptionInfo *exception)
1302 % A description of each parameter follows:
1304 % o image: the image.
1306 % o draw_info: the draw info.
1308 % o polygon_info: Specifies a pointer to a PolygonInfo structure.
1310 % o exception: return any errors or warnings in this structure.
1314 static inline double SaneStrokeWidth(const Image *image,
1315 const DrawInfo *draw_info)
1317 return(MagickMin((double) draw_info->stroke_width,
1318 (2.0*sqrt(2.0)+MagickEpsilon)*MagickMax(image->columns,image->rows)));
1321 static MagickBooleanType DrawBoundingRectangles(Image *image,
1322 const DrawInfo *draw_info,const PolygonInfo *polygon_info,
1323 ExceptionInfo *exception)
1351 (void) memset(primitive_info,0,sizeof(primitive_info));
1352 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1353 status=QueryColorCompliance("#000F",AllCompliance,&clone_info->fill,
1355 if (status == MagickFalse)
1357 clone_info=DestroyDrawInfo(clone_info);
1362 if (clone_info->density != (char *) NULL)
1370 flags=ParseGeometry(clone_info->density,&geometry_info);
1371 resolution.x=geometry_info.rho;
1372 resolution.y=geometry_info.sigma;
1373 if ((flags & SigmaValue) == MagickFalse)
1374 resolution.y=resolution.x;
1376 mid=(resolution.x/96.0)*ExpandAffine(&clone_info->affine)*
1377 SaneStrokeWidth(image,clone_info)/2.0;
1382 if (polygon_info != (PolygonInfo *) NULL)
1384 bounds=polygon_info->edges[0].bounds;
1385 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
1387 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1388 bounds.x1=polygon_info->edges[i].bounds.x1;
1389 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1390 bounds.y1=polygon_info->edges[i].bounds.y1;
1391 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1392 bounds.x2=polygon_info->edges[i].bounds.x2;
1393 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1394 bounds.y2=polygon_info->edges[i].bounds.y2;
1397 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1398 image->columns ? (double) image->columns-1 : bounds.x1;
1400 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1401 image->rows ? (double) image->rows-1 : bounds.y1;
1403 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1404 image->columns ? (double) image->columns-1 : bounds.x2;
1406 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1407 image->rows ? (double) image->rows-1 : bounds.y2;
1408 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
1410 if (polygon_info->edges[i].direction != 0)
1411 status=QueryColorCompliance("#f00",AllCompliance,&clone_info->stroke,
1414 status=QueryColorCompliance("#0f0",AllCompliance,&clone_info->stroke,
1416 if (status == MagickFalse)
1418 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1419 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1420 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1421 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1422 primitive_info[0].primitive=RectanglePrimitive;
1423 if (TraceRectangle(primitive_info,start,end) == MagickFalse)
1425 primitive_info[0].method=ReplaceMethod;
1426 coordinates=(ssize_t) primitive_info[0].coordinates;
1427 primitive_info[coordinates].primitive=UndefinedPrimitive;
1428 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1429 if (status == MagickFalse)
1432 if (i < (ssize_t) polygon_info->number_edges)
1434 clone_info=DestroyDrawInfo(clone_info);
1438 status=QueryColorCompliance("#00f",AllCompliance,&clone_info->stroke,
1440 if (status == MagickFalse)
1442 clone_info=DestroyDrawInfo(clone_info);
1445 start.x=(double) (bounds.x1-mid);
1446 start.y=(double) (bounds.y1-mid);
1447 end.x=(double) (bounds.x2+mid);
1448 end.y=(double) (bounds.y2+mid);
1449 primitive_info[0].primitive=RectanglePrimitive;
1450 if (TraceRectangle(primitive_info,start,end) == MagickFalse)
1452 primitive_info[0].method=ReplaceMethod;
1453 coordinates=(ssize_t) primitive_info[0].coordinates;
1454 primitive_info[coordinates].primitive=UndefinedPrimitive;
1455 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1456 clone_info=DestroyDrawInfo(clone_info);
1461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465 % D r a w C l i p P a t h %
1469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471 % DrawClipPath() draws the clip path on the image mask.
1473 % The format of the DrawClipPath method is:
1475 % MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
1476 % const char *id,ExceptionInfo *exception)
1478 % A description of each parameter follows:
1480 % o image: the image.
1482 % o draw_info: the draw info.
1484 % o id: the clip path id.
1486 % o exception: return any errors or warnings in this structure.
1489 MagickExport MagickBooleanType DrawClipPath(Image *image,
1490 const DrawInfo *draw_info,const char *id,ExceptionInfo *exception)
1501 clip_path=GetImageArtifact(image,id);
1502 if (clip_path == (const char *) NULL)
1503 return(MagickFalse);
1504 clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
1506 if (clipping_mask == (Image *) NULL)
1507 return(MagickFalse);
1508 status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
1509 clipping_mask=DestroyImage(clipping_mask);
1514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518 % D r a w C l i p p i n g M a s k %
1522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1524 % DrawClippingMask() draws the clip path and returns it as an image clipping
1527 % The format of the DrawClippingMask method is:
1529 % Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1530 % const char *id,const char *clip_path,ExceptionInfo *exception)
1532 % A description of each parameter follows:
1534 % o image: the image.
1536 % o draw_info: the draw info.
1538 % o id: the clip path id.
1540 % o clip_path: the clip path.
1542 % o exception: return any errors or warnings in this structure.
1545 static Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1546 const char *id,const char *clip_path,ExceptionInfo *exception)
1561 assert(image != (Image *) NULL);
1562 assert(image->signature == MagickCoreSignature);
1563 if (image->debug != MagickFalse)
1564 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1565 assert(draw_info != (const DrawInfo *) NULL);
1566 clip_mask=AcquireImage((const ImageInfo *) NULL,exception);
1567 status=SetImageExtent(clip_mask,image->columns,image->rows,exception);
1568 if (status == MagickFalse)
1569 return(DestroyImage(clip_mask));
1570 status=SetImageMask(clip_mask,WritePixelMask,(Image *) NULL,exception);
1571 status=QueryColorCompliance("#0000",AllCompliance,
1572 &clip_mask->background_color,exception);
1573 clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1574 clip_mask->background_color.alpha_trait=BlendPixelTrait;
1575 status=SetImageBackgroundColor(clip_mask,exception);
1576 if (image->debug != MagickFalse)
1577 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1579 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1580 (void) CloneString(&clone_info->primitive,clip_path);
1581 status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1583 if (clone_info->clip_mask != (char *) NULL)
1584 clone_info->clip_mask=DestroyString(clone_info->clip_mask);
1585 status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1587 clone_info->stroke_width=0.0;
1588 clone_info->alpha=OpaqueAlpha;
1589 clone_info->clip_path=MagickTrue;
1590 status=RenderMVGContent(clip_mask,clone_info,1,exception);
1591 clone_info=DestroyDrawInfo(clone_info);
1592 if (status != MagickFalse)
1594 status=SetImageMask(clip_mask,CompositePixelMask,(Image *) NULL,
1596 if (status != MagickFalse)
1598 separate_mask=SeparateImage(clip_mask,AlphaChannel,exception);
1599 if (separate_mask != (Image *) NULL)
1601 clip_mask=DestroyImage(clip_mask);
1602 clip_mask=separate_mask;
1603 status=NegateImage(clip_mask,MagickFalse,exception);
1607 if (image->debug != MagickFalse)
1608 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1609 if (status == MagickFalse)
1610 clip_mask=DestroyImage(clip_mask);
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 % D r a w C o m p o s i t e M a s k %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 % DrawCompositeMask() draws the mask path and returns it as an image mask.
1627 % The format of the DrawCompositeMask method is:
1629 % Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1630 % const char *id,const char *mask_path,ExceptionInfo *exception)
1632 % A description of each parameter follows:
1634 % o image: the image.
1636 % o draw_info: the draw info.
1638 % o id: the mask path id.
1640 % o mask_path: the mask path.
1642 % o exception: return any errors or warnings in this structure.
1645 static Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1646 const char *id,const char *mask_path,ExceptionInfo *exception)
1661 assert(image != (Image *) NULL);
1662 assert(image->signature == MagickCoreSignature);
1663 if (image->debug != MagickFalse)
1664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1665 assert(draw_info != (const DrawInfo *) NULL);
1666 composite_mask=AcquireImage((const ImageInfo *) NULL,exception);
1667 status=SetImageExtent(composite_mask,image->columns,image->rows,exception);
1668 if (status == MagickFalse)
1669 return(DestroyImage(composite_mask));
1670 status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
1672 status=QueryColorCompliance("#0000",AllCompliance,
1673 &composite_mask->background_color,exception);
1674 composite_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1675 composite_mask->background_color.alpha_trait=BlendPixelTrait;
1676 (void) SetImageBackgroundColor(composite_mask,exception);
1677 if (image->debug != MagickFalse)
1678 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin mask-path %s",
1680 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1681 (void) CloneString(&clone_info->primitive,mask_path);
1682 status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1684 status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1686 clone_info->stroke_width=0.0;
1687 clone_info->alpha=OpaqueAlpha;
1688 status=RenderMVGContent(composite_mask,clone_info,1,exception);
1689 clone_info=DestroyDrawInfo(clone_info);
1690 if (status != MagickFalse)
1692 status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
1694 if (status != MagickFalse)
1696 separate_mask=SeparateImage(composite_mask,AlphaChannel,exception);
1697 if (separate_mask != (Image *) NULL)
1699 composite_mask=DestroyImage(composite_mask);
1700 composite_mask=separate_mask;
1701 status=NegateImage(composite_mask,MagickFalse,exception);
1705 if (image->debug != MagickFalse)
1706 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end mask-path");
1707 if (status == MagickFalse)
1708 composite_mask=DestroyImage(composite_mask);
1709 return(composite_mask);
1713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717 + D r a w D a s h P o l y g o n %
1721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1723 % DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1724 % image while respecting the dash offset and dash pattern attributes.
1726 % The format of the DrawDashPolygon method is:
1728 % MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1729 % const PrimitiveInfo *primitive_info,Image *image,
1730 % ExceptionInfo *exception)
1732 % A description of each parameter follows:
1734 % o draw_info: the draw info.
1736 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1738 % o image: the image.
1740 % o exception: return any errors or warnings in this structure.
1743 static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1744 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
1776 assert(draw_info != (const DrawInfo *) NULL);
1777 if (image->debug != MagickFalse)
1778 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1779 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
1780 number_vertices=(size_t) i;
1781 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1782 (2UL*number_vertices+32UL),sizeof(*dash_polygon));
1783 if (dash_polygon == (PrimitiveInfo *) NULL)
1784 return(MagickFalse);
1785 (void) memset(dash_polygon,0,(2UL*number_vertices+32UL)*
1786 sizeof(*dash_polygon));
1787 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1788 clone_info->miterlimit=0;
1789 dash_polygon[0]=primitive_info[0];
1790 scale=ExpandAffine(&draw_info->affine);
1791 length=scale*draw_info->dash_pattern[0];
1792 offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
1793 scale*draw_info->dash_offset : 0.0;
1795 for (n=0; offset > 0.0; j=0)
1797 if (draw_info->dash_pattern[n] <= 0.0)
1799 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1800 if (offset > length)
1804 length=scale*draw_info->dash_pattern[n];
1807 if (offset < length)
1819 for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
1821 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1822 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1823 maximum_length=hypot(dx,dy);
1824 if (maximum_length > MaxBezierCoordinates)
1826 if (fabs(length) < MagickEpsilon)
1829 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1831 length=scale*draw_info->dash_pattern[n];
1833 for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
1835 total_length+=length;
1836 if ((n & 0x01) != 0)
1838 dash_polygon[0]=primitive_info[0];
1839 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1840 total_length*PerceptibleReciprocal(maximum_length));
1841 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1842 total_length*PerceptibleReciprocal(maximum_length));
1847 if ((j+1) > (ssize_t) number_vertices)
1849 dash_polygon[j]=primitive_info[i-1];
1850 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1851 total_length*PerceptibleReciprocal(maximum_length));
1852 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1853 total_length*PerceptibleReciprocal(maximum_length));
1854 dash_polygon[j].coordinates=1;
1856 dash_polygon[0].coordinates=(size_t) j;
1857 dash_polygon[j].primitive=UndefinedPrimitive;
1858 status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
1861 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1863 length=scale*draw_info->dash_pattern[n];
1865 length-=(maximum_length-total_length);
1866 if ((n & 0x01) != 0)
1868 dash_polygon[j]=primitive_info[i];
1869 dash_polygon[j].coordinates=1;
1872 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
1874 dash_polygon[j]=primitive_info[i-1];
1875 dash_polygon[j].point.x+=MagickEpsilon;
1876 dash_polygon[j].point.y+=MagickEpsilon;
1877 dash_polygon[j].coordinates=1;
1879 dash_polygon[0].coordinates=(size_t) j;
1880 dash_polygon[j].primitive=UndefinedPrimitive;
1881 status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
1883 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1884 clone_info=DestroyDrawInfo(clone_info);
1885 if (image->debug != MagickFalse)
1886 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1887 return(status != 0 ? MagickTrue : MagickFalse);
1891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1895 % D r a w G r a d i e n t I m a g e %
1899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1901 % DrawGradientImage() draws a linear gradient on the image.
1903 % The format of the DrawGradientImage method is:
1905 % MagickBooleanType DrawGradientImage(Image *image,
1906 % const DrawInfo *draw_info,ExceptionInfo *exception)
1908 % A description of each parameter follows:
1910 % o image: the image.
1912 % o draw_info: the draw info.
1914 % o exception: return any errors or warnings in this structure.
1918 static inline double GetStopColorOffset(const GradientInfo *gradient,
1919 const ssize_t x,const ssize_t y)
1921 switch (gradient->type)
1923 case UndefinedGradient:
1924 case LinearGradient:
1939 gradient_vector=(&gradient->gradient_vector);
1940 p.x=gradient_vector->x2-gradient_vector->x1;
1941 p.y=gradient_vector->y2-gradient_vector->y1;
1942 q.x=(double) x-gradient_vector->x1;
1943 q.y=(double) y-gradient_vector->y1;
1944 length=sqrt(q.x*q.x+q.y*q.y);
1945 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
1946 gamma=PerceptibleReciprocal(gamma);
1947 scale=p.x*q.x+p.y*q.y;
1948 offset=gamma*scale*length;
1951 case RadialGradient:
1956 if (gradient->spread == RepeatSpread)
1958 v.x=(double) x-gradient->center.x;
1959 v.y=(double) y-gradient->center.y;
1960 return(sqrt(v.x*v.x+v.y*v.y));
1962 v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
1963 gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
1964 gradient->angle))))*PerceptibleReciprocal(gradient->radii.x);
1965 v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
1966 gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
1967 gradient->angle))))*PerceptibleReciprocal(gradient->radii.y);
1968 return(sqrt(v.x*v.x+v.y*v.y));
1974 static int StopInfoCompare(const void *x,const void *y)
1980 stop_1=(StopInfo *) x;
1981 stop_2=(StopInfo *) y;
1982 if (stop_1->offset > stop_2->offset)
1984 if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
1989 MagickExport MagickBooleanType DrawGradientImage(Image *image,
1990 const DrawInfo *draw_info,ExceptionInfo *exception)
2020 Draw linear or radial gradient on image.
2022 assert(image != (Image *) NULL);
2023 assert(image->signature == MagickCoreSignature);
2024 if (image->debug != MagickFalse)
2025 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2026 assert(draw_info != (const DrawInfo *) NULL);
2027 gradient=(&draw_info->gradient);
2028 qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
2030 gradient_vector=(&gradient->gradient_vector);
2031 point.x=gradient_vector->x2-gradient_vector->x1;
2032 point.y=gradient_vector->y2-gradient_vector->y1;
2033 length=sqrt(point.x*point.x+point.y*point.y);
2034 bounding_box=gradient->bounding_box;
2036 GetPixelInfo(image,&zero);
2037 image_view=AcquireAuthenticCacheView(image,exception);
2038 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2039 #pragma omp parallel for schedule(static) shared(status) \
2040 magick_number_threads(image,image,bounding_box.height-bounding_box.y,1)
2042 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
2062 if (status == MagickFalse)
2064 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2065 if (q == (Quantum *) NULL)
2072 offset=GetStopColorOffset(gradient,0,y);
2073 if (gradient->type != RadialGradient)
2074 offset*=PerceptibleReciprocal(length);
2075 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
2077 GetPixelInfoPixel(image,q,&pixel);
2078 switch (gradient->spread)
2080 case UndefinedSpread:
2083 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
2084 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
2086 offset=GetStopColorOffset(gradient,x,y);
2087 if (gradient->type != RadialGradient)
2088 offset*=PerceptibleReciprocal(length);
2090 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2091 if (offset < gradient->stops[i].offset)
2093 if ((offset < 0.0) || (i == 0))
2094 composite=gradient->stops[0].color;
2096 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
2097 composite=gradient->stops[gradient->number_stops-1].color;
2102 alpha=(offset-gradient->stops[i].offset)/
2103 (gradient->stops[j].offset-gradient->stops[i].offset);
2104 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2105 &gradient->stops[j].color,alpha,&composite);
2111 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
2112 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
2114 offset=GetStopColorOffset(gradient,x,y);
2115 if (gradient->type != RadialGradient)
2116 offset*=PerceptibleReciprocal(length);
2120 if ((ssize_t) fmod(offset,2.0) == 0)
2121 offset=fmod(offset,1.0);
2123 offset=1.0-fmod(offset,1.0);
2124 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2125 if (offset < gradient->stops[i].offset)
2128 composite=gradient->stops[0].color;
2130 if (i == (ssize_t) gradient->number_stops)
2131 composite=gradient->stops[gradient->number_stops-1].color;
2136 alpha=(offset-gradient->stops[i].offset)/
2137 (gradient->stops[j].offset-gradient->stops[i].offset);
2138 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2139 &gradient->stops[j].color,alpha,&composite);
2151 antialias=MagickFalse;
2153 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
2154 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
2156 offset=GetStopColorOffset(gradient,x,y);
2157 if (gradient->type == LinearGradient)
2159 repeat=fmod(offset,length);
2161 repeat=length-fmod(-repeat,length);
2163 repeat=fmod(offset,length);
2164 antialias=(repeat < length) && ((repeat+1.0) > length) ?
2165 MagickTrue : MagickFalse;
2166 offset=PerceptibleReciprocal(length)*repeat;
2170 repeat=fmod(offset,gradient->radius);
2172 repeat=gradient->radius-fmod(-repeat,gradient->radius);
2174 repeat=fmod(offset,gradient->radius);
2175 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
2177 offset=repeat/gradient->radius;
2180 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2181 if (offset < gradient->stops[i].offset)
2184 composite=gradient->stops[0].color;
2186 if (i == (ssize_t) gradient->number_stops)
2187 composite=gradient->stops[gradient->number_stops-1].color;
2192 alpha=(offset-gradient->stops[i].offset)/
2193 (gradient->stops[j].offset-gradient->stops[i].offset);
2194 if (antialias != MagickFalse)
2196 if (gradient->type == LinearGradient)
2197 alpha=length-repeat;
2199 alpha=gradient->radius-repeat;
2201 j=(ssize_t) gradient->number_stops-1L;
2203 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2204 &gradient->stops[j].color,alpha,&composite);
2209 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
2211 SetPixelViaPixelInfo(image,&pixel,q);
2212 q+=GetPixelChannels(image);
2214 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2217 image_view=DestroyCacheView(image_view);
2222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2226 % D r a w I m a g e %
2230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2232 % DrawImage() draws a graphic primitive on your image. The primitive
2233 % may be represented as a string or filename. Precede the filename with an
2234 % "at" sign (@) and the contents of the file are drawn on the image. You
2235 % can affect how text is drawn by setting one or more members of the draw
2238 % The format of the DrawImage method is:
2240 % MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
2241 % ExceptionInfo *exception)
2243 % A description of each parameter follows:
2245 % o image: the image.
2247 % o draw_info: the draw info.
2249 % o exception: return any errors or warnings in this structure.
2253 static MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
2263 Check if there is enough storage for drawing pimitives.
2265 extent=(double) mvg_info->offset+pad+PrimitiveExtentPad;
2266 quantum=sizeof(**mvg_info->primitive_info);
2267 if (((extent*quantum) < (double) SSIZE_MAX) &&
2268 ((extent*quantum) < (double) GetMaxMemoryRequest()))
2270 if (extent <= (double) *mvg_info->extent)
2272 *mvg_info->primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
2273 *mvg_info->primitive_info,(size_t) extent,quantum);
2274 if (*mvg_info->primitive_info != (PrimitiveInfo *) NULL)
2276 (void) memset(*mvg_info->primitive_info+*mvg_info->extent,0,
2277 (extent-(*mvg_info->extent))*quantum);
2278 *mvg_info->extent=(size_t) extent;
2283 Reallocation failed, allocate a primitive to facilitate unwinding.
2285 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
2286 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2287 if (*mvg_info->primitive_info != (PrimitiveInfo *) NULL)
2288 *mvg_info->primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(
2289 *mvg_info->primitive_info);
2290 *mvg_info->primitive_info=(PrimitiveInfo *) AcquireCriticalMemory(
2291 PrimitiveExtentPad*quantum);
2292 (void) memset(*mvg_info->primitive_info,0,PrimitiveExtentPad*quantum);
2293 *mvg_info->extent=1;
2294 return(MagickFalse);
2297 static SplayTreeInfo *GetMVGMacros(const char *primitive)
2312 Scan graphic primitives for definitions and classes.
2314 if (primitive == (const char *) NULL)
2315 return((SplayTreeInfo *) NULL);
2316 macros=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
2317 RelinquishMagickMemory);
2318 token=AcquireString(primitive);
2319 extent=strlen(token)+MagickPathExtent;
2320 for (q=primitive; *q != '\0'; )
2322 GetNextToken(q,&q,extent,token);
2325 if (LocaleCompare("push",token) == 0)
2331 GetNextToken(q,&q,extent,token);
2335 name[MagickPathExtent];
2344 Named macro (e.g. push graphic-context "wheel").
2346 GetNextToken(q,&q,extent,token);
2349 (void) CopyMagickString(name,token,MagickPathExtent);
2351 for (p=q; *p != '\0'; )
2353 GetNextToken(p,&p,extent,token);
2356 if (LocaleCompare(token,"pop") == 0)
2358 end=p-strlen(token)-1;
2361 if (LocaleCompare(token,"push") == 0)
2363 if ((n == 0) && (end > start))
2371 GetNextToken(p,&p,extent,token);
2372 macro=AcquireString(start);
2373 macro[end-start]='\0';
2374 (void) AddValueToSplayTree(macros,ConstantString(name),
2375 ConstantString(macro));
2376 macro=DestroyString(macro);
2383 token=DestroyString(token);
2387 static inline MagickBooleanType IsPoint(const char *point)
2395 value=StringToDouble(point,&p);
2396 return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
2400 static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
2401 const PointInfo point)
2403 primitive_info->coordinates=1;
2404 primitive_info->closed_subpath=MagickFalse;
2405 primitive_info->point=point;
2409 static MagickBooleanType RenderMVGContent(Image *image,
2410 const DrawInfo *draw_info,const size_t depth,ExceptionInfo *exception)
2412 #define RenderImageTag "Render/Image"
2419 keyword[MagickPathExtent],
2420 geometry[MagickPathExtent],
2422 pattern[MagickPathExtent],
2489 assert(image != (Image *) NULL);
2490 assert(image->signature == MagickCoreSignature);
2491 if (image->debug != MagickFalse)
2492 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2493 assert(draw_info != (DrawInfo *) NULL);
2494 assert(draw_info->signature == MagickCoreSignature);
2495 if (image->debug != MagickFalse)
2496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2497 if (depth > MagickMaxRecursionDepth)
2498 ThrowBinaryException(DrawError,"VectorGraphicsNestedTooDeeply",
2500 if ((draw_info->primitive == (char *) NULL) ||
2501 (*draw_info->primitive == '\0'))
2502 return(MagickFalse);
2503 if (image->debug != MagickFalse)
2504 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
2505 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2506 return(MagickFalse);
2507 if (image->alpha_trait == UndefinedPixelTrait)
2509 status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2510 if (status == MagickFalse)
2511 return(status != 0 ? MagickTrue : MagickFalse);
2513 primitive=(char *) NULL;
2514 if (*draw_info->primitive != '@')
2515 primitive=AcquireString(draw_info->primitive);
2517 if ((strlen(draw_info->primitive) > 1) &&
2518 (*(draw_info->primitive+1) != '-'))
2519 primitive=FileToString(draw_info->primitive+1,~0UL,exception);
2520 if (primitive == (char *) NULL)
2521 return(MagickFalse);
2522 primitive_extent=(double) strlen(primitive);
2523 (void) SetImageArtifact(image,"MVG",primitive);
2526 stops=(StopInfo *) NULL;
2528 Allocate primitive info memory.
2530 graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
2531 if (graphic_context == (DrawInfo **) NULL)
2533 primitive=DestroyString(primitive);
2534 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2537 number_points=PrimitiveExtentPad;
2538 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
2539 sizeof(*primitive_info));
2540 if (primitive_info == (PrimitiveInfo *) NULL)
2542 primitive=DestroyString(primitive);
2543 for ( ; n >= 0; n--)
2544 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2545 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
2546 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2549 (void) memset(primitive_info,0,(size_t) number_points*
2550 sizeof(*primitive_info));
2551 mvg_info.primitive_info=(&primitive_info);
2552 mvg_info.extent=(&number_points);
2554 mvg_info.exception=exception;
2555 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2556 graphic_context[n]->viewbox=image->page;
2557 if ((image->page.width == 0) || (image->page.height == 0))
2559 graphic_context[n]->viewbox.width=image->columns;
2560 graphic_context[n]->viewbox.height=image->rows;
2562 token=AcquireString(primitive);
2563 extent=strlen(token)+MagickPathExtent;
2567 macros=GetMVGMacros(primitive);
2569 for (q=primitive; *q != '\0'; )
2572 Interpret graphic primitive.
2574 GetNextToken(q,&q,MagickPathExtent,keyword);
2575 if (*keyword == '\0')
2577 if (*keyword == '#')
2582 while ((*q != '\n') && (*q != '\0'))
2586 p=q-strlen(keyword)-1;
2587 primitive_type=UndefinedPrimitive;
2588 current=graphic_context[n]->affine;
2589 GetAffineMatrix(&affine);
2597 if (LocaleCompare("affine",keyword) == 0)
2599 GetNextToken(q,&q,extent,token);
2600 affine.sx=StringToDouble(token,&next_token);
2601 if (token == next_token)
2602 ThrowPointExpectedException(token,exception);
2603 GetNextToken(q,&q,extent,token);
2605 GetNextToken(q,&q,extent,token);
2606 affine.rx=StringToDouble(token,&next_token);
2607 if (token == next_token)
2608 ThrowPointExpectedException(token,exception);
2609 GetNextToken(q,&q,extent,token);
2611 GetNextToken(q,&q,extent,token);
2612 affine.ry=StringToDouble(token,&next_token);
2613 if (token == next_token)
2614 ThrowPointExpectedException(token,exception);
2615 GetNextToken(q,&q,extent,token);
2617 GetNextToken(q,&q,extent,token);
2618 affine.sy=StringToDouble(token,&next_token);
2619 if (token == next_token)
2620 ThrowPointExpectedException(token,exception);
2621 GetNextToken(q,&q,extent,token);
2623 GetNextToken(q,&q,extent,token);
2624 affine.tx=StringToDouble(token,&next_token);
2625 if (token == next_token)
2626 ThrowPointExpectedException(token,exception);
2627 GetNextToken(q,&q,extent,token);
2629 GetNextToken(q,&q,extent,token);
2630 affine.ty=StringToDouble(token,&next_token);
2631 if (token == next_token)
2632 ThrowPointExpectedException(token,exception);
2635 if (LocaleCompare("alpha",keyword) == 0)
2637 primitive_type=AlphaPrimitive;
2640 if (LocaleCompare("arc",keyword) == 0)
2642 primitive_type=ArcPrimitive;
2651 if (LocaleCompare("bezier",keyword) == 0)
2653 primitive_type=BezierPrimitive;
2656 if (LocaleCompare("border-color",keyword) == 0)
2658 GetNextToken(q,&q,extent,token);
2659 status&=QueryColorCompliance(token,AllCompliance,
2660 &graphic_context[n]->border_color,exception);
2669 if (LocaleCompare("class",keyword) == 0)
2674 GetNextToken(q,&q,extent,token);
2680 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
2681 if (mvg_class != (const char *) NULL)
2690 Inject class elements in stream.
2692 offset=(ssize_t) (p-primitive);
2693 elements=AcquireString(primitive);
2694 elements[offset]='\0';
2695 (void) ConcatenateString(&elements,mvg_class);
2696 (void) ConcatenateString(&elements,"\n");
2697 (void) ConcatenateString(&elements,q);
2698 primitive=DestroyString(primitive);
2704 if (LocaleCompare("clip-path",keyword) == 0)
2710 Take a node from within the MVG document, and duplicate it here.
2712 GetNextToken(q,&q,extent,token);
2718 (void) CloneString(&graphic_context[n]->clip_mask,token);
2719 clip_path=(const char *) GetValueFromSplayTree(macros,token);
2720 if (clip_path != (const char *) NULL)
2722 if (graphic_context[n]->clipping_mask != (Image *) NULL)
2723 graphic_context[n]->clipping_mask=
2724 DestroyImage(graphic_context[n]->clipping_mask);
2725 graphic_context[n]->clipping_mask=DrawClippingMask(image,
2726 graphic_context[n],token,clip_path,exception);
2727 if (draw_info->compliance != SVGCompliance)
2728 status&=DrawClipPath(image,graphic_context[n],
2729 graphic_context[n]->clip_mask,exception);
2733 if (LocaleCompare("clip-rule",keyword) == 0)
2738 GetNextToken(q,&q,extent,token);
2739 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2741 if (fill_rule == -1)
2746 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2749 if (LocaleCompare("clip-units",keyword) == 0)
2754 GetNextToken(q,&q,extent,token);
2755 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
2757 if (clip_units == -1)
2762 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
2763 if (clip_units == ObjectBoundingBox)
2765 GetAffineMatrix(¤t);
2766 affine.sx=draw_info->bounds.x2;
2767 affine.sy=draw_info->bounds.y2;
2768 affine.tx=draw_info->bounds.x1;
2769 affine.ty=draw_info->bounds.y1;
2774 if (LocaleCompare("circle",keyword) == 0)
2776 primitive_type=CirclePrimitive;
2779 if (LocaleCompare("color",keyword) == 0)
2781 primitive_type=ColorPrimitive;
2784 if (LocaleCompare("compliance",keyword) == 0)
2787 MVG compliance associates a clipping mask with an image; SVG
2788 compliance associates a clipping mask with a graphics context.
2790 GetNextToken(q,&q,extent,token);
2791 graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
2792 MagickComplianceOptions,MagickFalse,token);
2801 if (LocaleCompare("decorate",keyword) == 0)
2806 GetNextToken(q,&q,extent,token);
2807 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
2814 graphic_context[n]->decorate=(DecorationType) decorate;
2817 if (LocaleCompare("density",keyword) == 0)
2819 GetNextToken(q,&q,extent,token);
2820 (void) CloneString(&graphic_context[n]->density,token);
2823 if (LocaleCompare("direction",keyword) == 0)
2828 GetNextToken(q,&q,extent,token);
2829 direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
2831 if (direction == -1)
2834 graphic_context[n]->direction=(DirectionType) direction;
2843 if (LocaleCompare("ellipse",keyword) == 0)
2845 primitive_type=EllipsePrimitive;
2848 if (LocaleCompare("encoding",keyword) == 0)
2850 GetNextToken(q,&q,extent,token);
2851 (void) CloneString(&graphic_context[n]->encoding,token);
2860 if (LocaleCompare("fill",keyword) == 0)
2862 GetNextToken(q,&q,extent,token);
2863 if (graphic_context[n]->clip_path != MagickFalse)
2865 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
2866 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2867 (void) DrawPatternPath(image,draw_info,token,
2868 &graphic_context[n]->fill_pattern,exception);
2871 status&=QueryColorCompliance(token,AllCompliance,
2872 &graphic_context[n]->fill,exception);
2873 if (graphic_context[n]->fill_alpha != OpaqueAlpha)
2874 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
2878 if (LocaleCompare("fill-opacity",keyword) == 0)
2883 GetNextToken(q,&q,extent,token);
2884 if (graphic_context[n]->clip_path != MagickFalse)
2886 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
2887 opacity=MagickMin(MagickMax(factor*
2888 StringToDouble(token,&next_token),0.0),1.0);
2889 if (token == next_token)
2890 ThrowPointExpectedException(token,exception);
2891 graphic_context[n]->fill_alpha=opacity;
2892 if (graphic_context[n]->fill_alpha != OpaqueAlpha)
2893 graphic_context[n]->fill.alpha=ClampToQuantum(QuantumRange*
2894 graphic_context[n]->fill_alpha);
2897 if (LocaleCompare("fill-rule",keyword) == 0)
2902 GetNextToken(q,&q,extent,token);
2903 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2905 if (fill_rule == -1)
2910 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2913 if (LocaleCompare("font",keyword) == 0)
2915 GetNextToken(q,&q,extent,token);
2916 (void) CloneString(&graphic_context[n]->font,token);
2917 if (LocaleCompare("none",token) == 0)
2918 graphic_context[n]->font=(char *) RelinquishMagickMemory(
2919 graphic_context[n]->font);
2922 if (LocaleCompare("font-family",keyword) == 0)
2924 GetNextToken(q,&q,extent,token);
2925 (void) CloneString(&graphic_context[n]->family,token);
2928 if (LocaleCompare("font-size",keyword) == 0)
2930 GetNextToken(q,&q,extent,token);
2931 graphic_context[n]->pointsize=StringToDouble(token,&next_token);
2932 if (token == next_token)
2933 ThrowPointExpectedException(token,exception);
2936 if (LocaleCompare("font-stretch",keyword) == 0)
2941 GetNextToken(q,&q,extent,token);
2942 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
2948 graphic_context[n]->stretch=(StretchType) stretch;
2951 if (LocaleCompare("font-style",keyword) == 0)
2956 GetNextToken(q,&q,extent,token);
2957 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
2963 graphic_context[n]->style=(StyleType) style;
2966 if (LocaleCompare("font-weight",keyword) == 0)
2971 GetNextToken(q,&q,extent,token);
2972 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
2974 weight=(ssize_t) StringToUnsignedLong(token);
2975 graphic_context[n]->weight=(size_t) weight;
2984 if (LocaleCompare("gradient-units",keyword) == 0)
2986 GetNextToken(q,&q,extent,token);
2989 if (LocaleCompare("gravity",keyword) == 0)
2994 GetNextToken(q,&q,extent,token);
2995 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
3001 graphic_context[n]->gravity=(GravityType) gravity;
3010 if (LocaleCompare("image",keyword) == 0)
3015 primitive_type=ImagePrimitive;
3016 GetNextToken(q,&q,extent,token);
3017 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
3023 graphic_context[n]->compose=(CompositeOperator) compose;
3026 if (LocaleCompare("interline-spacing",keyword) == 0)
3028 GetNextToken(q,&q,extent,token);
3029 graphic_context[n]->interline_spacing=StringToDouble(token,
3031 if (token == next_token)
3032 ThrowPointExpectedException(token,exception);
3035 if (LocaleCompare("interword-spacing",keyword) == 0)
3037 GetNextToken(q,&q,extent,token);
3038 graphic_context[n]->interword_spacing=StringToDouble(token,
3040 if (token == next_token)
3041 ThrowPointExpectedException(token,exception);
3050 if (LocaleCompare("kerning",keyword) == 0)
3052 GetNextToken(q,&q,extent,token);
3053 graphic_context[n]->kerning=StringToDouble(token,&next_token);
3054 if (token == next_token)
3055 ThrowPointExpectedException(token,exception);
3064 if (LocaleCompare("letter-spacing",keyword) == 0)
3066 GetNextToken(q,&q,extent,token);
3067 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3068 clone_info->text=AcquireString(" ");
3069 status&=GetTypeMetrics(image,clone_info,&metrics,exception);
3070 graphic_context[n]->kerning=metrics.width*
3071 StringToDouble(token,&next_token);
3072 clone_info=DestroyDrawInfo(clone_info);
3073 if (token == next_token)
3074 ThrowPointExpectedException(token,exception);
3077 if (LocaleCompare("line",keyword) == 0)
3079 primitive_type=LinePrimitive;
3088 if (LocaleCompare("mask",keyword) == 0)
3094 Take a node from within the MVG document, and duplicate it here.
3096 GetNextToken(q,&q,extent,token);
3097 mask_path=(const char *) GetValueFromSplayTree(macros,token);
3098 if (mask_path != (const char *) NULL)
3100 if (graphic_context[n]->composite_mask != (Image *) NULL)
3101 graphic_context[n]->composite_mask=
3102 DestroyImage(graphic_context[n]->composite_mask);
3103 graphic_context[n]->composite_mask=DrawCompositeMask(image,
3104 graphic_context[n],token,mask_path,exception);
3105 if (draw_info->compliance != SVGCompliance)
3106 status=SetImageMask(image,CompositePixelMask,
3107 graphic_context[n]->composite_mask,exception);
3116 if (LocaleCompare("offset",keyword) == 0)
3118 GetNextToken(q,&q,extent,token);
3121 if (LocaleCompare("opacity",keyword) == 0)
3126 GetNextToken(q,&q,extent,token);
3127 if (graphic_context[n]->clip_path != MagickFalse)
3129 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3130 opacity=MagickMin(MagickMax(factor*
3131 StringToDouble(token,&next_token),0.0),1.0);
3132 if (token == next_token)
3133 ThrowPointExpectedException(token,exception);
3134 graphic_context[n]->fill_alpha*=opacity;
3135 if (graphic_context[n]->fill_alpha != OpaqueAlpha)
3136 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3137 graphic_context[n]->stroke_alpha*=opacity;
3138 if (graphic_context[n]->stroke_alpha != OpaqueAlpha)
3139 graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3148 if (LocaleCompare("path",keyword) == 0)
3150 primitive_type=PathPrimitive;
3153 if (LocaleCompare("point",keyword) == 0)
3155 primitive_type=PointPrimitive;
3158 if (LocaleCompare("polyline",keyword) == 0)
3160 primitive_type=PolylinePrimitive;
3163 if (LocaleCompare("polygon",keyword) == 0)
3165 primitive_type=PolygonPrimitive;
3168 if (LocaleCompare("pop",keyword) == 0)
3170 GetNextToken(q,&q,extent,token);
3171 if (LocaleCompare("class",token) == 0)
3173 if (LocaleCompare("clip-path",token) == 0)
3175 if (LocaleCompare("defs",token) == 0)
3178 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3182 if (LocaleCompare("gradient",token) == 0)
3184 if (LocaleCompare("graphic-context",token) == 0)
3188 (void) ThrowMagickException(exception,GetMagickModule(),
3189 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
3194 if ((graphic_context[n]->clip_mask != (char *) NULL) &&
3195 (draw_info->compliance != SVGCompliance))
3196 if (LocaleCompare(graphic_context[n]->clip_mask,
3197 graphic_context[n-1]->clip_mask) != 0)
3198 status=SetImageMask(image,WritePixelMask,(Image *) NULL,
3200 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3204 if (LocaleCompare("mask",token) == 0)
3206 if (LocaleCompare("pattern",token) == 0)
3208 if (LocaleCompare("symbol",token) == 0)
3211 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3218 if (LocaleCompare("push",keyword) == 0)
3220 GetNextToken(q,&q,extent,token);
3221 if (LocaleCompare("class",token) == 0)
3226 for (p=q; *q != '\0'; )
3228 GetNextToken(q,&q,extent,token);
3229 if (LocaleCompare(token,"pop") != 0)
3231 GetNextToken(q,(const char **) NULL,extent,token);
3232 if (LocaleCompare(token,"class") != 0)
3236 GetNextToken(q,&q,extent,token);
3239 if (LocaleCompare("clip-path",token) == 0)
3242 name[MaxTextExtent];
3247 GetNextToken(q,&q,extent,token);
3248 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
3249 clip_path=(const char *) GetValueFromSplayTree(macros,name);
3250 if (clip_path != (const char *) NULL)
3251 (void) SetImageArtifact(image,name,clip_path);
3254 if (LocaleCompare("defs",token) == 0)
3257 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3261 if (LocaleCompare("gradient",token) == 0)
3264 key[2*MagickPathExtent],
3265 name[MagickPathExtent],
3266 type[MagickPathExtent];
3271 GetNextToken(q,&q,extent,token);
3272 (void) CopyMagickString(name,token,MagickPathExtent);
3273 GetNextToken(q,&q,extent,token);
3274 (void) CopyMagickString(type,token,MagickPathExtent);
3275 GetNextToken(q,&q,extent,token);
3276 segment.x1=StringToDouble(token,&next_token);
3277 if (token == next_token)
3278 ThrowPointExpectedException(token,exception);
3279 GetNextToken(q,&q,extent,token);
3281 GetNextToken(q,&q,extent,token);
3282 segment.y1=StringToDouble(token,&next_token);
3283 if (token == next_token)
3284 ThrowPointExpectedException(token,exception);
3285 GetNextToken(q,&q,extent,token);
3287 GetNextToken(q,&q,extent,token);
3288 segment.x2=StringToDouble(token,&next_token);
3289 if (token == next_token)
3290 ThrowPointExpectedException(token,exception);
3291 GetNextToken(q,&q,extent,token);
3293 GetNextToken(q,&q,extent,token);
3294 segment.y2=StringToDouble(token,&next_token);
3295 if (token == next_token)
3296 ThrowPointExpectedException(token,exception);
3297 if (LocaleCompare(type,"radial") == 0)
3299 GetNextToken(q,&q,extent,token);
3301 GetNextToken(q,&q,extent,token);
3303 for (p=q; *q != '\0'; )
3305 GetNextToken(q,&q,extent,token);
3306 if (LocaleCompare(token,"pop") != 0)
3308 GetNextToken(q,(const char **) NULL,extent,token);
3309 if (LocaleCompare(token,"gradient") != 0)
3313 if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
3318 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3319 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3320 graphic_context[n]->affine.ry*segment.y1+
3321 graphic_context[n]->affine.tx;
3322 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3323 graphic_context[n]->affine.sy*segment.y1+
3324 graphic_context[n]->affine.ty;
3325 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3326 graphic_context[n]->affine.ry*segment.y2+
3327 graphic_context[n]->affine.tx;
3328 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3329 graphic_context[n]->affine.sy*segment.y2+
3330 graphic_context[n]->affine.ty;
3331 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3332 (void) SetImageArtifact(image,key,token);
3333 (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name);
3334 (void) SetImageArtifact(image,key,type);
3335 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3337 (void) FormatLocaleString(geometry,MagickPathExtent,
3338 "%gx%g%+.15g%+.15g",
3339 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
3340 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
3341 bounds.x1,bounds.y1);
3342 (void) SetImageArtifact(image,key,geometry);
3343 GetNextToken(q,&q,extent,token);
3346 if (LocaleCompare("graphic-context",token) == 0)
3349 graphic_context=(DrawInfo **) ResizeQuantumMemory(
3350 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
3351 if (graphic_context == (DrawInfo **) NULL)
3353 (void) ThrowMagickException(exception,GetMagickModule(),
3354 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3358 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
3359 graphic_context[n-1]);
3361 GetNextToken(q,&q,extent,token);
3364 if (LocaleCompare("mask",token) == 0)
3366 GetNextToken(q,&q,extent,token);
3369 if (LocaleCompare("pattern",token) == 0)
3372 key[2*MagickPathExtent],
3373 name[MagickPathExtent];
3378 GetNextToken(q,&q,extent,token);
3379 (void) CopyMagickString(name,token,MagickPathExtent);
3380 GetNextToken(q,&q,extent,token);
3381 bounds.x=(ssize_t) ceil(StringToDouble(token,&next_token)-0.5);
3382 if (token == next_token)
3383 ThrowPointExpectedException(token,exception);
3384 GetNextToken(q,&q,extent,token);
3386 GetNextToken(q,&q,extent,token);
3387 bounds.y=(ssize_t) ceil(StringToDouble(token,&next_token)-0.5);
3388 if (token == next_token)
3389 ThrowPointExpectedException(token,exception);
3390 GetNextToken(q,&q,extent,token);
3392 GetNextToken(q,&q,extent,token);
3393 bounds.width=(size_t) floor(StringToDouble(token,&next_token)+
3395 if (token == next_token)
3396 ThrowPointExpectedException(token,exception);
3397 GetNextToken(q,&q,extent,token);
3399 GetNextToken(q,&q,extent,token);
3400 bounds.height=(size_t) floor(StringToDouble(token,&next_token)+
3402 if (token == next_token)
3403 ThrowPointExpectedException(token,exception);
3404 for (p=q; *q != '\0'; )
3406 GetNextToken(q,&q,extent,token);
3407 if (LocaleCompare(token,"pop") != 0)
3409 GetNextToken(q,(const char **) NULL,extent,token);
3410 if (LocaleCompare(token,"pattern") != 0)
3414 if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
3419 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3420 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3421 (void) SetImageArtifact(image,key,token);
3422 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3424 (void) FormatLocaleString(geometry,MagickPathExtent,
3425 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
3426 bounds.height,(double) bounds.x,(double) bounds.y);
3427 (void) SetImageArtifact(image,key,geometry);
3428 GetNextToken(q,&q,extent,token);
3431 if (LocaleCompare("symbol",token) == 0)
3434 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3447 if (LocaleCompare("rectangle",keyword) == 0)
3449 primitive_type=RectanglePrimitive;
3452 if (LocaleCompare("rotate",keyword) == 0)
3454 GetNextToken(q,&q,extent,token);
3455 angle=StringToDouble(token,&next_token);
3456 if (token == next_token)
3457 ThrowPointExpectedException(token,exception);
3458 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
3459 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
3460 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
3461 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
3464 if (LocaleCompare("roundRectangle",keyword) == 0)
3466 primitive_type=RoundRectanglePrimitive;
3475 if (LocaleCompare("scale",keyword) == 0)
3477 GetNextToken(q,&q,extent,token);
3478 affine.sx=StringToDouble(token,&next_token);
3479 if (token == next_token)
3480 ThrowPointExpectedException(token,exception);
3481 GetNextToken(q,&q,extent,token);
3483 GetNextToken(q,&q,extent,token);
3484 affine.sy=StringToDouble(token,&next_token);
3485 if (token == next_token)
3486 ThrowPointExpectedException(token,exception);
3489 if (LocaleCompare("skewX",keyword) == 0)
3491 GetNextToken(q,&q,extent,token);
3492 angle=StringToDouble(token,&next_token);
3493 if (token == next_token)
3494 ThrowPointExpectedException(token,exception);
3495 affine.ry=sin(DegreesToRadians(angle));
3498 if (LocaleCompare("skewY",keyword) == 0)
3500 GetNextToken(q,&q,extent,token);
3501 angle=StringToDouble(token,&next_token);
3502 if (token == next_token)
3503 ThrowPointExpectedException(token,exception);
3504 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
3507 if (LocaleCompare("stop-color",keyword) == 0)
3513 if (number_stops == 1)
3514 stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops));
3516 if (number_stops > 2)
3517 stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
3519 if (stops == (StopInfo *) NULL)
3521 (void) ThrowMagickException(exception,GetMagickModule(),
3522 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3526 GetNextToken(q,&q,extent,token);
3527 status&=QueryColorCompliance(token,AllCompliance,&stop_color,
3529 stops[number_stops-1].color=stop_color;
3530 GetNextToken(q,&q,extent,token);
3531 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3532 stops[number_stops-1].offset=factor*StringToDouble(token,
3534 if (token == next_token)
3535 ThrowPointExpectedException(token,exception);
3538 if (LocaleCompare("stroke",keyword) == 0)
3540 GetNextToken(q,&q,extent,token);
3541 if (graphic_context[n]->clip_path != MagickFalse)
3543 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3544 if (GetImageArtifact(image,pattern) != (const char *) NULL)
3545 (void) DrawPatternPath(image,draw_info,token,
3546 &graphic_context[n]->stroke_pattern,exception);
3549 status&=QueryColorCompliance(token,AllCompliance,
3550 &graphic_context[n]->stroke,exception);
3551 if (graphic_context[n]->stroke_alpha != OpaqueAlpha)
3552 graphic_context[n]->stroke.alpha=
3553 graphic_context[n]->stroke_alpha;
3557 if (LocaleCompare("stroke-antialias",keyword) == 0)
3559 GetNextToken(q,&q,extent,token);
3560 graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
3561 MagickTrue : MagickFalse;
3564 if (LocaleCompare("stroke-dasharray",keyword) == 0)
3566 if (graphic_context[n]->dash_pattern != (double *) NULL)
3567 graphic_context[n]->dash_pattern=(double *)
3568 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
3569 if (IsPoint(q) != MagickFalse)
3575 GetNextToken(r,&r,extent,token);
3577 GetNextToken(r,&r,extent,token);
3578 for (x=0; IsPoint(token) != MagickFalse; x++)
3580 GetNextToken(r,&r,extent,token);
3582 GetNextToken(r,&r,extent,token);
3584 graphic_context[n]->dash_pattern=(double *)
3585 AcquireQuantumMemory((size_t) (2*x+4),
3586 sizeof(*graphic_context[n]->dash_pattern));
3587 if (graphic_context[n]->dash_pattern == (double *) NULL)
3589 (void) ThrowMagickException(exception,GetMagickModule(),
3590 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3595 (void) memset(graphic_context[n]->dash_pattern,0,(size_t)
3596 (2*x+4)*sizeof(*graphic_context[n]->dash_pattern));
3597 for (j=0; j < x; j++)
3599 GetNextToken(q,&q,extent,token);
3601 GetNextToken(q,&q,extent,token);
3602 graphic_context[n]->dash_pattern[j]=StringToDouble(token,
3604 if (token == next_token)
3605 ThrowPointExpectedException(token,exception);
3606 if (graphic_context[n]->dash_pattern[j] < 0.0)
3609 if ((x & 0x01) != 0)
3610 for ( ; j < (2*x); j++)
3611 graphic_context[n]->dash_pattern[j]=
3612 graphic_context[n]->dash_pattern[j-x];
3613 graphic_context[n]->dash_pattern[j]=0.0;
3616 GetNextToken(q,&q,extent,token);
3619 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
3621 GetNextToken(q,&q,extent,token);
3622 graphic_context[n]->dash_offset=StringToDouble(token,&next_token);
3623 if (token == next_token)
3624 ThrowPointExpectedException(token,exception);
3627 if (LocaleCompare("stroke-linecap",keyword) == 0)
3632 GetNextToken(q,&q,extent,token);
3633 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
3639 graphic_context[n]->linecap=(LineCap) linecap;
3642 if (LocaleCompare("stroke-linejoin",keyword) == 0)
3647 GetNextToken(q,&q,extent,token);
3648 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
3655 graphic_context[n]->linejoin=(LineJoin) linejoin;
3658 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
3660 GetNextToken(q,&q,extent,token);
3661 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
3664 if (LocaleCompare("stroke-opacity",keyword) == 0)
3669 GetNextToken(q,&q,extent,token);
3670 if (graphic_context[n]->clip_path != MagickFalse)
3672 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3673 opacity=MagickMin(MagickMax(factor*
3674 StringToDouble(token,&next_token),0.0),1.0);
3675 if (token == next_token)
3676 ThrowPointExpectedException(token,exception);
3677 graphic_context[n]->stroke_alpha=opacity;
3678 if (graphic_context[n]->stroke_alpha != OpaqueAlpha)
3679 graphic_context[n]->stroke.alpha=ClampToQuantum(QuantumRange*
3680 graphic_context[n]->stroke_alpha);
3683 if (LocaleCompare("stroke-width",keyword) == 0)
3685 GetNextToken(q,&q,extent,token);
3686 if (graphic_context[n]->clip_path != MagickFalse)
3688 graphic_context[n]->stroke_width=StringToDouble(token,&next_token);
3689 if (token == next_token)
3690 ThrowPointExpectedException(token,exception);
3699 if (LocaleCompare("text",keyword) == 0)
3701 primitive_type=TextPrimitive;
3705 if (LocaleCompare("text-align",keyword) == 0)
3710 GetNextToken(q,&q,extent,token);
3711 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3717 graphic_context[n]->align=(AlignType) align;
3720 if (LocaleCompare("text-anchor",keyword) == 0)
3725 GetNextToken(q,&q,extent,token);
3726 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3732 graphic_context[n]->align=(AlignType) align;
3735 if (LocaleCompare("text-antialias",keyword) == 0)
3737 GetNextToken(q,&q,extent,token);
3738 graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
3739 MagickTrue : MagickFalse;
3742 if (LocaleCompare("text-undercolor",keyword) == 0)
3744 GetNextToken(q,&q,extent,token);
3745 status&=QueryColorCompliance(token,AllCompliance,
3746 &graphic_context[n]->undercolor,exception);
3749 if (LocaleCompare("translate",keyword) == 0)
3751 GetNextToken(q,&q,extent,token);
3752 affine.tx=StringToDouble(token,&next_token);
3753 if (token == next_token)
3754 ThrowPointExpectedException(token,exception);
3755 GetNextToken(q,&q,extent,token);
3757 GetNextToken(q,&q,extent,token);
3758 affine.ty=StringToDouble(token,&next_token);
3759 if (token == next_token)
3760 ThrowPointExpectedException(token,exception);
3770 if (LocaleCompare("use",keyword) == 0)
3776 Get a macro from the MVG document, and "use" it here.
3778 GetNextToken(q,&q,extent,token);
3779 use=(const char *) GetValueFromSplayTree(macros,token);
3780 if (use != (const char *) NULL)
3782 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3783 (void) CloneString(&clone_info->primitive,use);
3784 status=RenderMVGContent(image,clone_info,depth+1,exception);
3785 clone_info=DestroyDrawInfo(clone_info);
3794 if (LocaleCompare("viewbox",keyword) == 0)
3796 GetNextToken(q,&q,extent,token);
3797 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
3799 if (token == next_token)
3800 ThrowPointExpectedException(token,exception);
3801 GetNextToken(q,&q,extent,token);
3803 GetNextToken(q,&q,extent,token);
3804 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
3806 if (token == next_token)
3807 ThrowPointExpectedException(token,exception);
3808 GetNextToken(q,&q,extent,token);
3810 GetNextToken(q,&q,extent,token);
3811 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
3812 token,&next_token)+0.5);
3813 if (token == next_token)
3814 ThrowPointExpectedException(token,exception);
3815 GetNextToken(q,&q,extent,token);
3817 GetNextToken(q,&q,extent,token);
3818 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
3819 token,&next_token)+0.5);
3820 if (token == next_token)
3821 ThrowPointExpectedException(token,exception);
3830 if (LocaleCompare("word-spacing",keyword) == 0)
3832 GetNextToken(q,&q,extent,token);
3833 graphic_context[n]->interword_spacing=StringToDouble(token,
3835 if (token == next_token)
3836 ThrowPointExpectedException(token,exception);
3848 if (status == MagickFalse)
3850 if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
3851 (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
3852 (fabs(affine.sy-1.0) >= MagickEpsilon) ||
3853 (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
3855 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
3856 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
3857 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
3858 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
3859 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
3861 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
3864 if (primitive_type == UndefinedPrimitive)
3868 if (number_stops > 1)
3873 type=LinearGradient;
3874 if (draw_info->gradient.type == RadialGradient)
3875 type=RadialGradient;
3876 (void) GradientImage(image,type,PadSpread,stops,number_stops,
3879 if (number_stops > 0)
3880 stops=(StopInfo *) RelinquishMagickMemory(stops);
3882 if ((image->debug != MagickFalse) && (q > p))
3883 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int)
3888 Parse the primitive attributes.
3893 primitive_info[0].point.x=0.0;
3894 primitive_info[0].point.y=0.0;
3895 primitive_info[0].coordinates=0;
3896 primitive_info[0].method=FloodfillMethod;
3897 primitive_info[0].closed_subpath=MagickFalse;
3898 for (x=0; *q != '\0'; x++)
3903 if (IsPoint(q) == MagickFalse)
3905 GetNextToken(q,&q,extent,token);
3906 point.x=StringToDouble(token,&next_token);
3907 if (token == next_token)
3908 ThrowPointExpectedException(token,exception);
3909 GetNextToken(q,&q,extent,token);
3911 GetNextToken(q,&q,extent,token);
3912 point.y=StringToDouble(token,&next_token);
3913 if (token == next_token)
3914 ThrowPointExpectedException(token,exception);
3915 GetNextToken(q,(const char **) NULL,extent,token);
3917 GetNextToken(q,&q,extent,token);
3918 primitive_info[i].primitive=primitive_type;
3919 primitive_info[i].point=point;
3920 primitive_info[i].coordinates=0;
3921 primitive_info[i].method=FloodfillMethod;
3922 primitive_info[i].closed_subpath=MagickFalse;
3925 if (i < (ssize_t) number_points)
3927 status&=CheckPrimitiveExtent(&mvg_info,number_points);
3929 if (status == MagickFalse)
3931 primitive_info[j].primitive=primitive_type;
3932 primitive_info[j].coordinates=(size_t) x;
3933 primitive_info[j].method=FloodfillMethod;
3934 primitive_info[j].closed_subpath=MagickFalse;
3935 primitive_info[j].text=(char *) NULL;
3937 Circumscribe primitive within a circle.
3939 bounds.x1=primitive_info[j].point.x;
3940 bounds.y1=primitive_info[j].point.y;
3941 bounds.x2=primitive_info[j].point.x;
3942 bounds.y2=primitive_info[j].point.y;
3943 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
3945 point=primitive_info[j+k].point;
3946 if (point.x < bounds.x1)
3948 if (point.y < bounds.y1)
3950 if (point.x > bounds.x2)
3952 if (point.y > bounds.y2)
3956 Speculate how many points our primitive might consume.
3958 coordinates=(double) primitive_info[j].coordinates;
3959 switch (primitive_type)
3961 case RectanglePrimitive:
3966 case RoundRectanglePrimitive:
3973 alpha=bounds.x2-bounds.x1;
3974 beta=bounds.y2-bounds.y1;
3975 radius=hypot((double) alpha,(double) beta);
3977 coordinates+=2.0*((size_t) ceil((double) MagickPI*radius))+6.0*
3978 BezierQuantum+360.0;
3981 case BezierPrimitive:
3983 coordinates=(double) (BezierQuantum*primitive_info[j].coordinates);
3984 if (primitive_info[j].coordinates > (107*BezierQuantum))
3986 (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
3987 "TooManyBezierCoordinates","`%s'",token);
3999 GetNextToken(q,&q,extent,token);
4002 for (s=token; *s != '\0'; s=t)
4007 value=StringToDouble(s,&t);
4016 for (s=token; *s != '\0'; s++)
4017 if (strspn(s,"AaCcQqSsTt") != 0)
4018 coordinates+=(20.0*BezierQuantum)+360.0;
4021 case CirclePrimitive:
4023 case EllipsePrimitive:
4030 alpha=bounds.x2-bounds.x1;
4031 beta=bounds.y2-bounds.y1;
4032 radius=hypot(alpha,beta);
4033 coordinates=2.0*(ceil(MagickPI*radius))+6.0*BezierQuantum+360.0;
4034 if (coordinates > (MaxBezierCoordinates/4))
4036 (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
4037 "TooManyBezierCoordinates","`%s'",token);
4045 if (coordinates > MaxBezierCoordinates)
4047 (void) ThrowMagickException(exception,GetMagickModule(),
4048 ResourceLimitError,"MemoryAllocationFailed","`%s'",token);
4051 if (status == MagickFalse)
4053 if (((size_t) (i+coordinates)) >= number_points)
4056 Resize based on speculative points required by primitive.
4058 number_points+=coordinates+1;
4059 if (number_points < (size_t) coordinates)
4061 (void) ThrowMagickException(exception,GetMagickModule(),
4062 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4067 status&=CheckPrimitiveExtent(&mvg_info,number_points);
4069 status&=CheckPrimitiveExtent(&mvg_info,PrimitiveExtentPad);
4070 if (status == MagickFalse)
4073 switch (primitive_type)
4075 case PointPrimitive:
4078 if (primitive_info[j].coordinates != 1)
4083 status&=TracePoint(primitive_info+j,primitive_info[j].point);
4084 i=(ssize_t) (j+primitive_info[j].coordinates);
4089 if (primitive_info[j].coordinates != 2)
4094 status&=TraceLine(primitive_info+j,primitive_info[j].point,
4095 primitive_info[j+1].point);
4096 i=(ssize_t) (j+primitive_info[j].coordinates);
4099 case RectanglePrimitive:
4101 if (primitive_info[j].coordinates != 2)
4106 status&=TraceRectangle(primitive_info+j,primitive_info[j].point,
4107 primitive_info[j+1].point);
4108 i=(ssize_t) (j+primitive_info[j].coordinates);
4111 case RoundRectanglePrimitive:
4113 if (primitive_info[j].coordinates != 3)
4118 if ((primitive_info[j+2].point.x < 0.0) ||
4119 (primitive_info[j+2].point.y < 0.0))
4124 if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
4129 if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
4134 status&=TraceRoundRectangle(&mvg_info,primitive_info[j].point,
4135 primitive_info[j+1].point,primitive_info[j+2].point);
4136 i=(ssize_t) (j+primitive_info[j].coordinates);
4141 if (primitive_info[j].coordinates != 3)
4143 primitive_type=UndefinedPrimitive;
4146 status&=TraceArc(&mvg_info,primitive_info[j].point,
4147 primitive_info[j+1].point,primitive_info[j+2].point);
4148 i=(ssize_t) (j+primitive_info[j].coordinates);
4151 case EllipsePrimitive:
4153 if (primitive_info[j].coordinates != 3)
4158 if ((primitive_info[j+1].point.x < 0.0) ||
4159 (primitive_info[j+1].point.y < 0.0))
4164 status&=TraceEllipse(&mvg_info,primitive_info[j].point,
4165 primitive_info[j+1].point,primitive_info[j+2].point);
4166 i=(ssize_t) (j+primitive_info[j].coordinates);
4169 case CirclePrimitive:
4171 if (primitive_info[j].coordinates != 2)
4176 status&=TraceCircle(&mvg_info,primitive_info[j].point,
4177 primitive_info[j+1].point);
4178 i=(ssize_t) (j+primitive_info[j].coordinates);
4181 case PolylinePrimitive:
4183 if (primitive_info[j].coordinates < 1)
4190 case PolygonPrimitive:
4192 if (primitive_info[j].coordinates < 3)
4197 primitive_info[i]=primitive_info[j];
4198 primitive_info[i].coordinates=0;
4199 primitive_info[j].coordinates++;
4200 primitive_info[j].closed_subpath=MagickTrue;
4204 case BezierPrimitive:
4206 if (primitive_info[j].coordinates < 3)
4211 status&=TraceBezier(&mvg_info,primitive_info[j].coordinates);
4212 i=(ssize_t) (j+primitive_info[j].coordinates);
4217 coordinates=(double) TracePath(&mvg_info,token,exception);
4218 if (coordinates == 0.0)
4223 i=(ssize_t) (j+coordinates);
4226 case AlphaPrimitive:
4227 case ColorPrimitive:
4232 if (primitive_info[j].coordinates != 1)
4237 GetNextToken(q,&q,extent,token);
4238 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
4244 primitive_info[j].method=(PaintMethod) method;
4250 geometry[MagickPathExtent];
4252 if (primitive_info[j].coordinates != 1)
4258 GetNextToken(q,&q,extent,token);
4259 (void) CloneString(&primitive_info[j].text,token);
4261 Compute text cursor offset.
4263 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4264 if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
4265 (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
4267 mvg_info.point=primitive_info->point;
4268 primitive_info->point.x+=cursor;
4272 mvg_info.point=primitive_info->point;
4275 (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
4276 primitive_info->point.x,primitive_info->point.y);
4277 clone_info->render=MagickFalse;
4278 clone_info->text=AcquireString(token);
4279 status&=GetTypeMetrics(image,clone_info,&metrics,exception);
4280 clone_info=DestroyDrawInfo(clone_info);
4281 cursor+=metrics.width;
4284 case ImagePrimitive:
4286 if (primitive_info[j].coordinates != 2)
4291 GetNextToken(q,&q,extent,token);
4292 (void) CloneString(&primitive_info[j].text,token);
4297 if ((image->debug != MagickFalse) && (q > p))
4298 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p-1),
4300 if (status == MagickFalse)
4302 primitive_info[i].primitive=UndefinedPrimitive;
4308 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4310 point=primitive_info[i].point;
4311 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
4312 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
4313 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
4314 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
4315 point=primitive_info[i].point;
4316 if (point.x < graphic_context[n]->bounds.x1)
4317 graphic_context[n]->bounds.x1=point.x;
4318 if (point.y < graphic_context[n]->bounds.y1)
4319 graphic_context[n]->bounds.y1=point.y;
4320 if (point.x > graphic_context[n]->bounds.x2)
4321 graphic_context[n]->bounds.x2=point.x;
4322 if (point.y > graphic_context[n]->bounds.y2)
4323 graphic_context[n]->bounds.y2=point.y;
4324 if (primitive_info[i].primitive == ImagePrimitive)
4326 if (i >= (ssize_t) number_points)
4327 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
4329 if (graphic_context[n]->render != MagickFalse)
4331 if ((n != 0) && (draw_info->compliance != SVGCompliance) &&
4332 (graphic_context[n]->clip_mask != (char *) NULL) &&
4333 (LocaleCompare(graphic_context[n]->clip_mask,
4334 graphic_context[n-1]->clip_mask) != 0))
4335 status&=DrawClipPath(image,graphic_context[n],
4336 graphic_context[n]->clip_mask,exception);
4337 status&=DrawPrimitive(image,graphic_context[n],primitive_info,
4340 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
4342 if (proceed == MagickFalse)
4347 if (image->debug != MagickFalse)
4348 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
4350 Relinquish resources.
4352 macros=DestroySplayTree(macros);
4353 token=DestroyString(token);
4354 if (primitive_info != (PrimitiveInfo *) NULL)
4356 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4357 if ((primitive_info[i].primitive == TextPrimitive) ||
4358 (primitive_info[i].primitive == ImagePrimitive))
4359 if (primitive_info[i].text != (char *) NULL)
4360 primitive_info[i].text=DestroyString(primitive_info[i].text);
4361 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4363 primitive=DestroyString(primitive);
4364 if (stops != (StopInfo *) NULL)
4365 stops=(StopInfo *) RelinquishMagickMemory(stops);
4366 for ( ; n >= 0; n--)
4367 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
4368 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
4369 if (status == MagickFalse)
4370 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
4372 return(status != 0 ? MagickTrue : MagickFalse);
4375 MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
4376 ExceptionInfo *exception)
4378 return(RenderMVGContent(image,draw_info,1,exception));
4382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4386 % D r a w P a t t e r n P a t h %
4390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4392 % DrawPatternPath() draws a pattern.
4394 % The format of the DrawPatternPath method is:
4396 % MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
4397 % const char *name,Image **pattern,ExceptionInfo *exception)
4399 % A description of each parameter follows:
4401 % o image: the image.
4403 % o draw_info: the draw info.
4405 % o name: the pattern name.
4407 % o image: the image.
4409 % o exception: return any errors or warnings in this structure.
4412 MagickExport MagickBooleanType DrawPatternPath(Image *image,
4413 const DrawInfo *draw_info,const char *name,Image **pattern,
4414 ExceptionInfo *exception)
4417 property[MagickPathExtent];
4433 assert(image != (Image *) NULL);
4434 assert(image->signature == MagickCoreSignature);
4435 if (image->debug != MagickFalse)
4436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4437 assert(draw_info != (const DrawInfo *) NULL);
4438 assert(name != (const char *) NULL);
4439 (void) FormatLocaleString(property,MagickPathExtent,"%s",name);
4440 path=GetImageArtifact(image,property);
4441 if (path == (const char *) NULL)
4442 return(MagickFalse);
4443 (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name);
4444 geometry=GetImageArtifact(image,property);
4445 if (geometry == (const char *) NULL)
4446 return(MagickFalse);
4447 if ((*pattern) != (Image *) NULL)
4448 *pattern=DestroyImage(*pattern);
4449 image_info=AcquireImageInfo();
4450 image_info->size=AcquireString(geometry);
4451 *pattern=AcquireImage(image_info,exception);
4452 image_info=DestroyImageInfo(image_info);
4453 (void) QueryColorCompliance("#000000ff",AllCompliance,
4454 &(*pattern)->background_color,exception);
4455 (void) SetImageBackgroundColor(*pattern,exception);
4456 if (image->debug != MagickFalse)
4457 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4458 "begin pattern-path %s %s",name,geometry);
4459 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4460 clone_info->fill_pattern=NewImageList();
4461 clone_info->stroke_pattern=NewImageList();
4462 (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name);
4463 type=GetImageArtifact(image,property);
4464 if (type != (const char *) NULL)
4465 clone_info->gradient.type=(GradientType) ParseCommandOption(
4466 MagickGradientOptions,MagickFalse,type);
4467 (void) CloneString(&clone_info->primitive,path);
4468 status=RenderMVGContent(*pattern,clone_info,1,exception);
4469 clone_info=DestroyDrawInfo(clone_info);
4470 if (image->debug != MagickFalse)
4471 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
4476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4480 + D r a w P o l y g o n P r i m i t i v e %
4484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4486 % DrawPolygonPrimitive() draws a polygon on the image.
4488 % The format of the DrawPolygonPrimitive method is:
4490 % MagickBooleanType DrawPolygonPrimitive(Image *image,
4491 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4492 % ExceptionInfo *exception)
4494 % A description of each parameter follows:
4496 % o image: the image.
4498 % o draw_info: the draw info.
4500 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4502 % o exception: return any errors or warnings in this structure.
4506 static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
4511 assert(polygon_info != (PolygonInfo **) NULL);
4512 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
4513 if (polygon_info[i] != (PolygonInfo *) NULL)
4514 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
4515 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
4516 return(polygon_info);
4519 static PolygonInfo **AcquirePolygonThreadSet(
4520 const PrimitiveInfo *primitive_info)
4523 *magick_restrict path_info;
4534 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4535 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
4536 sizeof(*polygon_info));
4537 if (polygon_info == (PolygonInfo **) NULL)
4538 return((PolygonInfo **) NULL);
4539 (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
4540 path_info=ConvertPrimitiveToPath(primitive_info);
4541 if (path_info == (PathInfo *) NULL)
4542 return(DestroyPolygonThreadSet(polygon_info));
4543 for (i=0; i < (ssize_t) number_threads; i++)
4545 polygon_info[i]=ConvertPathToPolygon(path_info);
4546 if (polygon_info[i] == (PolygonInfo *) NULL)
4547 return(DestroyPolygonThreadSet(polygon_info));
4549 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
4550 return(polygon_info);
4553 static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
4554 const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
4555 const ssize_t y,double *stroke_alpha)
4566 register const PointInfo
4580 Compute fill & stroke opacity for this (x,y) point.
4584 p=polygon_info->edges;
4585 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4587 if ((double) y <= (p->bounds.y1-mid-0.5))
4589 if ((double) y > (p->bounds.y2+mid+0.5))
4591 (void) DestroyEdge(polygon_info,(size_t) j);
4594 if (((double) x <= (p->bounds.x1-mid-0.5)) ||
4595 ((double) x > (p->bounds.x2+mid+0.5)))
4597 i=(ssize_t) MagickMax((double) p->highwater,1.0);
4598 for ( ; i < (ssize_t) p->number_points; i++)
4600 if ((double) y <= (p->points[i-1].y-mid-0.5))
4602 if ((double) y > (p->points[i].y+mid+0.5))
4604 if (p->scanline != (double) y)
4606 p->scanline=(double) y;
4607 p->highwater=(size_t) i;
4610 Compute distance between a point and an edge.
4613 delta.x=(q+1)->x-q->x;
4614 delta.y=(q+1)->y-q->y;
4615 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
4618 delta.x=(double) x-q->x;
4619 delta.y=(double) y-q->y;
4620 distance=delta.x*delta.x+delta.y*delta.y;
4624 alpha=delta.x*delta.x+delta.y*delta.y;
4627 delta.x=(double) x-(q+1)->x;
4628 delta.y=(double) y-(q+1)->y;
4629 distance=delta.x*delta.x+delta.y*delta.y;
4633 alpha=PerceptibleReciprocal(alpha);
4634 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
4635 distance=alpha*beta*beta;
4639 Compute stroke & subpath opacity.
4642 if (p->ghostline == MagickFalse)
4645 if ((*stroke_alpha < 1.0) &&
4646 (distance <= ((alpha+0.25)*(alpha+0.25))))
4649 if (distance <= ((alpha+0.25)*(alpha+0.25)))
4654 if (fabs(distance-1.0) >= MagickEpsilon)
4655 beta=sqrt((double) distance);
4657 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
4658 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
4662 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
4664 if (distance <= 0.0)
4671 if (fabs(beta) < MagickEpsilon)
4674 if (fabs(distance-1.0) >= MagickEpsilon)
4675 beta=sqrt(distance);
4678 if (subpath_alpha < (alpha*alpha))
4679 subpath_alpha=alpha*alpha;
4683 Compute fill opacity.
4685 if (fill == MagickFalse)
4687 if (subpath_alpha >= 1.0)
4690 Determine winding number.
4693 p=polygon_info->edges;
4694 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4696 if ((double) y <= p->bounds.y1)
4698 if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
4700 if ((double) x > p->bounds.x2)
4702 winding_number+=p->direction ? 1 : -1;
4705 i=(ssize_t) MagickMax((double) p->highwater,1.0);
4706 for ( ; i < (ssize_t) (p->number_points-1); i++)
4707 if ((double) y <= p->points[i].y)
4710 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
4711 winding_number+=p->direction ? 1 : -1;
4713 if (fill_rule != NonZeroRule)
4715 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
4719 if (MagickAbsoluteValue(winding_number) != 0)
4721 return(subpath_alpha);
4724 static MagickBooleanType DrawPolygonPrimitive(Image *image,
4725 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4726 ExceptionInfo *exception)
4739 **magick_restrict polygon_info;
4755 assert(image != (Image *) NULL);
4756 assert(image->signature == MagickCoreSignature);
4757 if (image->debug != MagickFalse)
4758 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4759 assert(draw_info != (DrawInfo *) NULL);
4760 assert(draw_info->signature == MagickCoreSignature);
4761 assert(primitive_info != (PrimitiveInfo *) NULL);
4762 if (primitive_info->coordinates <= 1)
4765 Compute bounding box.
4767 polygon_info=AcquirePolygonThreadSet(primitive_info);
4768 if (polygon_info == (PolygonInfo **) NULL)
4769 return(MagickFalse);
4770 DisableMSCWarning(4127)
4773 status=DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
4774 if (status == MagickFalse)
4776 polygon_info=DestroyPolygonThreadSet(polygon_info);
4781 if (image->debug != MagickFalse)
4782 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
4783 fill=(primitive_info->method == FillToBorderMethod) ||
4784 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
4785 mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
4786 bounds=polygon_info[0]->edges[0].bounds;
4787 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
4789 p=polygon_info[0]->edges+i;
4790 if (p->bounds.x1 < bounds.x1)
4791 bounds.x1=p->bounds.x1;
4792 if (p->bounds.y1 < bounds.y1)
4793 bounds.y1=p->bounds.y1;
4794 if (p->bounds.x2 > bounds.x2)
4795 bounds.x2=p->bounds.x2;
4796 if (p->bounds.y2 > bounds.y2)
4797 bounds.y2=p->bounds.y2;
4799 bounds.x1-=(mid+1.0);
4800 bounds.y1-=(mid+1.0);
4801 bounds.x2+=(mid+1.0);
4802 bounds.y2+=(mid+1.0);
4803 if ((bounds.x1 >= (double) image->columns) ||
4804 (bounds.y1 >= (double) image->rows) ||
4805 (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
4807 polygon_info=DestroyPolygonThreadSet(polygon_info);
4808 return(MagickTrue); /* virtual polygon */
4810 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
4811 (double) image->columns-1.0 : bounds.x1;
4812 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
4813 (double) image->rows-1.0 : bounds.y1;
4814 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
4815 (double) image->columns-1.0 : bounds.x2;
4816 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
4817 (double) image->rows-1.0 : bounds.y2;
4819 image_view=AcquireAuthenticCacheView(image,exception);
4820 if ((primitive_info->coordinates == 1) ||
4821 (polygon_info[0]->number_edges == 0))
4826 start_y=(ssize_t) ceil(bounds.y1-0.5);
4827 stop_y=(ssize_t) floor(bounds.y2+0.5);
4828 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4829 #pragma omp parallel for schedule(static) shared(status) \
4830 magick_number_threads(image,image,stop_y-start_y+1,1)
4832 for (y=start_y; y <= stop_y; y++)
4850 if (status == MagickFalse)
4852 start_x=(ssize_t) ceil(bounds.x1-0.5);
4853 stop_x=(ssize_t) floor(bounds.x2+0.5);
4855 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop_x-x+1),1,
4857 if (q == (Quantum *) NULL)
4862 GetPixelInfo(image,&pixel);
4863 for ( ; x <= stop_x; x++)
4865 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
4866 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
4868 GetFillColor(draw_info,x-start_x,y-start_y,&pixel,exception);
4869 SetPixelViaPixelInfo(image,&pixel,q);
4871 q+=GetPixelChannels(image);
4873 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4874 if (sync == MagickFalse)
4877 image_view=DestroyCacheView(image_view);
4878 polygon_info=DestroyPolygonThreadSet(polygon_info);
4879 if (image->debug != MagickFalse)
4880 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4881 " end draw-polygon");
4885 Draw polygon or line.
4887 start_y=(ssize_t) ceil(bounds.y1-0.5);
4888 stop_y=(ssize_t) floor(bounds.y2+0.5);
4889 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4890 #pragma omp parallel for schedule(static) shared(status) \
4891 magick_number_threads(image,image,stop_y-start_y+1,1)
4893 for (y=start_y; y <= stop_y; y++)
4896 id = GetOpenMPThreadId();
4908 if (status == MagickFalse)
4910 start_x=(ssize_t) ceil(bounds.x1-0.5);
4911 stop_x=(ssize_t) floor(bounds.x2+0.5);
4912 q=GetCacheViewAuthenticPixels(image_view,start_x,y,(size_t) (stop_x-start_x+
4914 if (q == (Quantum *) NULL)
4919 for (x=start_x; x <= stop_x; x++)
4932 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
4934 if (draw_info->stroke_antialias == MagickFalse)
4936 fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0;
4937 stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0;
4939 GetFillColor(draw_info,x-start_x,y-start_y,&fill_color,exception);
4940 CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
4941 (double) GetPixelAlpha(image,q),q);
4942 GetStrokeColor(draw_info,x-start_x,y-start_y,&stroke_color,exception);
4943 CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
4944 (double) GetPixelAlpha(image,q),q);
4945 q+=GetPixelChannels(image);
4947 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4950 image_view=DestroyCacheView(image_view);
4951 polygon_info=DestroyPolygonThreadSet(polygon_info);
4952 if (image->debug != MagickFalse)
4953 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
4958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4962 % D r a w P r i m i t i v e %
4966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4968 % DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
4970 % The format of the DrawPrimitive method is:
4972 % MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
4973 % PrimitiveInfo *primitive_info,ExceptionInfo *exception)
4975 % A description of each parameter follows:
4977 % o image: the image.
4979 % o draw_info: the draw info.
4981 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4983 % o exception: return any errors or warnings in this structure.
4987 static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
5013 x=(ssize_t) ceil(primitive_info->point.x-0.5);
5014 y=(ssize_t) ceil(primitive_info->point.y-0.5);
5015 switch (primitive_info->primitive)
5017 case AlphaPrimitive:
5019 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5020 "AlphaPrimitive %.20g,%.20g %s",(double) x,(double) y,
5021 methods[primitive_info->method]);
5024 case ColorPrimitive:
5026 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5027 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
5028 methods[primitive_info->method]);
5031 case ImagePrimitive:
5033 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5034 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
5037 case PointPrimitive:
5039 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5040 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
5041 methods[primitive_info->method]);
5046 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5047 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
5054 p=primitive_info[0].point;
5057 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5059 point=primitive_info[i].point;
5060 if (coordinates <= 0)
5062 coordinates=(ssize_t) primitive_info[i].coordinates;
5063 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5064 " begin open (%.20g)",(double) coordinates);
5067 point=primitive_info[i].point;
5068 if ((fabs(q.x-point.x) >= MagickEpsilon) ||
5069 (fabs(q.y-point.y) >= MagickEpsilon))
5070 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5071 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
5073 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5074 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
5077 if (coordinates > 0)
5079 if ((fabs(p.x-point.x) >= MagickEpsilon) ||
5080 (fabs(p.y-point.y) >= MagickEpsilon))
5081 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
5082 (double) coordinates);
5084 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
5085 (double) coordinates);
5089 MagickExport MagickBooleanType DrawPrimitive(Image *image,
5090 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5091 ExceptionInfo *exception)
5106 if (image->debug != MagickFalse)
5108 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5109 " begin draw-primitive");
5110 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5111 " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
5112 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
5113 draw_info->affine.tx,draw_info->affine.ty);
5116 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5117 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
5118 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
5119 status=SetImageColorspace(image,sRGBColorspace,exception);
5120 if (draw_info->compliance == SVGCompliance)
5122 status&=SetImageMask(image,WritePixelMask,draw_info->clipping_mask,
5124 status&=SetImageMask(image,CompositePixelMask,draw_info->composite_mask,
5127 x=(ssize_t) ceil(primitive_info->point.x-0.5);
5128 y=(ssize_t) ceil(primitive_info->point.y-0.5);
5129 image_view=AcquireAuthenticCacheView(image,exception);
5130 switch (primitive_info->primitive)
5132 case AlphaPrimitive:
5134 if (image->alpha_trait == UndefinedPixelTrait)
5135 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
5136 switch (primitive_info->method)
5147 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5148 if (q == (Quantum *) NULL)
5150 GetFillColor(draw_info,x,y,&pixel,exception);
5151 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5152 (void) SyncCacheViewAuthenticPixels(image_view,exception);
5164 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
5166 GetPixelInfo(image,&pixel);
5167 for (y=0; y < (ssize_t) image->rows; y++)
5172 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5174 if (q == (Quantum *) NULL)
5176 for (x=0; x < (ssize_t) image->columns; x++)
5178 GetPixelInfoPixel(image,q,&pixel);
5179 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5181 q+=GetPixelChannels(image);
5184 GetFillColor(draw_info,x,y,&pixel,exception);
5185 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5186 q+=GetPixelChannels(image);
5188 sync=SyncCacheViewAuthenticPixels(image_view,exception);
5189 if (sync == MagickFalse)
5194 case FloodfillMethod:
5195 case FillToBorderMethod:
5203 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
5205 if (primitive_info->method == FillToBorderMethod)
5207 target.red=(double) draw_info->border_color.red;
5208 target.green=(double) draw_info->border_color.green;
5209 target.blue=(double) draw_info->border_color.blue;
5211 channel_mask=SetImageChannelMask(image,AlphaChannel);
5212 status&=FloodfillPaintImage(image,draw_info,&target,x,y,
5213 primitive_info->method == FloodfillMethod ? MagickFalse :
5214 MagickTrue,exception);
5215 (void) SetImageChannelMask(image,channel_mask);
5226 for (y=0; y < (ssize_t) image->rows; y++)
5231 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5233 if (q == (Quantum *) NULL)
5235 for (x=0; x < (ssize_t) image->columns; x++)
5237 GetFillColor(draw_info,x,y,&pixel,exception);
5238 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5239 q+=GetPixelChannels(image);
5241 sync=SyncCacheViewAuthenticPixels(image_view,exception);
5242 if (sync == MagickFalse)
5250 case ColorPrimitive:
5252 switch (primitive_info->method)
5263 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5264 if (q == (Quantum *) NULL)
5266 GetPixelInfo(image,&pixel);
5267 GetFillColor(draw_info,x,y,&pixel,exception);
5268 SetPixelViaPixelInfo(image,&pixel,q);
5269 (void) SyncCacheViewAuthenticPixels(image_view,exception);
5281 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
5283 for (y=0; y < (ssize_t) image->rows; y++)
5288 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5290 if (q == (Quantum *) NULL)
5292 for (x=0; x < (ssize_t) image->columns; x++)
5294 GetPixelInfoPixel(image,q,&pixel);
5295 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5297 q+=GetPixelChannels(image);
5300 GetFillColor(draw_info,x,y,&pixel,exception);
5301 SetPixelViaPixelInfo(image,&pixel,q);
5302 q+=GetPixelChannels(image);
5304 sync=SyncCacheViewAuthenticPixels(image_view,exception);
5305 if (sync == MagickFalse)
5310 case FloodfillMethod:
5311 case FillToBorderMethod:
5316 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
5318 if (primitive_info->method == FillToBorderMethod)
5320 target.red=(double) draw_info->border_color.red;
5321 target.green=(double) draw_info->border_color.green;
5322 target.blue=(double) draw_info->border_color.blue;
5324 status&=FloodfillPaintImage(image,draw_info,&target,x,y,
5325 primitive_info->method == FloodfillMethod ? MagickFalse :
5326 MagickTrue,exception);
5337 GetPixelInfo(image,&pixel);
5338 for (y=0; y < (ssize_t) image->rows; y++)
5343 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5345 if (q == (Quantum *) NULL)
5347 for (x=0; x < (ssize_t) image->columns; x++)
5349 GetFillColor(draw_info,x,y,&pixel,exception);
5350 SetPixelViaPixelInfo(image,&pixel,q);
5351 q+=GetPixelChannels(image);
5353 sync=SyncCacheViewAuthenticPixels(image_view,exception);
5354 if (sync == MagickFalse)
5362 case ImagePrimitive:
5368 composite_geometry[MagickPathExtent];
5384 if (primitive_info->text == (char *) NULL)
5386 clone_info=AcquireImageInfo();
5387 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
5388 composite_images=ReadInlineImage(clone_info,primitive_info->text,
5392 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5394 composite_images=ReadImage(clone_info,exception);
5396 clone_info=DestroyImageInfo(clone_info);
5397 if (composite_images == (Image *) NULL)
5402 composite_image=RemoveFirstImageFromList(&composite_images);
5403 composite_images=DestroyImageList(composite_images);
5404 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
5405 NULL,(void *) NULL);
5406 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
5407 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
5408 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
5409 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
5414 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5415 "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
5416 composite_image->filter=image->filter;
5417 (void) TransformImage(&composite_image,(char *) NULL,
5418 composite_geometry,exception);
5420 if (composite_image->alpha_trait == UndefinedPixelTrait)
5421 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
5423 if (draw_info->alpha != OpaqueAlpha)
5424 (void) SetImageAlpha(composite_image,draw_info->alpha,exception);
5425 SetGeometry(image,&geometry);
5426 image->gravity=draw_info->gravity;
5429 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5430 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
5431 composite_image->rows,(double) geometry.x,(double) geometry.y);
5432 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
5433 affine=draw_info->affine;
5434 affine.tx=(double) geometry.x;
5435 affine.ty=(double) geometry.y;
5436 composite_image->interpolate=image->interpolate;
5437 status&=DrawAffineImage(image,composite_image,&affine,exception);
5438 composite_image=DestroyImage(composite_image);
5441 case PointPrimitive:
5449 if ((y < 0) || (y >= (ssize_t) image->rows))
5451 if ((x < 0) || (x >= (ssize_t) image->columns))
5453 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5454 if (q == (Quantum *) NULL)
5456 GetFillColor(draw_info,x,y,&fill_color,exception);
5457 CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,
5458 (double) GetPixelAlpha(image,q),q);
5459 (void) SyncCacheViewAuthenticPixels(image_view,exception);
5465 geometry[MagickPathExtent];
5470 if (primitive_info->text == (char *) NULL)
5472 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5473 (void) CloneString(&clone_info->text,primitive_info->text);
5474 (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
5475 primitive_info->point.x,primitive_info->point.y);
5476 (void) CloneString(&clone_info->geometry,geometry);
5477 status&=AnnotateImage(image,clone_info,exception);
5478 clone_info=DestroyDrawInfo(clone_info);
5490 if (IsEventLogging() != MagickFalse)
5491 LogPrimitiveInfo(primitive_info);
5492 scale=ExpandAffine(&draw_info->affine);
5493 if ((draw_info->dash_pattern != (double *) NULL) &&
5494 (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
5495 (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
5496 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
5501 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5502 clone_info->stroke_width=0.0;
5503 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5504 status&=DrawPolygonPrimitive(image,clone_info,primitive_info,
5506 clone_info=DestroyDrawInfo(clone_info);
5507 status=DrawDashPolygon(draw_info,primitive_info,image,exception);
5510 mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
5512 ((draw_info->stroke.alpha != (Quantum) TransparentAlpha) ||
5513 (draw_info->stroke_pattern != (Image *) NULL)))
5523 Draw strokes while respecting line cap/join attributes.
5525 closed_path=primitive_info[0].closed_subpath;
5526 i=(ssize_t) primitive_info[0].coordinates;
5527 x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
5528 y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
5529 if ((x < MagickEpsilon) && (y < MagickEpsilon))
5530 closed_path=MagickTrue;
5531 if ((((draw_info->linecap == RoundCap) ||
5532 (closed_path != MagickFalse)) &&
5533 (draw_info->linejoin == RoundJoin)) ||
5534 (primitive_info[i].primitive != UndefinedPrimitive))
5536 status=DrawPolygonPrimitive(image,draw_info,primitive_info,
5540 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5541 clone_info->stroke_width=0.0;
5542 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5543 status&=DrawPolygonPrimitive(image,clone_info,primitive_info,
5545 clone_info=DestroyDrawInfo(clone_info);
5546 status&=DrawStrokePolygon(image,draw_info,primitive_info,exception);
5549 status&=DrawPolygonPrimitive(image,draw_info,primitive_info,exception);
5553 image_view=DestroyCacheView(image_view);
5554 if (draw_info->compliance == SVGCompliance)
5556 status&=SetImageMask(image,WritePixelMask,(Image *) NULL,exception);
5557 status&=SetImageMask(image,CompositePixelMask,(Image *) NULL,exception);
5559 if (image->debug != MagickFalse)
5560 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
5561 return(status != 0 ? MagickTrue : MagickFalse);
5565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5569 + D r a w S t r o k e P o l y g o n %
5573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5575 % DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
5576 % the image while respecting the line cap and join attributes.
5578 % The format of the DrawStrokePolygon method is:
5580 % MagickBooleanType DrawStrokePolygon(Image *image,
5581 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
5583 % A description of each parameter follows:
5585 % o image: the image.
5587 % o draw_info: the draw info.
5589 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5594 static MagickBooleanType DrawRoundLinecap(Image *image,
5595 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5596 ExceptionInfo *exception)
5604 for (i=0; i < 4; i++)
5605 linecap[i]=(*primitive_info);
5606 linecap[0].coordinates=4;
5607 linecap[1].point.x+=2.0*MagickEpsilon;
5608 linecap[2].point.x+=2.0*MagickEpsilon;
5609 linecap[2].point.y+=2.0*MagickEpsilon;
5610 linecap[3].point.y+=2.0*MagickEpsilon;
5611 linecap[4].primitive=UndefinedPrimitive;
5612 return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
5615 static MagickBooleanType DrawStrokePolygon(Image *image,
5616 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5617 ExceptionInfo *exception)
5631 register const PrimitiveInfo
5636 Draw stroked polygon.
5638 if (image->debug != MagickFalse)
5639 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5640 " begin draw-stroke-polygon");
5641 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5642 clone_info->fill=draw_info->stroke;
5643 if (clone_info->fill_pattern != (Image *) NULL)
5644 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
5645 if (clone_info->stroke_pattern != (Image *) NULL)
5646 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
5647 MagickTrue,exception);
5648 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5649 clone_info->stroke_width=0.0;
5650 clone_info->fill_rule=NonZeroRule;
5652 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
5654 if (p->coordinates == 1)
5656 stroke_polygon=TraceStrokePolygon(image,draw_info,p);
5657 if (stroke_polygon == (PrimitiveInfo *) NULL)
5660 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
5663 status&=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception);
5664 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
5667 q=p+p->coordinates-1;
5668 closed_path=p->closed_subpath;
5669 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
5671 status&=DrawRoundLinecap(image,draw_info,p,exception);
5672 status&=DrawRoundLinecap(image,draw_info,q,exception);
5675 clone_info=DestroyDrawInfo(clone_info);
5676 if (image->debug != MagickFalse)
5677 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5678 " end draw-stroke-polygon");
5679 return(status != 0 ? MagickTrue : MagickFalse);
5683 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5687 % G e t A f f i n e M a t r i x %
5691 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5693 % GetAffineMatrix() returns an AffineMatrix initialized to the identity
5696 % The format of the GetAffineMatrix method is:
5698 % void GetAffineMatrix(AffineMatrix *affine_matrix)
5700 % A description of each parameter follows:
5702 % o affine_matrix: the affine matrix.
5705 MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
5707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
5708 assert(affine_matrix != (AffineMatrix *) NULL);
5709 (void) memset(affine_matrix,0,sizeof(*affine_matrix));
5710 affine_matrix->sx=1.0;
5711 affine_matrix->sy=1.0;
5715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5719 + G e t D r a w I n f o %
5723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5725 % GetDrawInfo() initializes draw_info to default values from image_info.
5727 % The format of the GetDrawInfo method is:
5729 % void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
5731 % A description of each parameter follows:
5733 % o image_info: the image info..
5735 % o draw_info: the draw info.
5738 MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
5753 Initialize draw attributes.
5755 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
5756 assert(draw_info != (DrawInfo *) NULL);
5757 (void) memset(draw_info,0,sizeof(*draw_info));
5758 clone_info=CloneImageInfo(image_info);
5759 GetAffineMatrix(&draw_info->affine);
5760 exception=AcquireExceptionInfo();
5761 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
5763 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
5765 draw_info->stroke_antialias=clone_info->antialias;
5766 draw_info->stroke_width=1.0;
5767 draw_info->fill_rule=EvenOddRule;
5768 draw_info->alpha=OpaqueAlpha;
5769 draw_info->fill_alpha=OpaqueAlpha;
5770 draw_info->stroke_alpha=OpaqueAlpha;
5771 draw_info->linecap=ButtCap;
5772 draw_info->linejoin=MiterJoin;
5773 draw_info->miterlimit=10;
5774 draw_info->decorate=NoDecoration;
5775 draw_info->pointsize=12.0;
5776 draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
5777 draw_info->compose=OverCompositeOp;
5778 draw_info->render=MagickTrue;
5779 draw_info->clip_path=MagickFalse;
5780 draw_info->debug=IsEventLogging();
5781 if (clone_info->font != (char *) NULL)
5782 draw_info->font=AcquireString(clone_info->font);
5783 if (clone_info->density != (char *) NULL)
5784 draw_info->density=AcquireString(clone_info->density);
5785 draw_info->text_antialias=clone_info->antialias;
5786 if (fabs(clone_info->pointsize) >= MagickEpsilon)
5787 draw_info->pointsize=clone_info->pointsize;
5788 draw_info->border_color=clone_info->border_color;
5789 if (clone_info->server_name != (char *) NULL)
5790 draw_info->server_name=AcquireString(clone_info->server_name);
5791 option=GetImageOption(clone_info,"direction");
5792 if (option != (const char *) NULL)
5793 draw_info->direction=(DirectionType) ParseCommandOption(
5794 MagickDirectionOptions,MagickFalse,option);
5796 draw_info->direction=UndefinedDirection;
5797 option=GetImageOption(clone_info,"encoding");
5798 if (option != (const char *) NULL)
5799 (void) CloneString(&draw_info->encoding,option);
5800 option=GetImageOption(clone_info,"family");
5801 if (option != (const char *) NULL)
5802 (void) CloneString(&draw_info->family,option);
5803 option=GetImageOption(clone_info,"fill");
5804 if (option != (const char *) NULL)
5805 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
5807 option=GetImageOption(clone_info,"gravity");
5808 if (option != (const char *) NULL)
5809 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
5810 MagickFalse,option);
5811 option=GetImageOption(clone_info,"interline-spacing");
5812 if (option != (const char *) NULL)
5813 draw_info->interline_spacing=StringToDouble(option,&next_token);
5814 option=GetImageOption(clone_info,"interword-spacing");
5815 if (option != (const char *) NULL)
5816 draw_info->interword_spacing=StringToDouble(option,&next_token);
5817 option=GetImageOption(clone_info,"kerning");
5818 if (option != (const char *) NULL)
5819 draw_info->kerning=StringToDouble(option,&next_token);
5820 option=GetImageOption(clone_info,"stroke");
5821 if (option != (const char *) NULL)
5822 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
5824 option=GetImageOption(clone_info,"strokewidth");
5825 if (option != (const char *) NULL)
5826 draw_info->stroke_width=StringToDouble(option,&next_token);
5827 option=GetImageOption(clone_info,"style");
5828 if (option != (const char *) NULL)
5829 draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
5830 MagickFalse,option);
5831 option=GetImageOption(clone_info,"undercolor");
5832 if (option != (const char *) NULL)
5833 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
5835 option=GetImageOption(clone_info,"weight");
5836 if (option != (const char *) NULL)
5841 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
5843 weight=(ssize_t) StringToUnsignedLong(option);
5844 draw_info->weight=(size_t) weight;
5846 exception=DestroyExceptionInfo(exception);
5847 draw_info->signature=MagickCoreSignature;
5848 clone_info=DestroyImageInfo(clone_info);
5852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5856 + P e r m u t a t e %
5860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5862 % Permutate() returns the permuation of the (n,k).
5864 % The format of the Permutate method is:
5866 % void Permutate(ssize_t n,ssize_t k)
5868 % A description of each parameter follows:
5876 static inline double Permutate(const ssize_t n,const ssize_t k)
5885 for (i=k+1; i <= n; i++)
5887 for (i=1; i <= (n-k); i++)
5893 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5897 + T r a c e P r i m i t i v e %
5901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5903 % TracePrimitive is a collection of methods for generating graphic
5904 % primitives such as arcs, ellipses, paths, etc.
5908 static MagickBooleanType TraceArc(MVGInfo *mvg_info,const PointInfo start,
5909 const PointInfo end,const PointInfo degrees)
5915 center.x=0.5*(end.x+start.x);
5916 center.y=0.5*(end.y+start.y);
5917 radius.x=fabs(center.x-start.x);
5918 radius.y=fabs(center.y-start.y);
5919 return(TraceEllipse(mvg_info,center,radius,degrees));
5922 static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,const PointInfo start,
5923 const PointInfo end,const PointInfo arc,const double angle,
5924 const MagickBooleanType large_arc,const MagickBooleanType sweep)
5949 register PrimitiveInfo
5961 offset=mvg_info->offset;
5962 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
5963 primitive_info->coordinates=0;
5964 if ((fabs(start.x-end.x) < MagickEpsilon) &&
5965 (fabs(start.y-end.y) < MagickEpsilon))
5966 return(TracePoint(primitive_info,end));
5967 radii.x=fabs(arc.x);
5968 radii.y=fabs(arc.y);
5969 if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
5970 return(TraceLine(primitive_info,start,end));
5971 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
5972 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
5973 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
5974 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
5975 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
5977 if (delta < MagickEpsilon)
5978 return(TraceLine(primitive_info,start,end));
5981 radii.x*=sqrt((double) delta);
5982 radii.y*=sqrt((double) delta);
5984 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
5985 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
5986 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
5987 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
5988 alpha=points[1].x-points[0].x;
5989 beta=points[1].y-points[0].y;
5990 factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25;
5995 factor=sqrt((double) factor);
5996 if (sweep == large_arc)
5999 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
6000 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
6001 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
6002 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
6003 if ((theta < 0.0) && (sweep != MagickFalse))
6004 theta+=2.0*MagickPI;
6006 if ((theta > 0.0) && (sweep == MagickFalse))
6007 theta-=2.0*MagickPI;
6008 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
6012 for (i=0; i < (ssize_t) arc_segments; i++)
6014 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
6015 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
6016 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
6017 sin(fmod((double) beta,DegreesToRadians(360.0)));
6018 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
6019 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
6020 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6021 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
6022 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
6023 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6024 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
6025 theta/arc_segments),DegreesToRadians(360.0))));
6026 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
6027 theta/arc_segments),DegreesToRadians(360.0))));
6028 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
6029 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6030 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
6031 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6032 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
6033 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
6034 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
6036 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
6038 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
6040 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
6042 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
6044 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
6046 if (i == (ssize_t) (arc_segments-1))
6048 if (TraceBezier(mvg_info,4) == MagickFalse)
6050 p=(*mvg_info->primitive_info)+mvg_info->offset;
6051 mvg_info->offset+=p->coordinates;
6054 mvg_info->offset=offset;
6055 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6056 primitive_info->coordinates=(size_t) (p-primitive_info);
6057 primitive_info->closed_subpath=MagickFalse;
6058 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6060 p->primitive=primitive_info->primitive;
6066 static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
6067 const size_t number_coordinates)
6082 register PrimitiveInfo
6094 Allocate coefficients.
6096 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6097 quantum=number_coordinates;
6098 for (i=0; i < (ssize_t) number_coordinates; i++)
6100 for (j=i+1; j < (ssize_t) number_coordinates; j++)
6102 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
6103 if (alpha > (double) quantum)
6104 quantum=(size_t) alpha;
6105 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
6106 if (alpha > (double) quantum)
6107 quantum=(size_t) alpha;
6110 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
6111 (double) BezierQuantum);
6112 control_points=quantum*number_coordinates;
6113 if (CheckPrimitiveExtent(mvg_info,control_points+1) == MagickFalse)
6114 return(MagickFalse);
6115 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6116 coefficients=(double *) AcquireQuantumMemory((size_t)
6117 number_coordinates,sizeof(*coefficients));
6118 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
6120 if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
6121 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
6123 Compute bezier points.
6125 end=primitive_info[number_coordinates-1].point;
6126 for (i=0; i < (ssize_t) number_coordinates; i++)
6127 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
6129 for (i=0; i < (ssize_t) control_points; i++)
6134 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
6135 for (j=0; j < (ssize_t) number_coordinates; j++)
6137 point.x+=alpha*coefficients[j]*p->point.x;
6138 point.y+=alpha*coefficients[j]*p->point.y;
6139 alpha*=weight/(1.0-weight);
6143 weight+=1.0/control_points;
6146 Bezier curves are just short segmented polys.
6149 for (i=0; i < (ssize_t) control_points; i++)
6151 if (TracePoint(p,points[i]) == MagickFalse)
6152 return(MagickFalse);
6155 if (TracePoint(p,end) == MagickFalse)
6156 return(MagickFalse);
6158 primitive_info->coordinates=(size_t) (p-primitive_info);
6159 primitive_info->closed_subpath=MagickFalse;
6160 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6162 p->primitive=primitive_info->primitive;
6165 points=(PointInfo *) RelinquishMagickMemory(points);
6166 coefficients=(double *) RelinquishMagickMemory(coefficients);
6170 static MagickBooleanType TraceCircle(MVGInfo *mvg_info,const PointInfo start,
6171 const PointInfo end)
6182 alpha=end.x-start.x;
6184 radius=hypot((double) alpha,(double) beta);
6185 offset.x=(double) radius;
6186 offset.y=(double) radius;
6189 return(TraceEllipse(mvg_info,start,offset,degrees));
6192 static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
6193 const PointInfo radii,const PointInfo arc)
6209 register PrimitiveInfo
6216 Ellipses are just short segmented polys.
6218 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6219 primitive_info->coordinates=0;
6220 if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
6222 delta=2.0*PerceptibleReciprocal(MagickMax(radii.x,radii.y));
6224 if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
6225 step=MagickPI/4.0/(MagickPI*PerceptibleReciprocal(delta)/2.0);
6226 angle.x=DegreesToRadians(arc.x);
6230 angle.y=DegreesToRadians(y);
6231 coordinates=ceil((angle.y-angle.x)/step+1.0);
6232 if ((coordinates > (double) SSIZE_MAX) ||
6233 (coordinates > (double) GetMaxMemoryRequest()))
6235 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6236 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6237 return(MagickFalse);
6239 if (CheckPrimitiveExtent(mvg_info,(size_t) coordinates) == MagickFalse)
6240 return(MagickFalse);
6241 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6242 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
6244 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
6245 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
6246 if (TracePoint(p,point) == MagickFalse)
6247 return(MagickFalse);
6250 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
6251 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
6252 if (TracePoint(p,point) == MagickFalse)
6253 return(MagickFalse);
6255 primitive_info->coordinates=(size_t) (p-primitive_info);
6256 primitive_info->closed_subpath=MagickFalse;
6257 x=fabs(primitive_info[0].point.x-
6258 primitive_info[primitive_info->coordinates-1].point.x);
6259 y=fabs(primitive_info[0].point.y-
6260 primitive_info[primitive_info->coordinates-1].point.y);
6261 if ((x < MagickEpsilon) && (y < MagickEpsilon))
6262 primitive_info->closed_subpath=MagickTrue;
6263 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6265 p->primitive=primitive_info->primitive;
6271 static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
6272 const PointInfo start,const PointInfo end)
6274 if (TracePoint(primitive_info,start) == MagickFalse)
6275 return(MagickFalse);
6276 if ((fabs(start.x-end.x) < MagickEpsilon) &&
6277 (fabs(start.y-end.y) < MagickEpsilon))
6279 primitive_info->primitive=PointPrimitive;
6280 primitive_info->coordinates=1;
6283 if (TracePoint(primitive_info+1,end) == MagickFalse)
6284 return(MagickFalse);
6285 (primitive_info+1)->primitive=primitive_info->primitive;
6286 primitive_info->coordinates=2;
6287 primitive_info->closed_subpath=MagickFalse;
6291 static size_t TracePath(MVGInfo *mvg_info,const char *path,
6292 ExceptionInfo *exception)
6296 token[MagickPathExtent];
6314 points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
6324 register PrimitiveInfo
6337 subpath_offset=mvg_info->offset;
6338 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6341 number_coordinates=0;
6343 primitive_type=primitive_info->primitive;
6345 for (p=path; *p != '\0'; )
6347 if (status == MagickFalse)
6349 while (isspace((int) ((unsigned char) *p)) != 0)
6353 last_attribute=attribute;
6354 attribute=(int) (*p++);
6364 large_arc = MagickFalse,
6365 sweep = MagickFalse;
6375 GetNextToken(p,&p,MagickPathExtent,token);
6377 GetNextToken(p,&p,MagickPathExtent,token);
6378 arc.x=StringToDouble(token,&next_token);
6379 if (token == next_token)
6380 ThrowPointExpectedException(token,exception);
6381 GetNextToken(p,&p,MagickPathExtent,token);
6383 GetNextToken(p,&p,MagickPathExtent,token);
6384 arc.y=StringToDouble(token,&next_token);
6385 if (token == next_token)
6386 ThrowPointExpectedException(token,exception);
6387 GetNextToken(p,&p,MagickPathExtent,token);
6389 GetNextToken(p,&p,MagickPathExtent,token);
6390 angle=StringToDouble(token,&next_token);
6391 if (token == next_token)
6392 ThrowPointExpectedException(token,exception);
6393 GetNextToken(p,&p,MagickPathExtent,token);
6395 GetNextToken(p,&p,MagickPathExtent,token);
6396 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6397 GetNextToken(p,&p,MagickPathExtent,token);
6399 GetNextToken(p,&p,MagickPathExtent,token);
6400 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6402 GetNextToken(p,&p,MagickPathExtent,token);
6403 GetNextToken(p,&p,MagickPathExtent,token);
6405 GetNextToken(p,&p,MagickPathExtent,token);
6406 x=StringToDouble(token,&next_token);
6407 if (token == next_token)
6408 ThrowPointExpectedException(token,exception);
6409 GetNextToken(p,&p,MagickPathExtent,token);
6411 GetNextToken(p,&p,MagickPathExtent,token);
6412 y=StringToDouble(token,&next_token);
6413 if (token == next_token)
6414 ThrowPointExpectedException(token,exception);
6415 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
6416 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
6417 if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
6419 q=(*mvg_info->primitive_info)+mvg_info->offset;
6420 mvg_info->offset+=q->coordinates;
6423 while (isspace((int) ((unsigned char) *p)) != 0)
6427 } while (IsPoint(p) != MagickFalse);
6434 Cubic Bézier curve.
6439 for (i=1; i < 4; i++)
6441 GetNextToken(p,&p,MagickPathExtent,token);
6443 GetNextToken(p,&p,MagickPathExtent,token);
6444 x=StringToDouble(token,&next_token);
6445 if (token == next_token)
6446 ThrowPointExpectedException(token,exception);
6447 GetNextToken(p,&p,MagickPathExtent,token);
6449 GetNextToken(p,&p,MagickPathExtent,token);
6450 y=StringToDouble(token,&next_token);
6451 if (token == next_token)
6452 ThrowPointExpectedException(token,exception);
6453 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
6454 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
6457 for (i=0; i < 4; i++)
6458 (q+i)->point=points[i];
6459 if (TraceBezier(mvg_info,4) == MagickFalse)
6461 q=(*mvg_info->primitive_info)+mvg_info->offset;
6462 mvg_info->offset+=q->coordinates;
6465 while (isspace((int) ((unsigned char) *p)) != 0)
6469 } while (IsPoint(p) != MagickFalse);
6477 GetNextToken(p,&p,MagickPathExtent,token);
6479 GetNextToken(p,&p,MagickPathExtent,token);
6480 x=StringToDouble(token,&next_token);
6481 if (token == next_token)
6482 ThrowPointExpectedException(token,exception);
6483 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
6484 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6486 q=(*mvg_info->primitive_info)+mvg_info->offset;
6487 if (TracePoint(q,point) == MagickFalse)
6489 mvg_info->offset+=q->coordinates;
6491 while (isspace((int) ((unsigned char) *p)) != 0)
6495 } while (IsPoint(p) != MagickFalse);
6506 GetNextToken(p,&p,MagickPathExtent,token);
6508 GetNextToken(p,&p,MagickPathExtent,token);
6509 x=StringToDouble(token,&next_token);
6510 if (token == next_token)
6511 ThrowPointExpectedException(token,exception);
6512 GetNextToken(p,&p,MagickPathExtent,token);
6514 GetNextToken(p,&p,MagickPathExtent,token);
6515 y=StringToDouble(token,&next_token);
6516 if (token == next_token)
6517 ThrowPointExpectedException(token,exception);
6518 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
6519 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
6520 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6522 q=(*mvg_info->primitive_info)+mvg_info->offset;
6523 if (TracePoint(q,point) == MagickFalse)
6525 mvg_info->offset+=q->coordinates;
6527 while (isspace((int) ((unsigned char) *p)) != 0)
6531 } while (IsPoint(p) != MagickFalse);
6540 if (mvg_info->offset != subpath_offset)
6542 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6543 primitive_info->coordinates=(size_t) (q-primitive_info);
6544 number_coordinates+=primitive_info->coordinates;
6546 subpath_offset=mvg_info->offset;
6551 GetNextToken(p,&p,MagickPathExtent,token);
6553 GetNextToken(p,&p,MagickPathExtent,token);
6554 x=StringToDouble(token,&next_token);
6555 if (token == next_token)
6556 ThrowPointExpectedException(token,exception);
6557 GetNextToken(p,&p,MagickPathExtent,token);
6559 GetNextToken(p,&p,MagickPathExtent,token);
6560 y=StringToDouble(token,&next_token);
6561 if (token == next_token)
6562 ThrowPointExpectedException(token,exception);
6563 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
6564 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
6568 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6570 q=(*mvg_info->primitive_info)+mvg_info->offset;
6571 if (TracePoint(q,point) == MagickFalse)
6573 mvg_info->offset+=q->coordinates;
6575 while (isspace((int) ((unsigned char) *p)) != 0)
6579 } while (IsPoint(p) != MagickFalse);
6586 Quadratic Bézier curve.
6591 for (i=1; i < 3; i++)
6593 GetNextToken(p,&p,MagickPathExtent,token);
6595 GetNextToken(p,&p,MagickPathExtent,token);
6596 x=StringToDouble(token,&next_token);
6597 if (token == next_token)
6598 ThrowPointExpectedException(token,exception);
6599 GetNextToken(p,&p,MagickPathExtent,token);
6601 GetNextToken(p,&p,MagickPathExtent,token);
6602 y=StringToDouble(token,&next_token);
6603 if (token == next_token)
6604 ThrowPointExpectedException(token,exception);
6607 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
6608 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
6611 for (i=0; i < 3; i++)
6612 (q+i)->point=points[i];
6613 if (TraceBezier(mvg_info,3) == MagickFalse)
6615 q=(*mvg_info->primitive_info)+mvg_info->offset;
6616 mvg_info->offset+=q->coordinates;
6619 while (isspace((int) ((unsigned char) *p)) != 0)
6623 } while (IsPoint(p) != MagickFalse);
6630 Cubic Bézier curve.
6634 points[0]=points[3];
6635 points[1].x=2.0*points[3].x-points[2].x;
6636 points[1].y=2.0*points[3].y-points[2].y;
6637 for (i=2; i < 4; i++)
6639 GetNextToken(p,&p,MagickPathExtent,token);
6641 GetNextToken(p,&p,MagickPathExtent,token);
6642 x=StringToDouble(token,&next_token);
6643 if (token == next_token)
6644 ThrowPointExpectedException(token,exception);
6645 GetNextToken(p,&p,MagickPathExtent,token);
6647 GetNextToken(p,&p,MagickPathExtent,token);
6648 y=StringToDouble(token,&next_token);
6649 if (token == next_token)
6650 ThrowPointExpectedException(token,exception);
6653 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
6654 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
6657 if (strchr("CcSs",last_attribute) == (char *) NULL)
6662 for (i=0; i < 4; i++)
6663 (q+i)->point=points[i];
6664 if (TraceBezier(mvg_info,4) == MagickFalse)
6666 q=(*mvg_info->primitive_info)+mvg_info->offset;
6667 mvg_info->offset+=q->coordinates;
6670 last_attribute=attribute;
6671 while (isspace((int) ((unsigned char) *p)) != 0)
6675 } while (IsPoint(p) != MagickFalse);
6682 Quadratic Bézier curve.
6686 points[0]=points[2];
6687 points[1].x=2.0*points[2].x-points[1].x;
6688 points[1].y=2.0*points[2].y-points[1].y;
6689 for (i=2; i < 3; i++)
6691 GetNextToken(p,&p,MagickPathExtent,token);
6693 GetNextToken(p,&p,MagickPathExtent,token);
6694 x=StringToDouble(token,&next_token);
6695 if (token == next_token)
6696 ThrowPointExpectedException(token,exception);
6697 GetNextToken(p,&p,MagickPathExtent,token);
6699 GetNextToken(p,&p,MagickPathExtent,token);
6700 y=StringToDouble(token,&next_token);
6701 if (token == next_token)
6702 ThrowPointExpectedException(token,exception);
6703 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
6704 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
6707 if (status == MagickFalse)
6709 if (strchr("QqTt",last_attribute) == (char *) NULL)
6714 for (i=0; i < 3; i++)
6715 (q+i)->point=points[i];
6716 if (TraceBezier(mvg_info,3) == MagickFalse)
6718 q=(*mvg_info->primitive_info)+mvg_info->offset;
6719 mvg_info->offset+=q->coordinates;
6722 last_attribute=attribute;
6723 while (isspace((int) ((unsigned char) *p)) != 0)
6727 } while (IsPoint(p) != MagickFalse);
6738 GetNextToken(p,&p,MagickPathExtent,token);
6740 GetNextToken(p,&p,MagickPathExtent,token);
6741 y=StringToDouble(token,&next_token);
6742 if (token == next_token)
6743 ThrowPointExpectedException(token,exception);
6744 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
6745 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6747 q=(*mvg_info->primitive_info)+mvg_info->offset;
6748 if (TracePoint(q,point) == MagickFalse)
6750 mvg_info->offset+=q->coordinates;
6752 while (isspace((int) ((unsigned char) *p)) != 0)
6756 } while (IsPoint(p) != MagickFalse);
6766 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6768 q=(*mvg_info->primitive_info)+mvg_info->offset;
6769 if (TracePoint(q,point) == MagickFalse)
6771 mvg_info->offset+=q->coordinates;
6773 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6774 primitive_info->coordinates=(size_t) (q-primitive_info);
6775 primitive_info->closed_subpath=MagickTrue;
6776 number_coordinates+=primitive_info->coordinates;
6778 subpath_offset=mvg_info->offset;
6784 ThrowPointExpectedException(token,exception);
6789 if (status == MagickFalse)
6791 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6792 primitive_info->coordinates=(size_t) (q-primitive_info);
6793 number_coordinates+=primitive_info->coordinates;
6794 for (i=0; i < (ssize_t) number_coordinates; i++)
6797 q->primitive=primitive_type;
6799 q->method=FillToBorderMethod;
6802 return(number_coordinates);
6805 static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
6806 const PointInfo start,const PointInfo end)
6811 register PrimitiveInfo
6817 if ((fabs(start.x-end.x) < MagickEpsilon) ||
6818 (fabs(start.y-end.y) < MagickEpsilon))
6820 primitive_info->coordinates=0;
6824 if (TracePoint(p,start) == MagickFalse)
6825 return(MagickFalse);
6829 if (TracePoint(p,point) == MagickFalse)
6830 return(MagickFalse);
6832 if (TracePoint(p,end) == MagickFalse)
6833 return(MagickFalse);
6837 if (TracePoint(p,point) == MagickFalse)
6838 return(MagickFalse);
6840 if (TracePoint(p,start) == MagickFalse)
6841 return(MagickFalse);
6843 primitive_info->coordinates=(size_t) (p-primitive_info);
6844 primitive_info->closed_subpath=MagickTrue;
6845 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6847 p->primitive=primitive_info->primitive;
6853 static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
6854 const PointInfo start,const PointInfo end,PointInfo arc)
6864 register PrimitiveInfo
6873 offset=mvg_info->offset;
6874 segment.x=fabs(end.x-start.x);
6875 segment.y=fabs(end.y-start.y);
6876 if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
6878 (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
6881 if (arc.x > (0.5*segment.x))
6882 arc.x=0.5*segment.x;
6883 if (arc.y > (0.5*segment.y))
6884 arc.y=0.5*segment.y;
6885 point.x=start.x+segment.x-arc.x;
6886 point.y=start.y+arc.y;
6889 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
6890 return(MagickFalse);
6891 p=(*mvg_info->primitive_info)+mvg_info->offset;
6892 mvg_info->offset+=p->coordinates;
6893 point.x=start.x+segment.x-arc.x;
6894 point.y=start.y+segment.y-arc.y;
6897 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
6898 return(MagickFalse);
6899 p=(*mvg_info->primitive_info)+mvg_info->offset;
6900 mvg_info->offset+=p->coordinates;
6901 point.x=start.x+arc.x;
6902 point.y=start.y+segment.y-arc.y;
6905 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
6906 return(MagickFalse);
6907 p=(*mvg_info->primitive_info)+mvg_info->offset;
6908 mvg_info->offset+=p->coordinates;
6909 point.x=start.x+arc.x;
6910 point.y=start.y+arc.y;
6913 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
6914 return(MagickFalse);
6915 p=(*mvg_info->primitive_info)+mvg_info->offset;
6916 mvg_info->offset+=p->coordinates;
6917 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6918 return(MagickFalse);
6919 p=(*mvg_info->primitive_info)+mvg_info->offset;
6920 if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
6921 return(MagickFalse);
6923 mvg_info->offset=offset;
6924 primitive_info=(*mvg_info->primitive_info)+offset;
6925 primitive_info->coordinates=(size_t) (p-primitive_info);
6926 primitive_info->closed_subpath=MagickTrue;
6927 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6929 p->primitive=primitive_info->primitive;
6935 static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
6936 const size_t number_vertices,const double offset)
6953 for (i=1; i < (ssize_t) number_vertices; i++)
6955 dx=primitive_info[0].point.x-primitive_info[i].point.x;
6956 dy=primitive_info[0].point.y-primitive_info[i].point.y;
6957 if ((fabs((double) dx) >= MagickEpsilon) ||
6958 (fabs((double) dy) >= MagickEpsilon))
6961 if (i == (ssize_t) number_vertices)
6962 i=(ssize_t) number_vertices-1L;
6963 distance=hypot((double) dx,(double) dy);
6964 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
6965 dx*(distance+offset)/distance);
6966 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
6967 dy*(distance+offset)/distance);
6968 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
6970 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
6971 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
6972 if ((fabs((double) dx) >= MagickEpsilon) ||
6973 (fabs((double) dy) >= MagickEpsilon))
6976 distance=hypot((double) dx,(double) dy);
6977 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
6978 dx*(distance+offset)/distance);
6979 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
6980 dy*(distance+offset)/distance);
6984 static PrimitiveInfo *TraceStrokePolygon(const Image *image,
6985 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
6987 #define CheckPathExtent(pad) \
6988 if ((ssize_t) (q+(pad)) >= (ssize_t) max_strokes) \
6990 if (~max_strokes < (pad)) \
6992 path_p=(PointInfo *) RelinquishMagickMemory(path_p); \
6993 path_q=(PointInfo *) RelinquishMagickMemory(path_q); \
6997 max_strokes+=(pad); \
6998 path_p=(PointInfo *) ResizeQuantumMemory(path_p,max_strokes, \
7000 path_q=(PointInfo *) ResizeQuantumMemory(path_q,max_strokes, \
7003 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL)) \
7005 if (path_p != (PointInfo *) NULL) \
7006 path_p=(PointInfo *) RelinquishMagickMemory(path_p); \
7007 if (path_q != (PointInfo *) NULL) \
7008 path_q=(PointInfo *) RelinquishMagickMemory(path_q); \
7009 polygon_primitive=(PrimitiveInfo *) \
7010 RelinquishMagickMemory(polygon_primitive); \
7011 return((PrimitiveInfo *) NULL); \
7015 typedef struct _LineSegment
7031 inverse_slope = {0,0},
7067 number_vertices=primitive_info->coordinates;
7068 max_strokes=2*number_vertices+6*BezierQuantum+360;
7069 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7070 number_vertices+2UL,sizeof(*polygon_primitive));
7071 if (polygon_primitive == (PrimitiveInfo *) NULL)
7072 return((PrimitiveInfo *) NULL);
7073 (void) memcpy(polygon_primitive,primitive_info,(size_t) number_vertices*
7074 sizeof(*polygon_primitive));
7075 closed_path=primitive_info[0].closed_subpath;
7076 if (((draw_info->linejoin == RoundJoin) ||
7077 (draw_info->linejoin == MiterJoin)) && (closed_path != MagickFalse))
7079 polygon_primitive[number_vertices]=primitive_info[1];
7082 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
7084 Compute the slope for the first line segment, p.
7088 for (n=1; n < (ssize_t) number_vertices; n++)
7090 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
7091 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
7092 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
7095 if (n == (ssize_t) number_vertices)
7097 if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
7100 Zero length subpath.
7102 stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
7103 sizeof(*stroke_polygon));
7104 stroke_polygon[0]=polygon_primitive[0];
7105 stroke_polygon[0].coordinates=0;
7106 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7108 return(stroke_polygon);
7110 n=(ssize_t) number_vertices-1L;
7112 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
7114 if (path_p == (PointInfo *) NULL)
7116 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7118 return((PrimitiveInfo *) NULL);
7120 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
7122 if (path_q == (PointInfo *) NULL)
7124 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
7125 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7127 return((PrimitiveInfo *) NULL);
7130 inverse_slope.p=0.0;
7131 if (fabs(dx.p) < MagickEpsilon)
7134 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7136 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7139 if (fabs(dy.p) < MagickEpsilon)
7142 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7144 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7149 inverse_slope.p=(-1.0/slope.p);
7151 mid=ExpandAffine(&draw_info->affine)*SaneStrokeWidth(image,draw_info)/2.0;
7152 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
7153 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
7154 (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
7155 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
7156 offset.y=(double) (offset.x*inverse_slope.p);
7157 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
7159 box_p[0].x=polygon_primitive[0].point.x-offset.x;
7160 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
7161 box_p[1].x=polygon_primitive[n].point.x-offset.x;
7162 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
7163 box_q[0].x=polygon_primitive[0].point.x+offset.x;
7164 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
7165 box_q[1].x=polygon_primitive[n].point.x+offset.x;
7166 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
7170 box_p[0].x=polygon_primitive[0].point.x+offset.x;
7171 box_p[0].y=polygon_primitive[0].point.y+offset.y;
7172 box_p[1].x=polygon_primitive[n].point.x+offset.x;
7173 box_p[1].y=polygon_primitive[n].point.y+offset.y;
7174 box_q[0].x=polygon_primitive[0].point.x-offset.x;
7175 box_q[0].y=polygon_primitive[0].point.y-offset.y;
7176 box_q[1].x=polygon_primitive[n].point.x-offset.x;
7177 box_q[1].y=polygon_primitive[n].point.y-offset.y;
7180 Create strokes for the line join attribute: bevel, miter, round.
7184 path_q[p++]=box_q[0];
7185 path_p[q++]=box_p[0];
7186 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
7189 Compute the slope for this line segment, q.
7191 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
7192 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
7193 dot_product=dx.q*dx.q+dy.q*dy.q;
7194 if (dot_product < 0.25)
7197 inverse_slope.q=0.0;
7198 if (fabs(dx.q) < MagickEpsilon)
7201 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7203 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7206 if (fabs(dy.q) < MagickEpsilon)
7209 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7211 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7216 inverse_slope.q=(-1.0/slope.q);
7218 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
7219 offset.y=(double) (offset.x*inverse_slope.q);
7220 dot_product=dy.q*offset.x-dx.q*offset.y;
7221 if (dot_product > 0.0)
7223 box_p[2].x=polygon_primitive[n].point.x-offset.x;
7224 box_p[2].y=polygon_primitive[n].point.y-offset.y;
7225 box_p[3].x=polygon_primitive[i].point.x-offset.x;
7226 box_p[3].y=polygon_primitive[i].point.y-offset.y;
7227 box_q[2].x=polygon_primitive[n].point.x+offset.x;
7228 box_q[2].y=polygon_primitive[n].point.y+offset.y;
7229 box_q[3].x=polygon_primitive[i].point.x+offset.x;
7230 box_q[3].y=polygon_primitive[i].point.y+offset.y;
7234 box_p[2].x=polygon_primitive[n].point.x+offset.x;
7235 box_p[2].y=polygon_primitive[n].point.y+offset.y;
7236 box_p[3].x=polygon_primitive[i].point.x+offset.x;
7237 box_p[3].y=polygon_primitive[i].point.y+offset.y;
7238 box_q[2].x=polygon_primitive[n].point.x-offset.x;
7239 box_q[2].y=polygon_primitive[n].point.y-offset.y;
7240 box_q[3].x=polygon_primitive[i].point.x-offset.x;
7241 box_q[3].y=polygon_primitive[i].point.y-offset.y;
7243 if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
7250 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
7251 box_p[3].y)/(slope.p-slope.q));
7252 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
7253 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
7254 box_q[3].y)/(slope.p-slope.q));
7255 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
7257 CheckPathExtent(6*BezierQuantum+360);
7258 dot_product=dx.q*dy.p-dx.p*dy.q;
7259 if (dot_product <= 0.0)
7260 switch (draw_info->linejoin)
7264 path_q[q++]=box_q[1];
7265 path_q[q++]=box_q[2];
7266 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7267 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7268 if (dot_product <= miterlimit)
7269 path_p[p++]=box_p[4];
7272 path_p[p++]=box_p[1];
7273 path_p[p++]=box_p[2];
7279 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7280 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7281 if (dot_product <= miterlimit)
7283 path_q[q++]=box_q[4];
7284 path_p[p++]=box_p[4];
7288 path_q[q++]=box_q[1];
7289 path_q[q++]=box_q[2];
7290 path_p[p++]=box_p[1];
7291 path_p[p++]=box_p[2];
7297 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7298 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7299 if (dot_product <= miterlimit)
7300 path_p[p++]=box_p[4];
7303 path_p[p++]=box_p[1];
7304 path_p[p++]=box_p[2];
7306 center=polygon_primitive[n].point;
7307 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
7308 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
7309 if (theta.q < theta.p)
7310 theta.q+=2.0*MagickPI;
7311 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
7312 (2.0*sqrt((double) (1.0/mid)))));
7313 CheckPathExtent(arc_segments+6*BezierQuantum+360);
7314 path_q[q].x=box_q[1].x;
7315 path_q[q].y=box_q[1].y;
7317 for (j=1; j < (ssize_t) arc_segments; j++)
7319 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7320 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
7321 (theta.p+delta_theta),DegreesToRadians(360.0))));
7322 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
7323 (theta.p+delta_theta),DegreesToRadians(360.0))));
7326 path_q[q++]=box_q[2];
7333 switch (draw_info->linejoin)
7337 path_p[p++]=box_p[1];
7338 path_p[p++]=box_p[2];
7339 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7340 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7341 if (dot_product <= miterlimit)
7342 path_q[q++]=box_q[4];
7345 path_q[q++]=box_q[1];
7346 path_q[q++]=box_q[2];
7352 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7353 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7354 if (dot_product <= miterlimit)
7356 path_q[q++]=box_q[4];
7357 path_p[p++]=box_p[4];
7361 path_q[q++]=box_q[1];
7362 path_q[q++]=box_q[2];
7363 path_p[p++]=box_p[1];
7364 path_p[p++]=box_p[2];
7370 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7371 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7372 if (dot_product <= miterlimit)
7373 path_q[q++]=box_q[4];
7376 path_q[q++]=box_q[1];
7377 path_q[q++]=box_q[2];
7379 center=polygon_primitive[n].point;
7380 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
7381 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
7382 if (theta.p < theta.q)
7383 theta.p+=2.0*MagickPI;
7384 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
7385 (2.0*sqrt((double) (1.0/mid)))));
7386 CheckPathExtent(arc_segments+6*BezierQuantum+360);
7387 path_p[p++]=box_p[1];
7388 for (j=1; j < (ssize_t) arc_segments; j++)
7390 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7391 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
7392 (theta.p+delta_theta),DegreesToRadians(360.0))));
7393 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
7394 (theta.p+delta_theta),DegreesToRadians(360.0))));
7397 path_p[p++]=box_p[2];
7404 inverse_slope.p=inverse_slope.q;
7413 path_p[p++]=box_p[1];
7414 path_q[q++]=box_q[1];
7416 Trace stroked polygon.
7418 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7419 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
7420 if (stroke_polygon != (PrimitiveInfo *) NULL)
7422 for (i=0; i < (ssize_t) p; i++)
7424 stroke_polygon[i]=polygon_primitive[0];
7425 stroke_polygon[i].point=path_p[i];
7427 if (closed_path != MagickFalse)
7429 stroke_polygon[i]=polygon_primitive[0];
7430 stroke_polygon[i].point=stroke_polygon[0].point;
7433 for ( ; i < (ssize_t) (p+q+closed_path); i++)
7435 stroke_polygon[i]=polygon_primitive[0];
7436 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
7438 if (closed_path != MagickFalse)
7440 stroke_polygon[i]=polygon_primitive[0];
7441 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
7444 stroke_polygon[i]=polygon_primitive[0];
7445 stroke_polygon[i].point=stroke_polygon[0].point;
7447 stroke_polygon[i].primitive=UndefinedPrimitive;
7448 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
7450 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
7451 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
7452 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
7453 return(stroke_polygon);