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/constitute-private.h"
62#include "MagickCore/draw.h"
63#include "MagickCore/draw-private.h"
64#include "MagickCore/enhance.h"
65#include "MagickCore/exception.h"
66#include "MagickCore/exception-private.h"
67#include "MagickCore/gem.h"
68#include "MagickCore/geometry.h"
69#include "MagickCore/image-private.h"
70#include "MagickCore/list.h"
71#include "MagickCore/log.h"
72#include "MagickCore/magick.h"
73#include "MagickCore/memory-private.h"
74#include "MagickCore/monitor.h"
75#include "MagickCore/monitor-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel-accessor.h"
79#include "MagickCore/property.h"
80#include "MagickCore/resample.h"
81#include "MagickCore/resample-private.h"
82#include "MagickCore/resource_.h"
83#include "MagickCore/splay-tree.h"
84#include "MagickCore/string_.h"
85#include "MagickCore/string-private.h"
86#include "MagickCore/thread-private.h"
87#include "MagickCore/token.h"
88#include "MagickCore/transform-private.h"
89#include "MagickCore/utility.h"
94#define AntialiasThreshold (1.0/3.0)
95#define BezierQuantum 200
96#define PrimitiveExtentPad 4296.0
97#define MaxBezierCoordinates 67108864
98#define ThrowPointExpectedException(token,exception) \
100 (void) ThrowMagickException(exception,GetMagickModule(),DrawError, \
101 "NonconformingDrawingPrimitiveDefinition","`%s'",token); \
102 status=MagickFalse; \
192 *DrawClippingMask(Image *,
const DrawInfo *,
const char *,
const char *,
195static MagickBooleanType
196 DrawStrokePolygon(Image *,
const DrawInfo *,
const PrimitiveInfo *,
198 RenderMVGContent(Image *,
const DrawInfo *,
const size_t,ExceptionInfo *),
199 TraceArc(MVGInfo *,
const PointInfo,
const PointInfo,
const PointInfo),
200 TraceArcPath(MVGInfo *,
const PointInfo,
const PointInfo,
const PointInfo,
201 const double,
const MagickBooleanType,
const MagickBooleanType),
202 TraceBezier(MVGInfo *,
const size_t),
203 TraceCircle(MVGInfo *,
const PointInfo,
const PointInfo),
204 TraceEllipse(MVGInfo *,
const PointInfo,
const PointInfo,
const PointInfo),
205 TraceLine(PrimitiveInfo *,
const PointInfo,
const PointInfo),
206 TraceRectangle(PrimitiveInfo *,
const PointInfo,
const PointInfo),
207 TraceRoundRectangle(MVGInfo *,
const PointInfo,
const PointInfo,PointInfo),
208 TraceSquareLinecap(PrimitiveInfo *,
const size_t,
const double);
211 *TraceStrokePolygon(
const DrawInfo *,
const PrimitiveInfo *,ExceptionInfo *);
214 TracePath(MVGInfo *,
const char *,ExceptionInfo *);
234MagickExport DrawInfo *AcquireDrawInfo(
void)
239 draw_info=(DrawInfo *) AcquireCriticalMemory(
sizeof(*draw_info));
240 GetDrawInfo((ImageInfo *) NULL,draw_info);
271MagickExport DrawInfo *CloneDrawInfo(
const ImageInfo *image_info,
272 const DrawInfo *draw_info)
280 clone_info=(DrawInfo *) AcquireCriticalMemory(
sizeof(*clone_info));
281 GetDrawInfo(image_info,clone_info);
282 if (draw_info == (DrawInfo *) NULL)
284 exception=AcquireExceptionInfo();
285 if (draw_info->id != (
char *) NULL)
286 (void) CloneString(&clone_info->id,draw_info->id);
287 if (draw_info->primitive != (
char *) NULL)
288 (void) CloneString(&clone_info->primitive,draw_info->primitive);
289 if (draw_info->geometry != (
char *) NULL)
290 (void) CloneString(&clone_info->geometry,draw_info->geometry);
291 clone_info->compliance=draw_info->compliance;
292 clone_info->viewbox=draw_info->viewbox;
293 clone_info->affine=draw_info->affine;
294 clone_info->gravity=draw_info->gravity;
295 clone_info->fill=draw_info->fill;
296 clone_info->stroke=draw_info->stroke;
297 clone_info->stroke_width=draw_info->stroke_width;
298 if (draw_info->fill_pattern != (Image *) NULL)
299 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
301 if (draw_info->stroke_pattern != (Image *) NULL)
302 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
303 MagickTrue,exception);
304 clone_info->stroke_antialias=draw_info->stroke_antialias;
305 clone_info->text_antialias=draw_info->text_antialias;
306 clone_info->fill_rule=draw_info->fill_rule;
307 clone_info->linecap=draw_info->linecap;
308 clone_info->linejoin=draw_info->linejoin;
309 clone_info->miterlimit=draw_info->miterlimit;
310 clone_info->dash_offset=draw_info->dash_offset;
311 clone_info->decorate=draw_info->decorate;
312 clone_info->compose=draw_info->compose;
313 if (draw_info->text != (
char *) NULL)
314 (void) CloneString(&clone_info->text,draw_info->text);
315 if (draw_info->font != (
char *) NULL)
316 (void) CloneString(&clone_info->font,draw_info->font);
317 if (draw_info->metrics != (
char *) NULL)
318 (void) CloneString(&clone_info->metrics,draw_info->metrics);
319 if (draw_info->family != (
char *) NULL)
320 (void) CloneString(&clone_info->family,draw_info->family);
321 clone_info->style=draw_info->style;
322 clone_info->stretch=draw_info->stretch;
323 clone_info->weight=draw_info->weight;
324 if (draw_info->encoding != (
char *) NULL)
325 (void) CloneString(&clone_info->encoding,draw_info->encoding);
326 clone_info->pointsize=draw_info->pointsize;
327 clone_info->kerning=draw_info->kerning;
328 clone_info->interline_spacing=draw_info->interline_spacing;
329 clone_info->interword_spacing=draw_info->interword_spacing;
330 clone_info->direction=draw_info->direction;
331 clone_info->word_break=draw_info->word_break;
332 if (draw_info->density != (
char *) NULL)
333 (void) CloneString(&clone_info->density,draw_info->density);
334 clone_info->align=draw_info->align;
335 clone_info->undercolor=draw_info->undercolor;
336 clone_info->border_color=draw_info->border_color;
337 if (draw_info->server_name != (
char *) NULL)
338 (void) CloneString(&clone_info->server_name,draw_info->server_name);
339 if (draw_info->dash_pattern != (
double *) NULL)
344 for (x=0; fabs(draw_info->dash_pattern[x]) >= MagickEpsilon; x++) ;
345 clone_info->dash_pattern=(
double *) AcquireQuantumMemory((
size_t) (x+1),
346 sizeof(*clone_info->dash_pattern));
347 if (clone_info->dash_pattern == (
double *) NULL)
348 ThrowFatalException(ResourceLimitFatalError,
349 "UnableToAllocateDashPattern");
350 (void) memset(clone_info->dash_pattern,0,(
size_t) (x+1)*
351 sizeof(*clone_info->dash_pattern));
352 (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(
size_t)
353 x*
sizeof(*clone_info->dash_pattern));
355 clone_info->gradient=draw_info->gradient;
356 if (draw_info->gradient.stops != (StopInfo *) NULL)
361 number_stops=clone_info->gradient.number_stops;
362 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((
size_t)
363 number_stops,
sizeof(*clone_info->gradient.stops));
364 if (clone_info->gradient.stops == (StopInfo *) NULL)
365 ThrowFatalException(ResourceLimitFatalError,
366 "UnableToAllocateDashPattern");
367 (void) memcpy(clone_info->gradient.stops,draw_info->gradient.stops,
368 (
size_t) number_stops*
sizeof(*clone_info->gradient.stops));
370 clone_info->bounds=draw_info->bounds;
371 clone_info->fill_alpha=draw_info->fill_alpha;
372 clone_info->stroke_alpha=draw_info->stroke_alpha;
373 clone_info->element_reference=draw_info->element_reference;
374 clone_info->clip_path=draw_info->clip_path;
375 clone_info->clip_units=draw_info->clip_units;
376 if (draw_info->clip_mask != (
char *) NULL)
377 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
378 if (draw_info->clipping_mask != (Image *) NULL)
379 clone_info->clipping_mask=CloneImage(draw_info->clipping_mask,0,0,
380 MagickTrue,exception);
381 if (draw_info->composite_mask != (Image *) NULL)
382 clone_info->composite_mask=CloneImage(draw_info->composite_mask,0,0,
383 MagickTrue,exception);
384 clone_info->render=draw_info->render;
385 clone_info->debug=draw_info->debug;
386 exception=DestroyExceptionInfo(exception);
421static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
426 if (polygon_info->edges != (EdgeInfo *) NULL)
428 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
429 if (polygon_info->edges[i].points != (PointInfo *) NULL)
430 polygon_info->edges[i].points=(PointInfo *)
431 RelinquishMagickMemory(polygon_info->edges[i].points);
432 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(
433 polygon_info->edges);
435 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
437#if defined(__cplusplus) || defined(c_plusplus)
441static int DrawCompareEdges(
const void *p_edge,
const void *q_edge)
443#define DrawCompareEdge(p,q) \
445 if (((p)-(q)) < 0.0) \
447 if (((p)-(q)) > 0.0) \
458 p=((
const EdgeInfo *) p_edge)->points;
459 q=((
const EdgeInfo *) q_edge)->points;
460 DrawCompareEdge(p[0].y,q[0].y);
461 DrawCompareEdge(p[0].x,q[0].x);
462 DrawCompareEdge((p[1].x-p[0].x)*(q[1].y-q[0].y),(p[1].y-p[0].y)*
464 DrawCompareEdge(p[1].y,q[1].y);
465 DrawCompareEdge(p[1].x,q[1].x);
469#if defined(__cplusplus) || defined(c_plusplus)
473static void LogPolygonInfo(
const PolygonInfo *polygon_info)
482 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" begin active-edge");
483 p=polygon_info->edges;
484 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
486 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" edge %.20g:",
488 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" direction: %s",
489 p->direction != MagickFalse ?
"down" :
"up");
490 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" ghostline: %s",
491 p->ghostline != MagickFalse ?
"transparent" :
"opaque");
492 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
493 " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
494 p->bounds.x2,p->bounds.y2);
495 for (j=0; j < (ssize_t) p->number_points; j++)
496 (
void) LogMagickEvent(DrawEvent,GetMagickModule(),
" %g,%g",
497 p->points[j].x,p->points[j].y);
500 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end active-edge");
503static void ReversePoints(PointInfo *points,
const size_t number_points)
511 for (i=0; i < (number_points >> 1); i++)
514 points[i]=points[number_points-(i+1)];
515 points[number_points-(i+1)]=point;
519static PolygonInfo *ConvertPathToPolygon(
const PathInfo *path_info,
520 ExceptionInfo *exception)
551 polygon_info=(PolygonInfo *) AcquireMagickMemory(
sizeof(*polygon_info));
552 if (polygon_info == (PolygonInfo *) NULL)
554 (void) ThrowMagickException(exception,GetMagickModule(),
555 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
556 return((PolygonInfo *) NULL);
559 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges,
560 sizeof(*polygon_info->edges));
561 if (polygon_info->edges == (EdgeInfo *) NULL)
563 (void) ThrowMagickException(exception,GetMagickModule(),
564 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
565 return(DestroyPolygonInfo(polygon_info));
567 (void) memset(polygon_info->edges,0,number_edges*
568 sizeof(*polygon_info->edges));
571 ghostline=MagickFalse;
574 points=(PointInfo *) NULL;
575 (void) memset(&point,0,
sizeof(point));
576 (void) memset(&bounds,0,
sizeof(bounds));
577 polygon_info->edges[edge].number_points=(size_t) n;
578 polygon_info->edges[edge].scanline=0.0;
579 polygon_info->edges[edge].highwater=0;
580 polygon_info->edges[edge].ghostline=ghostline;
581 polygon_info->edges[edge].direction=(ssize_t) direction;
582 polygon_info->edges[edge].points=points;
583 polygon_info->edges[edge].bounds=bounds;
584 polygon_info->number_edges=0;
585 for (i=0; path_info[i].code != EndCode; i++)
587 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
588 (path_info[i].code == GhostlineCode))
593 if ((points != (PointInfo *) NULL) && (n >= 2))
595 if (edge == number_edges)
598 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
599 polygon_info->edges,(
size_t) number_edges,
600 sizeof(*polygon_info->edges));
601 if (polygon_info->edges == (EdgeInfo *) NULL)
603 (void) ThrowMagickException(exception,GetMagickModule(),
604 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
605 points=(PointInfo *) RelinquishMagickMemory(points);
606 return(DestroyPolygonInfo(polygon_info));
609 polygon_info->edges[edge].number_points=(size_t) n;
610 polygon_info->edges[edge].scanline=(-1.0);
611 polygon_info->edges[edge].highwater=0;
612 polygon_info->edges[edge].ghostline=ghostline;
613 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
615 ReversePoints(points,(
size_t) n);
616 polygon_info->edges[edge].points=points;
617 polygon_info->edges[edge].bounds=bounds;
618 polygon_info->edges[edge].bounds.y1=points[0].y;
619 polygon_info->edges[edge].bounds.y2=points[n-1].y;
620 points=(PointInfo *) NULL;
621 ghostline=MagickFalse;
623 polygon_info->number_edges=edge;
625 if (points == (PointInfo *) NULL)
628 points=(PointInfo *) AcquireQuantumMemory((
size_t) number_points,
630 if (points == (PointInfo *) NULL)
632 (void) ThrowMagickException(exception,GetMagickModule(),
633 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
634 return(DestroyPolygonInfo(polygon_info));
637 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
638 point=path_info[i].point;
649 next_direction=((path_info[i].point.y > point.y) ||
650 ((fabs(path_info[i].point.y-point.y) < MagickEpsilon) &&
651 (path_info[i].point.x > point.x))) ? 1 : -1;
652 if ((points != (PointInfo *) NULL) && (direction != 0) &&
653 (direction != next_direction))
659 if (edge == number_edges)
662 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
663 polygon_info->edges,(
size_t) number_edges,
664 sizeof(*polygon_info->edges));
665 if (polygon_info->edges == (EdgeInfo *) NULL)
667 (void) ThrowMagickException(exception,GetMagickModule(),
668 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
669 points=(PointInfo *) RelinquishMagickMemory(points);
670 return(DestroyPolygonInfo(polygon_info));
673 polygon_info->edges[edge].number_points=(size_t) n;
674 polygon_info->edges[edge].scanline=(-1.0);
675 polygon_info->edges[edge].highwater=0;
676 polygon_info->edges[edge].ghostline=ghostline;
677 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
679 ReversePoints(points,(
size_t) n);
680 polygon_info->edges[edge].points=points;
681 polygon_info->edges[edge].bounds=bounds;
682 polygon_info->edges[edge].bounds.y1=points[0].y;
683 polygon_info->edges[edge].bounds.y2=points[n-1].y;
684 polygon_info->number_edges=edge+1;
685 points=(PointInfo *) NULL;
687 points=(PointInfo *) AcquireQuantumMemory((
size_t) number_points,
689 if (points == (PointInfo *) NULL)
691 (void) ThrowMagickException(exception,GetMagickModule(),
692 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
693 return(DestroyPolygonInfo(polygon_info));
696 ghostline=MagickFalse;
702 direction=next_direction;
703 if (points == (PointInfo *) NULL)
705 if (n == (ssize_t) number_points)
708 points=(PointInfo *) ResizeQuantumMemory(points,(
size_t) number_points,
710 if (points == (PointInfo *) NULL)
712 (void) ThrowMagickException(exception,GetMagickModule(),
713 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
714 return(DestroyPolygonInfo(polygon_info));
717 point=path_info[i].point;
719 if (point.x < bounds.x1)
721 if (point.x > bounds.x2)
725 if (points != (PointInfo *) NULL)
728 points=(PointInfo *) RelinquishMagickMemory(points);
731 if (edge == number_edges)
734 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
735 polygon_info->edges,(
size_t) number_edges,
736 sizeof(*polygon_info->edges));
737 if (polygon_info->edges == (EdgeInfo *) NULL)
739 (void) ThrowMagickException(exception,GetMagickModule(),
740 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
741 return(DestroyPolygonInfo(polygon_info));
744 polygon_info->edges[edge].number_points=(size_t) n;
745 polygon_info->edges[edge].scanline=(-1.0);
746 polygon_info->edges[edge].highwater=0;
747 polygon_info->edges[edge].ghostline=ghostline;
748 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
750 ReversePoints(points,(
size_t) n);
751 polygon_info->edges[edge].points=points;
752 polygon_info->edges[edge].bounds=bounds;
753 polygon_info->edges[edge].bounds.y1=points[0].y;
754 polygon_info->edges[edge].bounds.y2=points[n-1].y;
755 points=(PointInfo *) NULL;
756 ghostline=MagickFalse;
758 polygon_info->number_edges=edge;
761 polygon_info->number_edges=edge;
762 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(polygon_info->edges,
763 polygon_info->number_edges,
sizeof(*polygon_info->edges));
764 if (polygon_info->edges == (EdgeInfo *) NULL)
766 (void) ThrowMagickException(exception,GetMagickModule(),
767 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
768 return(DestroyPolygonInfo(polygon_info));
770 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
775 edge_info=polygon_info->edges+i;
776 edge_info->points=(PointInfo *) ResizeQuantumMemory(edge_info->points,
777 edge_info->number_points,
sizeof(*edge_info->points));
778 if (edge_info->points == (PointInfo *) NULL)
780 (void) ThrowMagickException(exception,GetMagickModule(),
781 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
782 return(DestroyPolygonInfo(polygon_info));
785 qsort(polygon_info->edges,(
size_t) polygon_info->number_edges,
786 sizeof(*polygon_info->edges),DrawCompareEdges);
787 if ((GetLogEventMask() & DrawEvent) != 0)
788 LogPolygonInfo(polygon_info);
789 return(polygon_info);
822static void LogPathInfo(
const PathInfo *path_info)
827 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" begin vector-path");
828 for (p=path_info; p->code != EndCode; p++)
829 (
void) LogMagickEvent(DrawEvent,GetMagickModule(),
830 " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
831 "moveto ghostline" : p->code == OpenCode ?
"moveto open" :
832 p->code == MoveToCode ?
"moveto" : p->code == LineToCode ?
"lineto" :
834 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end vector-path");
837static PathInfo *ConvertPrimitiveToPath(
const PrimitiveInfo *primitive_info,
838 ExceptionInfo *exception)
864 switch (primitive_info->primitive)
871 return((PathInfo *) NULL);
875 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
876 path_info=(PathInfo *) AcquireQuantumMemory((
size_t) (3UL*i+1UL),
878 if (path_info == (PathInfo *) NULL)
880 (void) ThrowMagickException(exception,GetMagickModule(),
881 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
882 return((PathInfo *) NULL);
885 closed_subpath=MagickFalse;
892 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
895 if (coordinates <= 0)
900 coordinates=primitive_info[i].coordinates;
901 p=primitive_info[i].point;
904 closed_subpath=primitive_info[i].closed_subpath;
907 if ((code == MoveToCode) || (coordinates <= 0) ||
908 (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
909 (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
914 path_info[n].code=code;
915 path_info[n].point=primitive_info[i].point;
916 q=primitive_info[i].point;
921 if (closed_subpath != MagickFalse)
923 closed_subpath=MagickFalse;
929 path_info[start].code=OpenCode;
930 path_info[n].code=GhostlineCode;
931 path_info[n].point=primitive_info[i].point;
933 path_info[n].code=LineToCode;
934 path_info[n].point=p;
937 path_info[n].code=EndCode;
938 path_info[n].point.x=0.0;
939 path_info[n].point.y=0.0;
940 if (IsEventLogging() != MagickFalse)
941 LogPathInfo(path_info);
942 path_info=(PathInfo *) ResizeQuantumMemory(path_info,(
size_t) (n+1),
969MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
971 assert(draw_info != (DrawInfo *) NULL);
972 assert(draw_info->signature == MagickCoreSignature);
973 if (IsEventLogging() != MagickFalse)
974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
975 if (draw_info->id != (
char *) NULL)
976 draw_info->id=DestroyString(draw_info->id);
977 if (draw_info->primitive != (
char *) NULL)
978 draw_info->primitive=DestroyString(draw_info->primitive);
979 if (draw_info->text != (
char *) NULL)
980 draw_info->text=DestroyString(draw_info->text);
981 if (draw_info->geometry != (
char *) NULL)
982 draw_info->geometry=DestroyString(draw_info->geometry);
983 if (draw_info->fill_pattern != (Image *) NULL)
984 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
985 if (draw_info->stroke_pattern != (Image *) NULL)
986 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
987 if (draw_info->font != (
char *) NULL)
988 draw_info->font=DestroyString(draw_info->font);
989 if (draw_info->metrics != (
char *) NULL)
990 draw_info->metrics=DestroyString(draw_info->metrics);
991 if (draw_info->family != (
char *) NULL)
992 draw_info->family=DestroyString(draw_info->family);
993 if (draw_info->encoding != (
char *) NULL)
994 draw_info->encoding=DestroyString(draw_info->encoding);
995 if (draw_info->density != (
char *) NULL)
996 draw_info->density=DestroyString(draw_info->density);
997 if (draw_info->server_name != (
char *) NULL)
998 draw_info->server_name=(
char *)
999 RelinquishMagickMemory(draw_info->server_name);
1000 if (draw_info->dash_pattern != (
double *) NULL)
1001 draw_info->dash_pattern=(
double *) RelinquishMagickMemory(
1002 draw_info->dash_pattern);
1003 if (draw_info->gradient.stops != (StopInfo *) NULL)
1004 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
1005 draw_info->gradient.stops);
1006 if (draw_info->clip_mask != (
char *) NULL)
1007 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
1008 if (draw_info->clipping_mask != (Image *) NULL)
1009 draw_info->clipping_mask=DestroyImage(draw_info->clipping_mask);
1010 if (draw_info->composite_mask != (Image *) NULL)
1011 draw_info->composite_mask=DestroyImage(draw_info->composite_mask);
1012 if (draw_info->image_info != (ImageInfo *) NULL)
1013 draw_info->image_info=DestroyImageInfo(draw_info->image_info);
1014 draw_info->signature=(~MagickCoreSignature);
1015 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
1050static SegmentInfo AffineEdge(
const Image *image,
const AffineMatrix *affine,
1051 const double y,
const SegmentInfo *edge)
1066 inverse_edge.x1=edge->x1;
1067 inverse_edge.y1=edge->y1;
1068 inverse_edge.x2=edge->x2;
1069 inverse_edge.y2=edge->y2;
1070 z=affine->ry*y+affine->tx;
1071 if (affine->sx >= MagickEpsilon)
1073 intercept=(-z/affine->sx);
1075 if (x > inverse_edge.x1)
1077 intercept=(-z+(double) image->columns)/affine->sx;
1079 if (x < inverse_edge.x2)
1083 if (affine->sx < -MagickEpsilon)
1085 intercept=(-z+(double) image->columns)/affine->sx;
1087 if (x > inverse_edge.x1)
1089 intercept=(-z/affine->sx);
1091 if (x < inverse_edge.x2)
1095 if ((z < 0.0) || ((
size_t) floor(z+0.5) >= image->columns))
1097 inverse_edge.x2=edge->x1;
1098 return(inverse_edge);
1103 z=affine->sy*y+affine->ty;
1104 if (affine->rx >= MagickEpsilon)
1106 intercept=(-z/affine->rx);
1108 if (x > inverse_edge.x1)
1110 intercept=(-z+(double) image->rows)/affine->rx;
1112 if (x < inverse_edge.x2)
1116 if (affine->rx < -MagickEpsilon)
1118 intercept=(-z+(double) image->rows)/affine->rx;
1120 if (x > inverse_edge.x1)
1122 intercept=(-z/affine->rx);
1124 if (x < inverse_edge.x2)
1128 if ((z < 0.0) || ((
size_t) floor(z+0.5) >= image->rows))
1130 inverse_edge.x2=edge->x2;
1131 return(inverse_edge);
1133 return(inverse_edge);
1136static AffineMatrix InverseAffineMatrix(
const AffineMatrix *affine)
1144 determinant=MagickSafeReciprocal(affine->sx*affine->sy-affine->rx*
1146 inverse_affine.sx=determinant*affine->sy;
1147 inverse_affine.rx=determinant*(-affine->rx);
1148 inverse_affine.ry=determinant*(-affine->ry);
1149 inverse_affine.sy=determinant*affine->sx;
1150 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1152 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1154 return(inverse_affine);
1157MagickExport MagickBooleanType DrawAffineImage(Image *image,
1158 const Image *source,
const AffineMatrix *affine,ExceptionInfo *exception)
1192 assert(image != (Image *) NULL);
1193 assert(image->signature == MagickCoreSignature);
1194 if (IsEventLogging() != MagickFalse)
1195 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1196 assert(source != (
const Image *) NULL);
1197 assert(source->signature == MagickCoreSignature);
1198 assert(affine != (AffineMatrix *) NULL);
1201 extent[1].x=(double) source->columns;
1203 extent[2].x=(double) source->columns;
1204 extent[2].y=(double) source->rows;
1206 extent[3].y=(double) source->rows;
1207 for (i=0; i < 4; i++)
1213 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1214 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1218 for (i=1; i < 4; i++)
1220 if (min.x > extent[i].x)
1222 if (min.y > extent[i].y)
1224 if (max.x < extent[i].x)
1226 if (max.y < extent[i].y)
1232 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1233 return(MagickFalse);
1239 inverse_affine=InverseAffineMatrix(affine);
1242 if (edge.y2 > ((
double) image->rows-1.0))
1243 edge.y2=(double) image->rows-1.0;
1244 GetPixelInfo(image,&zero);
1245 start=CastDoubleToSsizeT(ceil(edge.y1-0.5));
1246 stop=CastDoubleToSsizeT(floor(edge.y2+0.5));
1247 source_view=AcquireVirtualCacheView(source,exception);
1248 image_view=AcquireAuthenticCacheView(image,exception);
1249#if defined(MAGICKCORE_OPENMP_SUPPORT)
1250 #pragma omp parallel for schedule(static) shared(status) \
1251 magick_number_threads(source,image,(size_t) (stop-start),2)
1253 for (y=start; y <= stop; y++)
1271 if (status == MagickFalse)
1273 inverse_edge=AffineEdge(source,&inverse_affine,(
double) y,&edge);
1274 if (inverse_edge.x2 < inverse_edge.x1)
1276 if (inverse_edge.x1 < 0.0)
1277 inverse_edge.x1=0.0;
1278 if (inverse_edge.x2 > ((
double) image->columns-1.0))
1279 inverse_edge.x2=(double) image->columns-1.0;
1280 q=GetCacheViewAuthenticPixels(image_view,CastDoubleToSsizeT(
1281 ceil(inverse_edge.x1-0.5)),y,(
size_t) CastDoubleToSsizeT(floor(
1282 inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),1,exception);
1283 if (q == (Quantum *) NULL)
1287 for (x=CastDoubleToSsizeT(ceil(inverse_edge.x1-0.5));
1288 x <= CastDoubleToSsizeT(floor(inverse_edge.x2+0.5)); x++)
1290 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1292 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1294 status=InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1295 point.x,point.y,&pixel,exception);
1296 if (status == MagickFalse)
1298 GetPixelInfoPixel(image,q,&composite);
1299 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1301 SetPixelViaPixelInfo(image,&composite,q);
1302 q+=(ptrdiff_t) GetPixelChannels(image);
1304 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1307 source_view=DestroyCacheView(source_view);
1308 image_view=DestroyCacheView(image_view);
1344static MagickBooleanType DrawBoundingRectangles(Image *image,
1345 const DrawInfo *draw_info,
const PolygonInfo *polygon_info,
1346 ExceptionInfo *exception)
1374 (void) memset(primitive_info,0,
sizeof(primitive_info));
1375 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1376 status=QueryColorCompliance(
"#000F",AllCompliance,&clone_info->fill,
1378 if (status == MagickFalse)
1380 clone_info=DestroyDrawInfo(clone_info);
1381 return(MagickFalse);
1385 if (clone_info->density != (
char *) NULL)
1393 flags=ParseGeometry(clone_info->density,&geometry_info);
1394 if ((flags & RhoValue) != 0)
1395 resolution.x=geometry_info.rho;
1396 resolution.y=resolution.x;
1397 if ((flags & SigmaValue) != 0)
1398 resolution.y=geometry_info.sigma;
1400 mid=(resolution.x/96.0)*ExpandAffine(&clone_info->affine)*
1401 clone_info->stroke_width/2.0;
1406 if (polygon_info != (PolygonInfo *) NULL)
1408 bounds=polygon_info->edges[0].bounds;
1409 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
1411 if (polygon_info->edges[i].bounds.x1 < (
double) bounds.x1)
1412 bounds.x1=polygon_info->edges[i].bounds.x1;
1413 if (polygon_info->edges[i].bounds.y1 < (
double) bounds.y1)
1414 bounds.y1=polygon_info->edges[i].bounds.y1;
1415 if (polygon_info->edges[i].bounds.x2 > (
double) bounds.x2)
1416 bounds.x2=polygon_info->edges[i].bounds.x2;
1417 if (polygon_info->edges[i].bounds.y2 > (
double) bounds.y2)
1418 bounds.y2=polygon_info->edges[i].bounds.y2;
1421 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1422 image->columns ? (
double) image->columns-1 : bounds.x1;
1424 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1425 image->rows ? (
double) image->rows-1 : bounds.y1;
1427 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1428 image->columns ? (
double) image->columns-1 : bounds.x2;
1430 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1431 image->rows ? (
double) image->rows-1 : bounds.y2;
1432 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
1434 if (polygon_info->edges[i].direction != 0)
1435 status=QueryColorCompliance(
"#f00",AllCompliance,&clone_info->stroke,
1438 status=QueryColorCompliance(
"#0f0",AllCompliance,&clone_info->stroke,
1440 if (status == MagickFalse)
1442 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1443 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1444 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1445 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1446 primitive_info[0].primitive=RectanglePrimitive;
1447 status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1448 primitive_info[0].method=ReplaceMethod;
1449 coordinates=(ssize_t) primitive_info[0].coordinates;
1450 primitive_info[coordinates].primitive=UndefinedPrimitive;
1451 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1452 if (status == MagickFalse)
1455 if (i < (ssize_t) polygon_info->number_edges)
1457 clone_info=DestroyDrawInfo(clone_info);
1458 return(status == 0 ? MagickFalse : MagickTrue);
1461 status=QueryColorCompliance(
"#00f",AllCompliance,&clone_info->stroke,
1463 if (status == MagickFalse)
1465 clone_info=DestroyDrawInfo(clone_info);
1466 return(MagickFalse);
1468 start.x=(double) (bounds.x1-mid);
1469 start.y=(double) (bounds.y1-mid);
1470 end.x=(double) (bounds.x2+mid);
1471 end.y=(double) (bounds.y2+mid);
1472 primitive_info[0].primitive=RectanglePrimitive;
1473 status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1474 primitive_info[0].method=ReplaceMethod;
1475 coordinates=(ssize_t) primitive_info[0].coordinates;
1476 primitive_info[coordinates].primitive=UndefinedPrimitive;
1477 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1478 clone_info=DestroyDrawInfo(clone_info);
1479 return(status == 0 ? MagickFalse : MagickTrue);
1511MagickExport MagickBooleanType DrawClipPath(Image *image,
1512 const DrawInfo *draw_info,
const char *
id,ExceptionInfo *exception)
1523 clip_path=GetImageArtifact(image,
id);
1524 if (clip_path == (
const char *) NULL)
1525 return(MagickFalse);
1526 clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
1528 if (clipping_mask == (Image *) NULL)
1529 return(MagickFalse);
1530 status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
1531 clipping_mask=DestroyImage(clipping_mask);
1567static Image *DrawClippingMask(Image *image,
const DrawInfo *draw_info,
1568 const char *
id,
const char *clip_path,ExceptionInfo *exception)
1583 assert(image != (Image *) NULL);
1584 assert(image->signature == MagickCoreSignature);
1585 assert(draw_info != (
const DrawInfo *) NULL);
1586 if (IsEventLogging() != MagickFalse)
1587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1588 clip_mask=AcquireImage((
const ImageInfo *) NULL,exception);
1589 status=SetImageExtent(clip_mask,image->columns,image->rows,exception);
1590 if (status == MagickFalse)
1591 return(DestroyImage(clip_mask));
1592 status=SetImageMask(clip_mask,WritePixelMask,(Image *) NULL,exception);
1593 status=QueryColorCompliance(
"#0000",AllCompliance,
1594 &clip_mask->background_color,exception);
1595 clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1596 clip_mask->background_color.alpha_trait=BlendPixelTrait;
1597 status=SetImageBackgroundColor(clip_mask,exception);
1598 if (draw_info->debug != MagickFalse)
1599 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"\nbegin clip-path %s",
1601 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1602 (void) CloneString(&clone_info->primitive,clip_path);
1603 status=QueryColorCompliance(
"#ffffff",AllCompliance,&clone_info->fill,
1605 if (clone_info->clip_mask != (
char *) NULL)
1606 clone_info->clip_mask=DestroyString(clone_info->clip_mask);
1607 status=QueryColorCompliance(
"#00000000",AllCompliance,&clone_info->stroke,
1609 clone_info->stroke_width=0.0;
1610 clone_info->alpha=OpaqueAlpha;
1611 clone_info->clip_path=MagickTrue;
1612 status=RenderMVGContent(clip_mask,clone_info,0,exception);
1613 clone_info=DestroyDrawInfo(clone_info);
1614 separate_mask=SeparateImage(clip_mask,AlphaChannel,exception);
1615 if (separate_mask == (Image *) NULL)
1619 clip_mask=DestroyImage(clip_mask);
1620 clip_mask=separate_mask;
1621 status&=(MagickStatusType) NegateImage(clip_mask,MagickFalse,exception);
1623 if (status == MagickFalse)
1624 clip_mask=DestroyImage(clip_mask);
1625 if (draw_info->debug != MagickFalse)
1626 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"end clip-path");
1661static Image *DrawCompositeMask(Image *image,
const DrawInfo *draw_info,
1662 const char *
id,
const char *mask_path,ExceptionInfo *exception)
1677 assert(image != (Image *) NULL);
1678 assert(image->signature == MagickCoreSignature);
1679 assert(draw_info != (
const DrawInfo *) NULL);
1680 if (IsEventLogging() != MagickFalse)
1681 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1682 composite_mask=AcquireImage((
const ImageInfo *) NULL,exception);
1683 status=SetImageExtent(composite_mask,image->columns,image->rows,exception);
1684 if (status == MagickFalse)
1685 return(DestroyImage(composite_mask));
1686 status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
1688 status=QueryColorCompliance(
"#0000",AllCompliance,
1689 &composite_mask->background_color,exception);
1690 composite_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1691 composite_mask->background_color.alpha_trait=BlendPixelTrait;
1692 (void) SetImageBackgroundColor(composite_mask,exception);
1693 if (draw_info->debug != MagickFalse)
1694 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"\nbegin mask-path %s",
1696 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1697 (void) CloneString(&clone_info->primitive,mask_path);
1698 status=QueryColorCompliance(
"#ffffff",AllCompliance,&clone_info->fill,
1700 status=QueryColorCompliance(
"#00000000",AllCompliance,&clone_info->stroke,
1702 clone_info->stroke_width=0.0;
1703 clone_info->alpha=OpaqueAlpha;
1704 status=RenderMVGContent(composite_mask,clone_info,0,exception);
1705 clone_info=DestroyDrawInfo(clone_info);
1706 separate_mask=SeparateImage(composite_mask,AlphaChannel,exception);
1707 if (separate_mask != (Image *) NULL)
1709 composite_mask=DestroyImage(composite_mask);
1710 composite_mask=separate_mask;
1711 status=NegateImage(composite_mask,MagickFalse,exception);
1712 if (status == MagickFalse)
1713 composite_mask=DestroyImage(composite_mask);
1715 if (status == MagickFalse)
1716 composite_mask=DestroyImage(composite_mask);
1717 if (draw_info->debug != MagickFalse)
1718 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"end mask-path");
1719 return(composite_mask);
1753static MagickBooleanType DrawDashPolygon(
const DrawInfo *draw_info,
1754 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
1784 assert(draw_info != (
const DrawInfo *) NULL);
1785 if (draw_info->debug != MagickFalse)
1786 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" begin draw-dash");
1787 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
1788 number_vertices=(size_t) i;
1789 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((
size_t)
1790 (2UL*number_vertices+32UL),
sizeof(*dash_polygon));
1791 if (dash_polygon == (PrimitiveInfo *) NULL)
1793 (void) ThrowMagickException(exception,GetMagickModule(),
1794 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
1795 return(MagickFalse);
1797 (void) memset(dash_polygon,0,(2UL*number_vertices+32UL)*
1798 sizeof(*dash_polygon));
1799 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1800 clone_info->miterlimit=0;
1801 dash_polygon[0]=primitive_info[0];
1802 dash_polygon[0].closed_subpath=MagickFalse;
1803 scale=ExpandAffine(&draw_info->affine);
1804 length=scale*draw_info->dash_pattern[0];
1805 offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
1806 scale*draw_info->dash_offset : 0.0;
1808 for (n=0; offset > 0.0; j=0)
1810 if (draw_info->dash_pattern[n] <= 0.0)
1812 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1813 if (offset > length)
1817 length=scale*draw_info->dash_pattern[n];
1820 if (offset < length)
1832 for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
1834 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1835 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1836 maximum_length=hypot(dx,dy);
1837 if (maximum_length > (
double) (MaxBezierCoordinates >> 2))
1839 if (fabs(length) < MagickEpsilon)
1841 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1843 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1845 length=scale*draw_info->dash_pattern[n];
1847 for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
1849 total_length+=length;
1850 if ((n & 0x01) != 0)
1852 dash_polygon[0]=primitive_info[0];
1853 dash_polygon[0].closed_subpath=MagickFalse;
1854 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1855 total_length*MagickSafeReciprocal(maximum_length));
1856 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1857 total_length*MagickSafeReciprocal(maximum_length));
1862 if ((j+1) > (ssize_t) number_vertices)
1864 dash_polygon[j]=primitive_info[i-1];
1865 dash_polygon[j].closed_subpath=MagickFalse;
1866 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1867 total_length*MagickSafeReciprocal(maximum_length));
1868 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1869 total_length*MagickSafeReciprocal(maximum_length));
1870 dash_polygon[j].coordinates=1;
1872 dash_polygon[0].coordinates=(size_t) j;
1873 dash_polygon[j].primitive=UndefinedPrimitive;
1874 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1875 dash_polygon,exception);
1876 if (status == MagickFalse)
1879 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1881 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1883 length=scale*draw_info->dash_pattern[n];
1885 length-=(maximum_length-total_length);
1886 if ((n & 0x01) != 0)
1888 dash_polygon[j]=primitive_info[i];
1889 dash_polygon[j].coordinates=1;
1892 if ((status != MagickFalse) && (total_length < maximum_length) &&
1893 ((n & 0x01) == 0) && (j > 1))
1895 dash_polygon[j]=primitive_info[i-1];
1896 dash_polygon[j].closed_subpath=MagickFalse;
1897 dash_polygon[j].point.x+=MagickEpsilon;
1898 dash_polygon[j].point.y+=MagickEpsilon;
1899 dash_polygon[j].coordinates=1;
1901 dash_polygon[0].coordinates=(size_t) j;
1902 dash_polygon[j].primitive=UndefinedPrimitive;
1903 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1904 dash_polygon,exception);
1906 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1907 clone_info=DestroyDrawInfo(clone_info);
1908 if (draw_info->debug != MagickFalse)
1909 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end draw-dash");
1910 return(status != 0 ? MagickTrue : MagickFalse);
1941static inline double GetStopColorOffset(
const GradientInfo *gradient,
1942 const ssize_t x,
const ssize_t y)
1944 switch (gradient->type)
1946 case UndefinedGradient:
1947 case LinearGradient:
1962 gradient_vector=(&gradient->gradient_vector);
1963 p.x=gradient_vector->x2-gradient_vector->x1;
1964 p.y=gradient_vector->y2-gradient_vector->y1;
1965 q.x=(double) x-gradient_vector->x1;
1966 q.y=(double) y-gradient_vector->y1;
1967 length=sqrt(q.x*q.x+q.y*q.y);
1968 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
1969 gamma=MagickSafeReciprocal(gamma);
1970 scale=p.x*q.x+p.y*q.y;
1971 offset=gamma*scale*length;
1974 case RadialGradient:
1979 if (gradient->spread == RepeatSpread)
1981 v.x=(double) x-gradient->center.x;
1982 v.y=(double) y-gradient->center.y;
1983 return(sqrt(v.x*v.x+v.y*v.y));
1985 v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
1986 gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
1987 gradient->angle))))*MagickSafeReciprocal(gradient->radii.x);
1988 v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
1989 gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
1990 gradient->angle))))*MagickSafeReciprocal(gradient->radii.y);
1991 return(sqrt(v.x*v.x+v.y*v.y));
1997static int StopInfoCompare(
const void *x,
const void *y)
2003 stop_1=(StopInfo *) x;
2004 stop_2=(StopInfo *) y;
2005 if (stop_1->offset > stop_2->offset)
2007 if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
2012MagickExport MagickBooleanType DrawGradientImage(Image *image,
2013 const DrawInfo *draw_info,ExceptionInfo *exception)
2048 assert(image != (Image *) NULL);
2049 assert(image->signature == MagickCoreSignature);
2050 assert(draw_info != (
const DrawInfo *) NULL);
2051 if (IsEventLogging() != MagickFalse)
2052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2053 gradient=(&draw_info->gradient);
2054 qsort(gradient->stops,gradient->number_stops,
sizeof(StopInfo),
2056 gradient_vector=(&gradient->gradient_vector);
2057 point.x=gradient_vector->x2-gradient_vector->x1;
2058 point.y=gradient_vector->y2-gradient_vector->y1;
2059 length=sqrt(point.x*point.x+point.y*point.y);
2060 bounding_box=gradient->bounding_box;
2062 GetPixelInfo(image,&zero);
2063 image_view=AcquireAuthenticCacheView(image,exception);
2064 height=(size_t) (bounding_box.y+(ssize_t) bounding_box.height);
2065#if defined(MAGICKCORE_OPENMP_SUPPORT)
2066 #pragma omp parallel for schedule(static) shared(status) \
2067 magick_number_threads(image,image,height,1)
2069 for (y=bounding_box.y; y < (ssize_t) height; y++)
2090 if (status == MagickFalse)
2092 q=GetCacheViewAuthenticPixels(image_view,bounding_box.x,y,(
size_t)
2093 bounding_box.width,1,exception);
2094 if (q == (Quantum *) NULL)
2101 offset=GetStopColorOffset(gradient,0,y);
2102 if (gradient->type != RadialGradient)
2103 offset*=MagickSafeReciprocal(length);
2104 width=(size_t) (bounding_box.x+(ssize_t) bounding_box.width);
2105 for (x=bounding_box.x; x < (ssize_t) width; x++)
2107 GetPixelInfoPixel(image,q,&pixel);
2108 switch (gradient->spread)
2110 case UndefinedSpread:
2113 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2114 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2116 offset=GetStopColorOffset(gradient,x,y);
2117 if (gradient->type != RadialGradient)
2118 offset*=MagickSafeReciprocal(length);
2120 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2121 if (offset < gradient->stops[i].offset)
2123 if ((offset < 0.0) || (i == 0))
2124 composite=gradient->stops[0].color;
2126 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
2127 composite=gradient->stops[gradient->number_stops-1].color;
2132 alpha=(offset-gradient->stops[i].offset)/
2133 (gradient->stops[j].offset-gradient->stops[i].offset);
2134 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2135 &gradient->stops[j].color,alpha,&composite);
2141 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2142 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2144 offset=GetStopColorOffset(gradient,x,y);
2145 if (gradient->type != RadialGradient)
2146 offset*=MagickSafeReciprocal(length);
2150 if ((ssize_t) fmod(offset,2.0) == 0)
2151 offset=fmod(offset,1.0);
2153 offset=1.0-fmod(offset,1.0);
2154 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2155 if (offset < gradient->stops[i].offset)
2158 composite=gradient->stops[0].color;
2160 if (i == (ssize_t) gradient->number_stops)
2161 composite=gradient->stops[gradient->number_stops-1].color;
2166 alpha=(offset-gradient->stops[i].offset)/
2167 (gradient->stops[j].offset-gradient->stops[i].offset);
2168 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2169 &gradient->stops[j].color,alpha,&composite);
2181 antialias=MagickFalse;
2183 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2184 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2186 offset=GetStopColorOffset(gradient,x,y);
2187 if (gradient->type == LinearGradient)
2189 repeat=fmod(offset,length);
2191 repeat=length-fmod(-repeat,length);
2193 repeat=fmod(offset,length);
2194 antialias=(repeat < length) && ((repeat+1.0) > length) ?
2195 MagickTrue : MagickFalse;
2196 offset=MagickSafeReciprocal(length)*repeat;
2200 repeat=fmod(offset,gradient->radius);
2202 repeat=gradient->radius-fmod(-repeat,gradient->radius);
2204 repeat=fmod(offset,gradient->radius);
2205 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
2207 offset=repeat*MagickSafeReciprocal(gradient->radius);
2210 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2211 if (offset < gradient->stops[i].offset)
2214 composite=gradient->stops[0].color;
2216 if (i == (ssize_t) gradient->number_stops)
2217 composite=gradient->stops[gradient->number_stops-1].color;
2222 alpha=(offset-gradient->stops[i].offset)/
2223 (gradient->stops[j].offset-gradient->stops[i].offset);
2224 if (antialias != MagickFalse)
2226 if (gradient->type == LinearGradient)
2227 alpha=length-repeat;
2229 alpha=gradient->radius-repeat;
2231 j=(ssize_t) gradient->number_stops-1L;
2233 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2234 &gradient->stops[j].color,alpha,&composite);
2239 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
2241 SetPixelViaPixelInfo(image,&pixel,q);
2242 q+=(ptrdiff_t) GetPixelChannels(image);
2244 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2247 image_view=DestroyCacheView(image_view);
2283static inline MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
2298 if ((mvg_info == (MVGInfo *) NULL) ||
2299 (mvg_info->primitive_info == (PrimitiveInfo **) NULL) ||
2300 (*mvg_info->primitive_info == (PrimitiveInfo *) NULL) ||
2301 (mvg_info->extent == (
size_t *) NULL))
2302 return(MagickFalse);
2303 proposed_extent=mvg_info->offset+pad+PrimitiveExtentPad+1.0;
2304 if ((proposed_extent <= 0.0) || (proposed_extent > (
double) MAGICK_SIZE_MAX))
2305 return(MagickFalse);
2306 extent=CastDoubleToSizeT(ceil(proposed_extent));
2307 if (extent <= *mvg_info->extent)
2309 if (extent > (GetMaxMemoryRequest()/
sizeof(PrimitiveInfo)))
2310 return(MagickFalse);
2311 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
2312 *mvg_info->primitive_info,extent+1,
sizeof(PrimitiveInfo));
2313 if (primitive_info == (PrimitiveInfo *) NULL)
2318 extent=(size_t) PrimitiveExtentPad;
2319 primitive_info=(PrimitiveInfo *) AcquireCriticalMemory(extent*
2320 sizeof(*primitive_info));
2321 (void) memset(primitive_info,0,extent*
sizeof(*primitive_info));
2322 *mvg_info->primitive_info=primitive_info;
2323 *mvg_info->extent=extent;
2325 ThrowMagickException(mvg_info->exception,GetMagickModule(),
2326 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
2327 return(MagickFalse);
2329 primitive_info[extent].primitive=UndefinedPrimitive;
2330 primitive_info[extent].text=(
char *) NULL;
2334 for (i=(ssize_t) *mvg_info->extent; i < (ssize_t) extent; i++)
2336 primitive_info[i].primitive=UndefinedPrimitive;
2337 primitive_info[i].text=(
char *) NULL;
2339 *mvg_info->primitive_info=primitive_info;
2340 *mvg_info->extent=extent;
2344static inline double GetDrawValue(
const char *magick_restrict
string,
2345 char **magick_restrict sentinel)
2348 **magick_restrict q;
2354 value=InterpretLocaleValue(
string,q);
2359static int MVGMacroCompare(
const void *target,
const void *source)
2365 p=(
const char *) target;
2366 q=(
const char *) source;
2367 return(strcmp(p,q));
2370static SplayTreeInfo *GetMVGMacros(
const char *primitive,
2371 ExceptionInfo *exception)
2389 if (primitive == (
const char *) NULL)
2390 return((SplayTreeInfo *) NULL);
2391 macros=NewSplayTree(MVGMacroCompare,RelinquishMagickMemory,
2392 RelinquishMagickMemory);
2393 macro=AcquireString(primitive);
2394 token=AcquireString(primitive);
2395 extent=strlen(token)+MagickPathExtent;
2396 for (q=primitive; *q !=
'\0'; )
2398 if (GetNextToken(q,&q,extent,token) < 1)
2402 if (LocaleCompare(
"push",token) == 0)
2408 (void) GetNextToken(q,&q,extent,token);
2412 name[MagickPathExtent];
2423 (void) GetNextToken(q,&q,extent,token);
2426 (void) CopyMagickString(name,token,MagickPathExtent);
2428 for (p=q; *p !=
'\0'; )
2430 if (GetNextToken(p,&p,extent,token) < 1)
2434 if (LocaleCompare(token,
"pop") == 0)
2436 end=p-strlen(token)-1;
2439 if (LocaleCompare(token,
"push") == 0)
2441 if (n++ > MagickMaxRecursionDepth)
2443 (void) ThrowMagickException(exception,GetMagickModule(),
2444 DrawError,
"VectorGraphicsNestedTooDeeply",
"`%s'",token);
2448 if ((n == 0) && (end >= start))
2451 length=(size_t) (end-start);
2456 (void) GetNextToken(p,&p,extent,token);
2459 (void) CopyMagickString(macro,start,length);
2460 (void) AddValueToSplayTree(macros,ConstantString(name),
2461 ConstantString(macro));
2469 token=DestroyString(token);
2470 macro=DestroyString(macro);
2474static inline MagickBooleanType IsValidListChar(
int c)
2476 if ((c >=
'0') && (c <=
'9'))
2490 return(MagickFalse);
2495static inline MagickBooleanType IsValidPoint(
const char *point)
2503 value=GetDrawValue(point,&p);
2504 return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
2508static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
2509 const PointInfo point)
2511 primitive_info->point=point;
2512 primitive_info->coordinates=1;
2513 primitive_info->closed_subpath=MagickFalse;
2514 primitive_info->text=(
char *) NULL;
2518static MagickBooleanType RenderMVGContent(Image *image,
2519 const DrawInfo *draw_info,
const size_t depth,ExceptionInfo *exception)
2521#define RenderImageTag "Render/Image"
2528 keyword[MagickPathExtent],
2529 geometry[MagickPathExtent],
2531 pattern[MagickPathExtent],
2595 assert(image != (Image *) NULL);
2596 assert(image->signature == MagickCoreSignature);
2597 assert(draw_info != (DrawInfo *) NULL);
2598 assert(draw_info->signature == MagickCoreSignature);
2599 if (IsEventLogging() != MagickFalse)
2600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2601 if (depth > MagickMaxRecursionDepth)
2602 ThrowBinaryException(DrawError,
"VectorGraphicsNestedTooDeeply",
2604 if ((draw_info->primitive == (
char *) NULL) ||
2605 (*draw_info->primitive ==
'\0'))
2606 return(MagickFalse);
2607 if (draw_info->debug != MagickFalse)
2608 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"begin draw-image");
2609 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2610 return(MagickFalse);
2611 if ((image->alpha_trait & BlendPixelTrait) == 0)
2613 status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2614 if (status == MagickFalse)
2615 return(MagickFalse);
2617 if ((*draw_info->primitive ==
'@') && (strlen(draw_info->primitive) > 1) &&
2618 (*(draw_info->primitive+1) !=
'-') && (depth == 0))
2619 primitive=FileToString(draw_info->primitive,~0UL,exception);
2621 primitive=AcquireString(draw_info->primitive);
2622 if (primitive == (
char *) NULL)
2623 return(MagickFalse);
2624 primitive_extent=(double) strlen(primitive);
2625 (void) SetImageArtifact(image,
"mvg:vector-graphics",primitive);
2628 stops=(StopInfo *) NULL;
2632 graphic_context=(DrawInfo **) AcquireMagickMemory(
sizeof(*graphic_context));
2633 if (graphic_context == (DrawInfo **) NULL)
2635 primitive=DestroyString(primitive);
2636 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2639 number_points=(size_t) PrimitiveExtentPad;
2640 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((
size_t)
2641 (number_points+1),
sizeof(*primitive_info));
2642 if (primitive_info == (PrimitiveInfo *) NULL)
2644 primitive=DestroyString(primitive);
2645 for ( ; n >= 0; n--)
2646 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2647 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
2648 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2651 (void) memset(primitive_info,0,(
size_t) (number_points+1)*
2652 sizeof(*primitive_info));
2653 (void) memset(&mvg_info,0,
sizeof(mvg_info));
2654 mvg_info.primitive_info=(&primitive_info);
2655 mvg_info.extent=(&number_points);
2656 mvg_info.exception=exception;
2657 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2658 graphic_context[n]->viewbox=image->page;
2659 if ((image->page.width == 0) || (image->page.height == 0))
2661 graphic_context[n]->viewbox.width=image->columns;
2662 graphic_context[n]->viewbox.height=image->rows;
2664 token=AcquireString(primitive);
2665 extent=strlen(token)+MagickPathExtent;
2669 macros=GetMVGMacros(primitive,exception);
2671 for (q=primitive; *q !=
'\0'; )
2676 if (GetNextToken(q,&q,MagickPathExtent,keyword) < 1)
2678 if (*keyword ==
'\0')
2680 if (*keyword ==
'#')
2685 while ((*q !=
'\n') && (*q !=
'\0'))
2689 p=q-strlen(keyword)-1;
2690 primitive_type=UndefinedPrimitive;
2691 current=graphic_context[n]->affine;
2692 GetAffineMatrix(&affine);
2701 if (LocaleCompare(
"affine",keyword) == 0)
2703 (void) GetNextToken(q,&q,extent,token);
2704 affine.sx=GetDrawValue(token,&next_token);
2705 if (token == next_token)
2706 ThrowPointExpectedException(token,exception);
2707 (void) GetNextToken(q,&q,extent,token);
2708 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
2709 ThrowPointExpectedException(token,exception);
2711 (void) GetNextToken(q,&q,extent,token);
2712 affine.ry=GetDrawValue(token,&next_token);
2713 if (token == next_token)
2714 ThrowPointExpectedException(token,exception);
2715 (void) GetNextToken(q,&q,extent,token);
2716 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
2717 ThrowPointExpectedException(token,exception);
2719 (void) GetNextToken(q,&q,extent,token);
2720 affine.rx=GetDrawValue(token,&next_token);
2721 if (token == next_token)
2722 ThrowPointExpectedException(token,exception);
2723 (void) GetNextToken(q,&q,extent,token);
2724 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
2725 ThrowPointExpectedException(token,exception);
2727 (void) GetNextToken(q,&q,extent,token);
2728 affine.sy=GetDrawValue(token,&next_token);
2729 if (token == next_token)
2730 ThrowPointExpectedException(token,exception);
2731 (void) GetNextToken(q,&q,extent,token);
2732 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
2733 ThrowPointExpectedException(token,exception);
2735 (void) GetNextToken(q,&q,extent,token);
2736 affine.tx=GetDrawValue(token,&next_token);
2737 if (token == next_token)
2738 ThrowPointExpectedException(token,exception);
2739 (void) GetNextToken(q,&q,extent,token);
2740 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
2741 ThrowPointExpectedException(token,exception);
2743 (void) GetNextToken(q,&q,extent,token);
2744 affine.ty=GetDrawValue(token,&next_token);
2745 if (token == next_token)
2746 ThrowPointExpectedException(token,exception);
2749 if (LocaleCompare(
"alpha",keyword) == 0)
2751 primitive_type=AlphaPrimitive;
2754 if (LocaleCompare(
"arc",keyword) == 0)
2756 primitive_type=ArcPrimitive;
2765 if (LocaleCompare(
"bezier",keyword) == 0)
2767 primitive_type=BezierPrimitive;
2770 if (LocaleCompare(
"border-color",keyword) == 0)
2772 (void) GetNextToken(q,&q,extent,token);
2773 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
2774 &graphic_context[n]->border_color,exception);
2783 if (LocaleCompare(
"class",keyword) == 0)
2788 (void) GetNextToken(q,&q,extent,token);
2789 if ((*token ==
'\0') || (*token ==
';'))
2797 for (i=0; i <= n; i++)
2798 if (LocaleCompare(token,graphic_context[i]->
id) == 0)
2802 if (classDepth++ > MagickMaxRecursionDepth)
2804 (void) ThrowMagickException(exception,GetMagickModule(),
2805 DrawError,
"VectorGraphicsNestedTooDeeply",
"`%s'",token);
2809 mvg_class=(
const char *) GetValueFromSplayTree(macros,token);
2810 if ((graphic_context[n]->render != MagickFalse) &&
2811 (mvg_class != (
const char *) NULL) && (p > primitive))
2822 (void) CloneString(&graphic_context[n]->
id,token);
2823 offset=(ssize_t) (p-primitive);
2824 elements=AcquireString(primitive);
2825 elements[offset]=
'\0';
2826 (void) ConcatenateString(&elements,mvg_class);
2827 (void) ConcatenateString(&elements,
"\n");
2828 (void) ConcatenateString(&elements,q);
2829 primitive=DestroyString(primitive);
2835 if (LocaleCompare(
"clip-path",keyword) == 0)
2843 (void) GetNextToken(q,&q,extent,token);
2849 (void) CloneString(&graphic_context[n]->clip_mask,token);
2850 clip_path=(
const char *) GetValueFromSplayTree(macros,token);
2851 if (clip_path != (
const char *) NULL)
2853 if (graphic_context[n]->clipping_mask != (Image *) NULL)
2854 graphic_context[n]->clipping_mask=
2855 DestroyImage(graphic_context[n]->clipping_mask);
2856 graphic_context[n]->clipping_mask=DrawClippingMask(image,
2857 graphic_context[n],token,clip_path,exception);
2858 if (graphic_context[n]->compliance != SVGCompliance)
2860 clip_path=(
const char *) GetValueFromSplayTree(macros,
2861 graphic_context[n]->clip_mask);
2862 if (clip_path != (
const char *) NULL)
2863 (void) SetImageArtifact(image,
2864 graphic_context[n]->clip_mask,clip_path);
2865 status&=(MagickStatusType) DrawClipPath(image,
2866 graphic_context[n],graphic_context[n]->clip_mask,
2872 if (LocaleCompare(
"clip-rule",keyword) == 0)
2877 (void) GetNextToken(q,&q,extent,token);
2878 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2880 if (fill_rule == -1)
2885 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2888 if (LocaleCompare(
"clip-units",keyword) == 0)
2893 (void) GetNextToken(q,&q,extent,token);
2894 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
2896 if (clip_units == -1)
2901 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
2902 if (clip_units == ObjectBoundingBox)
2904 GetAffineMatrix(¤t);
2905 affine.sx=draw_info->bounds.x2;
2906 affine.sy=draw_info->bounds.y2;
2907 affine.tx=draw_info->bounds.x1;
2908 affine.ty=draw_info->bounds.y1;
2913 if (LocaleCompare(
"circle",keyword) == 0)
2915 primitive_type=CirclePrimitive;
2918 if (LocaleCompare(
"color",keyword) == 0)
2920 primitive_type=ColorPrimitive;
2923 if (LocaleCompare(
"compliance",keyword) == 0)
2929 (void) GetNextToken(q,&q,extent,token);
2930 graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
2931 MagickComplianceOptions,MagickFalse,token);
2934 if (LocaleCompare(
"currentColor",keyword) == 0)
2936 (void) GetNextToken(q,&q,extent,token);
2945 if (LocaleCompare(
"decorate",keyword) == 0)
2950 (void) GetNextToken(q,&q,extent,token);
2951 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
2958 graphic_context[n]->decorate=(DecorationType) decorate;
2961 if (LocaleCompare(
"density",keyword) == 0)
2963 (void) GetNextToken(q,&q,extent,token);
2964 (void) CloneString(&graphic_context[n]->density,token);
2967 if (LocaleCompare(
"direction",keyword) == 0)
2972 (void) GetNextToken(q,&q,extent,token);
2973 direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
2975 if (direction == -1)
2978 graphic_context[n]->direction=(DirectionType) direction;
2987 if (LocaleCompare(
"ellipse",keyword) == 0)
2989 primitive_type=EllipsePrimitive;
2992 if (LocaleCompare(
"encoding",keyword) == 0)
2994 (void) GetNextToken(q,&q,extent,token);
2995 (void) CloneString(&graphic_context[n]->encoding,token);
3004 if (LocaleCompare(
"fill",keyword) == 0)
3009 (void) GetNextToken(q,&q,extent,token);
3010 if (graphic_context[n]->clip_path != MagickFalse)
3012 mvg_class=(
const char *) GetValueFromSplayTree(macros,token);
3013 if (mvg_class != (
const char *) NULL)
3015 (void) DrawPatternPath(image,draw_info,mvg_class,
3016 &graphic_context[n]->fill_pattern,exception);
3019 (void) FormatLocaleString(pattern,MagickPathExtent,
"%s",token);
3020 if (GetImageArtifact(image,pattern) != (
const char *) NULL)
3022 (void) DrawPatternPath(image,draw_info,token,
3023 &graphic_context[n]->fill_pattern,exception);
3026 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3027 &graphic_context[n]->fill,exception);
3028 if (graphic_context[n]->fill_alpha != (
double) OpaqueAlpha)
3029 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3032 if (LocaleCompare(
"fill-opacity",keyword) == 0)
3037 (void) GetNextToken(q,&q,extent,token);
3038 if (graphic_context[n]->clip_path != MagickFalse)
3040 factor=strchr(token,
'%') != (
char *) NULL ? 0.01 : 1.0;
3041 opacity=MagickMin(MagickMax(factor*
3042 GetDrawValue(token,&next_token),0.0),1.0);
3043 if (token == next_token)
3044 ThrowPointExpectedException(token,exception);
3045 if (graphic_context[n]->compliance == SVGCompliance)
3046 graphic_context[n]->fill_alpha*=opacity;
3048 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3049 if (graphic_context[n]->fill.alpha != (
double) TransparentAlpha)
3050 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3052 graphic_context[n]->fill.alpha=(MagickRealType)
3053 ClampToQuantum((
double) QuantumRange*opacity);
3054 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3057 if (LocaleCompare(
"fill-rule",keyword) == 0)
3062 (void) GetNextToken(q,&q,extent,token);
3063 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
3065 if (fill_rule == -1)
3070 graphic_context[n]->fill_rule=(FillRule) fill_rule;
3073 if (LocaleCompare(
"font",keyword) == 0)
3075 (void) GetNextToken(q,&q,extent,token);
3076 (void) CloneString(&graphic_context[n]->font,token);
3077 if (LocaleCompare(
"none",token) == 0)
3078 graphic_context[n]->font=(
char *) RelinquishMagickMemory(
3079 graphic_context[n]->font);
3082 if (LocaleCompare(
"font-family",keyword) == 0)
3084 (void) GetNextToken(q,&q,extent,token);
3085 (void) CloneString(&graphic_context[n]->family,token);
3088 if (LocaleCompare(
"font-size",keyword) == 0)
3090 (void) GetNextToken(q,&q,extent,token);
3091 graphic_context[n]->pointsize=GetDrawValue(token,&next_token);
3092 if (token == next_token)
3093 ThrowPointExpectedException(token,exception);
3096 if (LocaleCompare(
"font-stretch",keyword) == 0)
3101 (void) GetNextToken(q,&q,extent,token);
3102 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
3108 graphic_context[n]->stretch=(StretchType) stretch;
3111 if (LocaleCompare(
"font-style",keyword) == 0)
3116 (void) GetNextToken(q,&q,extent,token);
3117 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
3123 graphic_context[n]->style=(StyleType) style;
3126 if (LocaleCompare(
"font-weight",keyword) == 0)
3131 (void) GetNextToken(q,&q,extent,token);
3132 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
3134 weight=(ssize_t) StringToUnsignedLong(token);
3135 graphic_context[n]->weight=(size_t) weight;
3144 if (LocaleCompare(
"gradient-units",keyword) == 0)
3146 (void) GetNextToken(q,&q,extent,token);
3149 if (LocaleCompare(
"gravity",keyword) == 0)
3154 (void) GetNextToken(q,&q,extent,token);
3155 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
3161 graphic_context[n]->gravity=(GravityType) gravity;
3170 if (LocaleCompare(
"image",keyword) == 0)
3175 primitive_type=ImagePrimitive;
3176 (void) GetNextToken(q,&q,extent,token);
3177 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
3183 graphic_context[n]->compose=(CompositeOperator) compose;
3186 if (LocaleCompare(
"interline-spacing",keyword) == 0)
3188 (void) GetNextToken(q,&q,extent,token);
3189 graphic_context[n]->interline_spacing=GetDrawValue(token,
3191 if (token == next_token)
3192 ThrowPointExpectedException(token,exception);
3195 if (LocaleCompare(
"interword-spacing",keyword) == 0)
3197 (void) GetNextToken(q,&q,extent,token);
3198 graphic_context[n]->interword_spacing=GetDrawValue(token,
3200 if (token == next_token)
3201 ThrowPointExpectedException(token,exception);
3210 if (LocaleCompare(
"kerning",keyword) == 0)
3212 (void) GetNextToken(q,&q,extent,token);
3213 graphic_context[n]->kerning=GetDrawValue(token,&next_token);
3214 if (token == next_token)
3215 ThrowPointExpectedException(token,exception);
3224 if (LocaleCompare(
"letter-spacing",keyword) == 0)
3226 (void) GetNextToken(q,&q,extent,token);
3227 if (IsValidPoint(token) == MagickFalse)
3229 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3230 clone_info->text=AcquireString(
" ");
3231 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,&metrics,
3233 graphic_context[n]->kerning=metrics.width*
3234 GetDrawValue(token,&next_token);
3235 clone_info=DestroyDrawInfo(clone_info);
3236 if (token == next_token)
3237 ThrowPointExpectedException(token,exception);
3240 if (LocaleCompare(
"line",keyword) == 0)
3242 primitive_type=LinePrimitive;
3251 if (LocaleCompare(
"mask",keyword) == 0)
3259 (void) GetNextToken(q,&q,extent,token);
3260 mask_path=(
const char *) GetValueFromSplayTree(macros,token);
3261 if (mask_path != (
const char *) NULL)
3263 if (graphic_context[n]->composite_mask != (Image *) NULL)
3264 graphic_context[n]->composite_mask=
3265 DestroyImage(graphic_context[n]->composite_mask);
3266 graphic_context[n]->composite_mask=DrawCompositeMask(image,
3267 graphic_context[n],token,mask_path,exception);
3268 if (graphic_context[n]->compliance != SVGCompliance)
3269 status=SetImageMask(image,CompositePixelMask,
3270 graphic_context[n]->composite_mask,exception);
3280 if (LocaleCompare(
"offset",keyword) == 0)
3282 (void) GetNextToken(q,&q,extent,token);
3285 if (LocaleCompare(
"opacity",keyword) == 0)
3290 (void) GetNextToken(q,&q,extent,token);
3291 if (graphic_context[n]->clip_path != MagickFalse)
3293 factor=strchr(token,
'%') != (
char *) NULL ? 0.01 : 1.0;
3294 opacity=MagickMin(MagickMax(factor*
3295 GetDrawValue(token,&next_token),0.0),1.0);
3296 if (token == next_token)
3297 ThrowPointExpectedException(token,exception);
3298 if (graphic_context[n]->compliance == SVGCompliance)
3300 graphic_context[n]->fill_alpha*=opacity;
3301 graphic_context[n]->stroke_alpha*=opacity;
3305 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3306 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3308 if (graphic_context[n]->fill.alpha != (
double) TransparentAlpha)
3310 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3311 graphic_context[n]->stroke.alpha=
3312 graphic_context[n]->stroke_alpha;
3316 graphic_context[n]->fill.alpha=(MagickRealType)
3317 ClampToQuantum((
double) QuantumRange*opacity);
3318 graphic_context[n]->stroke.alpha=(MagickRealType)
3319 ClampToQuantum((
double) QuantumRange*opacity);
3321 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3322 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3331 if (LocaleCompare(
"path",keyword) == 0)
3333 primitive_type=PathPrimitive;
3336 if (LocaleCompare(
"point",keyword) == 0)
3338 primitive_type=PointPrimitive;
3341 if (LocaleCompare(
"polyline",keyword) == 0)
3343 primitive_type=PolylinePrimitive;
3346 if (LocaleCompare(
"polygon",keyword) == 0)
3348 primitive_type=PolygonPrimitive;
3351 if (LocaleCompare(
"pop",keyword) == 0)
3353 if (GetNextToken(q,&q,extent,token) < 1)
3355 if (LocaleCompare(
"class",token) == 0)
3357 if (LocaleCompare(
"clip-path",token) == 0)
3359 if (LocaleCompare(
"defs",token) == 0)
3362 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3366 if (LocaleCompare(
"gradient",token) == 0)
3368 if (LocaleCompare(
"graphic-context",token) == 0)
3372 (void) ThrowMagickException(exception,GetMagickModule(),
3373 DrawError,
"UnbalancedGraphicContextPushPop",
"`%s'",token);
3378 if ((graphic_context[n]->clip_mask != (
char *) NULL) &&
3379 (graphic_context[n]->compliance != SVGCompliance))
3380 if (LocaleCompare(graphic_context[n]->clip_mask,
3381 graphic_context[n-1]->clip_mask) != 0)
3382 status=SetImageMask(image,WritePixelMask,(Image *) NULL,
3384 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3388 if (LocaleCompare(
"mask",token) == 0)
3390 if (LocaleCompare(
"pattern",token) == 0)
3392 if (LocaleCompare(
"symbol",token) == 0)
3395 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3402 if (LocaleCompare(
"push",keyword) == 0)
3404 if (GetNextToken(q,&q,extent,token) < 1)
3406 if (LocaleCompare(
"class",token) == 0)
3411 for (p=q; *q !=
'\0'; )
3413 if (GetNextToken(q,&q,extent,token) < 1)
3415 if (LocaleCompare(token,
"pop") != 0)
3417 (void) GetNextToken(q,(
const char **) NULL,extent,token);
3418 if (LocaleCompare(token,
"class") != 0)
3422 (void) GetNextToken(q,&q,extent,token);
3425 if (LocaleCompare(
"clip-path",token) == 0)
3427 (void) GetNextToken(q,&q,extent,token);
3428 for (p=q; *q !=
'\0'; )
3430 if (GetNextToken(q,&q,extent,token) < 1)
3432 if (LocaleCompare(token,
"pop") != 0)
3434 (void) GetNextToken(q,(
const char **) NULL,extent,token);
3435 if (LocaleCompare(token,
"clip-path") != 0)
3439 if ((q == (
char *) NULL) || (p == (
char *) NULL) || ((q-4) < p))
3444 (void) GetNextToken(q,&q,extent,token);
3447 if (LocaleCompare(
"defs",token) == 0)
3450 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3454 if (LocaleCompare(
"gradient",token) == 0)
3457 key[2*MagickPathExtent],
3458 name[MagickPathExtent],
3459 type[MagickPathExtent];
3464 (void) GetNextToken(q,&q,extent,token);
3465 (void) CopyMagickString(name,token,MagickPathExtent);
3466 (void) GetNextToken(q,&q,extent,token);
3467 (void) CopyMagickString(type,token,MagickPathExtent);
3468 (void) GetNextToken(q,&q,extent,token);
3469 segment.x1=GetDrawValue(token,&next_token);
3470 if (token == next_token)
3471 ThrowPointExpectedException(token,exception);
3472 (void) GetNextToken(q,&q,extent,token);
3473 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3474 ThrowPointExpectedException(token,exception);
3476 (void) GetNextToken(q,&q,extent,token);
3477 segment.y1=GetDrawValue(token,&next_token);
3478 if (token == next_token)
3479 ThrowPointExpectedException(token,exception);
3480 (void) GetNextToken(q,&q,extent,token);
3481 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3482 ThrowPointExpectedException(token,exception);
3484 (void) GetNextToken(q,&q,extent,token);
3485 segment.x2=GetDrawValue(token,&next_token);
3486 if (token == next_token)
3487 ThrowPointExpectedException(token,exception);
3488 (void) GetNextToken(q,&q,extent,token);
3489 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3490 ThrowPointExpectedException(token,exception);
3492 (void) GetNextToken(q,&q,extent,token);
3493 segment.y2=GetDrawValue(token,&next_token);
3494 if (token == next_token)
3495 ThrowPointExpectedException(token,exception);
3496 if (LocaleCompare(type,
"radial") == 0)
3498 (void) GetNextToken(q,&q,extent,token);
3499 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3500 ThrowPointExpectedException(token,exception);
3502 (void) GetNextToken(q,&q,extent,token);
3504 for (p=q; *q !=
'\0'; )
3506 if (GetNextToken(q,&q,extent,token) < 1)
3508 if (LocaleCompare(token,
"pop") != 0)
3510 (void) GetNextToken(q,(
const char **) NULL,extent,token);
3511 if (LocaleCompare(token,
"gradient") != 0)
3515 if ((q == (
char *) NULL) || (*q ==
'\0') ||
3516 (p == (
char *) NULL) || ((q-4) < p) ||
3517 ((
size_t) (q-p+4+1) > extent))
3522 (void) CopyMagickString(token,p,(
size_t) (q-p-4+1));
3523 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3524 graphic_context[n]->affine.ry*segment.y1+
3525 graphic_context[n]->affine.tx;
3526 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3527 graphic_context[n]->affine.sy*segment.y1+
3528 graphic_context[n]->affine.ty;
3529 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3530 graphic_context[n]->affine.ry*segment.y2+
3531 graphic_context[n]->affine.tx;
3532 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3533 graphic_context[n]->affine.sy*segment.y2+
3534 graphic_context[n]->affine.ty;
3535 (void) FormatLocaleString(key,MagickPathExtent,
"%s",name);
3536 (void) SetImageArtifact(image,key,token);
3537 (void) FormatLocaleString(key,MagickPathExtent,
"%s-type",name);
3538 (void) SetImageArtifact(image,key,type);
3539 (void) FormatLocaleString(key,MagickPathExtent,
"%s-geometry",
3541 (void) FormatLocaleString(geometry,MagickPathExtent,
3542 "%gx%g%+.15g%+.15g",
3543 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
3544 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
3545 bounds.x1,bounds.y1);
3546 (void) SetImageArtifact(image,key,geometry);
3547 (void) GetNextToken(q,&q,extent,token);
3550 if (LocaleCompare(
"graphic-context",token) == 0)
3553 graphic_context=(DrawInfo **) ResizeQuantumMemory(
3554 graphic_context,(
size_t) (n+1),
sizeof(*graphic_context));
3555 if (graphic_context == (DrawInfo **) NULL)
3557 (void) ThrowMagickException(exception,GetMagickModule(),
3558 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
3563 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
3564 graphic_context[n-1]);
3567 (void) GetNextToken(q,&q,extent,token);
3568 (void) CloneString(&graphic_context[n]->
id,token);
3570 if (n > MagickMaxRecursionDepth)
3572 (void) ThrowMagickException(exception,GetMagickModule(),
3573 DrawError,
"VectorGraphicsNestedTooDeeply",
"`%s'",
3579 if (LocaleCompare(
"mask",token) == 0)
3581 (void) GetNextToken(q,&q,extent,token);
3584 if (LocaleCompare(
"pattern",token) == 0)
3587 key[2*MagickPathExtent],
3588 name[MagickPathExtent];
3593 (void) GetNextToken(q,&q,extent,token);
3594 (void) CopyMagickString(name,token,MagickPathExtent);
3595 (void) GetNextToken(q,&q,extent,token);
3596 region.x=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3598 if (token == next_token)
3599 ThrowPointExpectedException(token,exception);
3600 (void) GetNextToken(q,&q,extent,token);
3601 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3602 ThrowPointExpectedException(token,exception);
3604 (void) GetNextToken(q,&q,extent,token);
3605 region.y=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3607 if (token == next_token)
3608 ThrowPointExpectedException(token,exception);
3609 (void) GetNextToken(q,&q,extent,token);
3610 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3611 ThrowPointExpectedException(token,exception);
3613 (void) GetNextToken(q,&q,extent,token);
3614 region.width=CastDoubleToSizeT(floor(GetDrawValue(token,
3616 if (token == next_token)
3617 ThrowPointExpectedException(token,exception);
3618 (void) GetNextToken(q,&q,extent,token);
3619 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3620 ThrowPointExpectedException(token,exception);
3622 (void) GetNextToken(q,&q,extent,token);
3623 region.height=CastDoubleToSizeT(GetDrawValue(token,
3625 if (token == next_token)
3626 ThrowPointExpectedException(token,exception);
3627 for (p=q; *q !=
'\0'; )
3629 if (GetNextToken(q,&q,extent,token) < 1)
3631 if (LocaleCompare(token,
"pop") != 0)
3633 (void) GetNextToken(q,(
const char **) NULL,extent,token);
3634 if (LocaleCompare(token,
"pattern") != 0)
3638 if ((q == (
char *) NULL) || (p == (
char *) NULL) ||
3639 ((q-4) < p) || ((
size_t) (q-p+4+1) > extent))
3644 (void) CopyMagickString(token,p,(
size_t) (q-p-4+1));
3645 (void) FormatLocaleString(key,MagickPathExtent,
"%s",name);
3646 (void) SetImageArtifact(image,key,token);
3647 (void) FormatLocaleString(key,MagickPathExtent,
"%s-geometry",
3649 (void) FormatLocaleString(geometry,MagickPathExtent,
3650 "%.20gx%.20g%+.20g%+.20g",(
double) region.width,(
double)
3651 region.height,(
double) region.x,(
double) region.y);
3652 (void) SetImageArtifact(image,key,geometry);
3653 (void) GetNextToken(q,&q,extent,token);
3656 if (LocaleCompare(
"symbol",token) == 0)
3659 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3672 if (LocaleCompare(
"rectangle",keyword) == 0)
3674 primitive_type=RectanglePrimitive;
3677 if (LocaleCompare(
"rotate",keyword) == 0)
3679 (void) GetNextToken(q,&q,extent,token);
3680 angle=GetDrawValue(token,&next_token);
3681 if (token == next_token)
3682 ThrowPointExpectedException(token,exception);
3683 affine.sx=cos(DegreesToRadians(fmod((
double) angle,360.0)));
3684 affine.rx=sin(DegreesToRadians(fmod((
double) angle,360.0)));
3685 affine.ry=(-sin(DegreesToRadians(fmod((
double) angle,360.0))));
3686 affine.sy=cos(DegreesToRadians(fmod((
double) angle,360.0)));
3689 if (LocaleCompare(
"roundRectangle",keyword) == 0)
3691 primitive_type=RoundRectanglePrimitive;
3700 if (LocaleCompare(
"scale",keyword) == 0)
3702 (void) GetNextToken(q,&q,extent,token);
3703 affine.sx=GetDrawValue(token,&next_token);
3704 if (token == next_token)
3705 ThrowPointExpectedException(token,exception);
3706 (void) GetNextToken(q,&q,extent,token);
3707 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3708 ThrowPointExpectedException(token,exception);
3710 (void) GetNextToken(q,&q,extent,token);
3711 affine.sy=GetDrawValue(token,&next_token);
3712 if (token == next_token)
3713 ThrowPointExpectedException(token,exception);
3716 if (LocaleCompare(
"skewX",keyword) == 0)
3718 (void) GetNextToken(q,&q,extent,token);
3719 angle=GetDrawValue(token,&next_token);
3720 if (token == next_token)
3721 ThrowPointExpectedException(token,exception);
3722 affine.ry=sin(DegreesToRadians(angle));
3725 if (LocaleCompare(
"skewY",keyword) == 0)
3727 (void) GetNextToken(q,&q,extent,token);
3728 angle=GetDrawValue(token,&next_token);
3729 if (token == next_token)
3730 ThrowPointExpectedException(token,exception);
3731 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
3734 if (LocaleCompare(
"stop-color",keyword) == 0)
3740 if (number_stops == 1)
3741 stops=(StopInfo *) AcquireQuantumMemory(2,
sizeof(*stops));
3743 if (number_stops > 2)
3744 stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
3746 if (stops == (StopInfo *) NULL)
3748 (void) ThrowMagickException(exception,GetMagickModule(),
3749 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
3754 (void) GetNextToken(q,&q,extent,token);
3755 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3756 &stop_color,exception);
3757 stops[number_stops-1].color=stop_color;
3758 (void) GetNextToken(q,&q,extent,token);
3759 factor=strchr(token,
'%') != (
char *) NULL ? 0.01 : 1.0;
3760 stops[number_stops-1].offset=factor*GetDrawValue(token,
3762 if (token == next_token)
3763 ThrowPointExpectedException(token,exception);
3766 if (LocaleCompare(
"stroke",keyword) == 0)
3771 (void) GetNextToken(q,&q,extent,token);
3772 if (graphic_context[n]->clip_path != MagickFalse)
3774 mvg_class=(
const char *) GetValueFromSplayTree(macros,token);
3775 if (mvg_class != (
const char *) NULL)
3777 (void) DrawPatternPath(image,draw_info,mvg_class,
3778 &graphic_context[n]->stroke_pattern,exception);
3781 (void) FormatLocaleString(pattern,MagickPathExtent,
"%s",token);
3782 if (GetImageArtifact(image,pattern) != (
const char *) NULL)
3784 (void) DrawPatternPath(image,draw_info,token,
3785 &graphic_context[n]->stroke_pattern,exception);
3788 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3789 &graphic_context[n]->stroke,exception);
3790 if (graphic_context[n]->stroke_alpha != (
double) OpaqueAlpha)
3791 graphic_context[n]->stroke.alpha=
3792 graphic_context[n]->stroke_alpha;
3795 if (LocaleCompare(
"stroke-antialias",keyword) == 0)
3797 (void) GetNextToken(q,&q,extent,token);
3798 graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
3799 MagickTrue : MagickFalse;
3802 if (LocaleCompare(
"stroke-dasharray",keyword) == 0)
3804 if (graphic_context[n]->dash_pattern != (
double *) NULL)
3805 graphic_context[n]->dash_pattern=(
double *)
3806 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
3807 if (IsValidPoint(q) != MagickFalse)
3813 (void) GetNextToken(r,&r,extent,token);
3814 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3815 ThrowPointExpectedException(token,exception);
3817 (void) GetNextToken(r,&r,extent,token);
3818 for (x=0; IsValidPoint(token) != MagickFalse; x++)
3820 (void) GetNextToken(r,&r,extent,token);
3822 (void) GetNextToken(r,&r,extent,token);
3824 graphic_context[n]->dash_pattern=(
double *)
3825 AcquireQuantumMemory((
size_t) (2*x+2),
3826 sizeof(*graphic_context[n]->dash_pattern));
3827 if (graphic_context[n]->dash_pattern == (
double *) NULL)
3829 (void) ThrowMagickException(exception,GetMagickModule(),
3830 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
3835 (void) memset(graphic_context[n]->dash_pattern,0,(
size_t)
3836 (2*x+2)*
sizeof(*graphic_context[n]->dash_pattern));
3837 for (j=0; j < x; j++)
3839 (void) GetNextToken(q,&q,extent,token);
3840 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
3841 ThrowPointExpectedException(token,exception);
3843 (void) GetNextToken(q,&q,extent,token);
3844 graphic_context[n]->dash_pattern[j]=GetDrawValue(token,
3846 if (token == next_token)
3847 ThrowPointExpectedException(token,exception);
3848 if (graphic_context[n]->dash_pattern[j] <= 0.0)
3851 if (((x & 0x01) != 0) && (j == x))
3852 for ( ; j < (2*x); j++)
3853 graphic_context[n]->dash_pattern[j]=
3854 graphic_context[n]->dash_pattern[j-x];
3855 graphic_context[n]->dash_pattern[j]=0.0;
3858 (void) GetNextToken(q,&q,extent,token);
3861 if (LocaleCompare(
"stroke-dashoffset",keyword) == 0)
3863 (void) GetNextToken(q,&q,extent,token);
3864 graphic_context[n]->dash_offset=GetDrawValue(token,&next_token);
3865 if (token == next_token)
3866 ThrowPointExpectedException(token,exception);
3869 if (LocaleCompare(
"stroke-linecap",keyword) == 0)
3874 (void) GetNextToken(q,&q,extent,token);
3875 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
3881 graphic_context[n]->linecap=(LineCap) linecap;
3884 if (LocaleCompare(
"stroke-linejoin",keyword) == 0)
3889 (void) GetNextToken(q,&q,extent,token);
3890 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
3897 graphic_context[n]->linejoin=(LineJoin) linejoin;
3900 if (LocaleCompare(
"stroke-miterlimit",keyword) == 0)
3902 (void) GetNextToken(q,&q,extent,token);
3903 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
3906 if (LocaleCompare(
"stroke-opacity",keyword) == 0)
3911 (void) GetNextToken(q,&q,extent,token);
3912 if (graphic_context[n]->clip_path != MagickFalse)
3914 factor=strchr(token,
'%') != (
char *) NULL ? 0.01 : 1.0;
3915 opacity=MagickMin(MagickMax(factor*GetDrawValue(token,&next_token),
3917 if (token == next_token)
3918 ThrowPointExpectedException(token,exception);
3919 if (graphic_context[n]->compliance == SVGCompliance)
3920 graphic_context[n]->stroke_alpha*=opacity;
3922 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3923 if (graphic_context[n]->stroke.alpha != (
double) TransparentAlpha)
3924 graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3926 graphic_context[n]->stroke.alpha=(MagickRealType)
3927 ClampToQuantum((
double) QuantumRange*opacity);
3928 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3931 if (LocaleCompare(
"stroke-width",keyword) == 0)
3933 (void) GetNextToken(q,&q,extent,token);
3934 if (graphic_context[n]->clip_path != MagickFalse)
3936 graphic_context[n]->stroke_width=GetDrawValue(token,&next_token);
3937 if ((token == next_token) ||
3938 (graphic_context[n]->stroke_width < 0.0))
3939 ThrowPointExpectedException(token,exception);
3948 if (LocaleCompare(
"text",keyword) == 0)
3950 primitive_type=TextPrimitive;
3954 if (LocaleCompare(
"text-align",keyword) == 0)
3959 (void) GetNextToken(q,&q,extent,token);
3960 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3966 graphic_context[n]->align=(AlignType) align;
3969 if (LocaleCompare(
"text-anchor",keyword) == 0)
3974 (void) GetNextToken(q,&q,extent,token);
3975 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3981 graphic_context[n]->align=(AlignType) align;
3984 if (LocaleCompare(
"text-antialias",keyword) == 0)
3986 (void) GetNextToken(q,&q,extent,token);
3987 graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
3988 MagickTrue : MagickFalse;
3991 if (LocaleCompare(
"text-undercolor",keyword) == 0)
3993 (void) GetNextToken(q,&q,extent,token);
3994 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3995 &graphic_context[n]->undercolor,exception);
3998 if (LocaleCompare(
"translate",keyword) == 0)
4000 (void) GetNextToken(q,&q,extent,token);
4001 affine.tx=GetDrawValue(token,&next_token);
4002 if (token == next_token)
4003 ThrowPointExpectedException(token,exception);
4004 (void) GetNextToken(q,&q,extent,token);
4005 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
4006 ThrowPointExpectedException(token,exception);
4008 (void) GetNextToken(q,&q,extent,token);
4009 affine.ty=GetDrawValue(token,&next_token);
4010 if (token == next_token)
4011 ThrowPointExpectedException(token,exception);
4021 if (LocaleCompare(
"use",keyword) == 0)
4029 (void) GetNextToken(q,&q,extent,token);
4030 use=(
const char *) GetValueFromSplayTree(macros,token);
4031 if (use != (
const char *) NULL)
4033 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4034 (void) CloneString(&clone_info->primitive,use);
4035 status=RenderMVGContent(image,clone_info,depth+1,exception);
4036 clone_info=DestroyDrawInfo(clone_info);
4046 if (LocaleCompare(
"viewbox",keyword) == 0)
4048 (void) GetNextToken(q,&q,extent,token);
4049 graphic_context[n]->viewbox.x=CastDoubleToSsizeT(ceil(
4050 GetDrawValue(token,&next_token)-0.5));
4051 if (token == next_token)
4052 ThrowPointExpectedException(token,exception);
4053 (void) GetNextToken(q,&q,extent,token);
4054 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
4055 ThrowPointExpectedException(token,exception);
4057 (void) GetNextToken(q,&q,extent,token);
4058 graphic_context[n]->viewbox.y=CastDoubleToSsizeT(
4059 ceil(GetDrawValue(token,&next_token)-0.5));
4060 if (token == next_token)
4061 ThrowPointExpectedException(token,exception);
4062 (void) GetNextToken(q,&q,extent,token);
4063 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
4064 ThrowPointExpectedException(token,exception);
4066 (void) GetNextToken(q,&q,extent,token);
4067 graphic_context[n]->viewbox.width=CastDoubleToSizeT(floor(
4068 GetDrawValue(token,&next_token)+0.5));
4069 if (token == next_token)
4070 ThrowPointExpectedException(token,exception);
4071 (void) GetNextToken(q,&q,extent,token);
4072 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
4073 ThrowPointExpectedException(token,exception);
4075 (void) GetNextToken(q,&q,extent,token);
4076 graphic_context[n]->viewbox.height=CastDoubleToSizeT(floor(
4077 GetDrawValue(token,&next_token)+0.5));
4078 if (token == next_token)
4079 ThrowPointExpectedException(token,exception);
4088 if (LocaleCompare(
"word-spacing",keyword) == 0)
4090 (void) GetNextToken(q,&q,extent,token);
4091 graphic_context[n]->interword_spacing=GetDrawValue(token,
4093 if (token == next_token)
4094 ThrowPointExpectedException(token,exception);
4106 if (status == MagickFalse)
4108 if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
4109 (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
4110 (fabs(affine.sy-1.0) >= MagickEpsilon) ||
4111 (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
4113 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
4114 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
4115 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
4116 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
4117 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
4119 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
4122 if (primitive_type == UndefinedPrimitive)
4126 if (number_stops > 1)
4131 type=LinearGradient;
4132 if (draw_info->gradient.type == RadialGradient)
4133 type=RadialGradient;
4134 (void) GradientImage(image,type,PadSpread,stops,number_stops,
4137 if (number_stops > 0)
4138 stops=(StopInfo *) RelinquishMagickMemory(stops);
4140 if ((draw_info->debug != MagickFalse) && (q > p))
4141 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" %.*s",(
int)
4148 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4149 if (primitive_info[i].text != (
char *) NULL)
4150 primitive_info[i].text=DestroyString(primitive_info[i].text);
4154 primitive_info[0].primitive=primitive_type;
4155 primitive_info[0].point.x=0.0;
4156 primitive_info[0].point.y=0.0;
4157 primitive_info[0].coordinates=0;
4158 primitive_info[0].method=FloodfillMethod;
4159 primitive_info[0].closed_subpath=MagickFalse;
4160 for (x=0; *q !=
'\0'; x++)
4165 if (IsValidPoint(q) == MagickFalse)
4167 (void) GetNextToken(q,&q,extent,token);
4168 point.x=GetDrawValue(token,&next_token);
4169 if (token == next_token)
4170 ThrowPointExpectedException(token,exception);
4171 (void) GetNextToken(q,&q,extent,token);
4172 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
4173 ThrowPointExpectedException(token,exception);
4175 (void) GetNextToken(q,&q,extent,token);
4176 point.y=GetDrawValue(token,&next_token);
4177 if (token == next_token)
4178 ThrowPointExpectedException(token,exception);
4179 (void) GetNextToken(q,(
const char **) NULL,extent,token);
4181 (void) GetNextToken(q,&q,extent,token);
4182 primitive_info[i].primitive=primitive_type;
4183 primitive_info[i].point=point;
4184 primitive_info[i].coordinates=0;
4185 primitive_info[i].method=FloodfillMethod;
4186 primitive_info[i].closed_subpath=MagickFalse;
4189 if (i < (ssize_t) number_points)
4191 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(
double)
4193 primitive_info=(*mvg_info.primitive_info);
4194 if (status == MagickFalse)
4197 if (status == MagickFalse)
4199 if (primitive_info[j].text != (
char *) NULL)
4200 primitive_info[j].text=DestroyString(primitive_info[j].text);
4201 primitive_info[j].primitive=primitive_type;
4202 primitive_info[j].coordinates=(size_t) x;
4203 primitive_info[j].method=FloodfillMethod;
4204 primitive_info[j].closed_subpath=MagickFalse;
4208 bounds.x1=primitive_info[j].point.x;
4209 bounds.y1=primitive_info[j].point.y;
4210 bounds.x2=primitive_info[j].point.x;
4211 bounds.y2=primitive_info[j].point.y;
4212 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
4214 point=primitive_info[j+k].point;
4215 if (point.x < bounds.x1)
4217 if (point.y < bounds.y1)
4219 if (point.x > bounds.x2)
4221 if (point.y > bounds.y2)
4227 coordinates=(double) primitive_info[j].coordinates;
4228 switch (primitive_type)
4230 case RectanglePrimitive:
4235 case RoundRectanglePrimitive:
4242 alpha=bounds.x2-bounds.x1;
4243 beta=bounds.y2-bounds.y1;
4244 radius=hypot(alpha,beta);
4246 coordinates+=2.0*((size_t) ceil((
double) MagickPI*radius))+6.0*
4247 BezierQuantum+360.0;
4250 case BezierPrimitive:
4252 coordinates=(BezierQuantum*(double) primitive_info[j].coordinates);
4261 (void) GetNextToken(q,&q,extent,token);
4264 for (s=token; *s !=
'\0'; s=t)
4269 value=GetDrawValue(s,&t);
4278 for (s=token; *s !=
'\0'; s++)
4279 if (strspn(s,
"AaCcQqSsTt") != 0)
4280 coordinates+=(20.0*BezierQuantum)+360.0;
4286 if (status == MagickFalse)
4288 if (((
size_t) (i+coordinates)) >= number_points)
4293 number_points+=(size_t) coordinates+1;
4294 if (number_points < (
size_t) coordinates)
4296 (void) ThrowMagickException(exception,GetMagickModule(),
4297 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
4303 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(
double)
4305 primitive_info=(*mvg_info.primitive_info);
4306 if (status == MagickFalse)
4309 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,
4310 PrimitiveExtentPad);
4311 primitive_info=(*mvg_info.primitive_info);
4312 if (status == MagickFalse)
4315 switch (primitive_type)
4317 case PointPrimitive:
4320 if (primitive_info[j].coordinates != 1)
4325 status&=(MagickStatusType) TracePoint(primitive_info+j,
4326 primitive_info[j].point);
4327 primitive_info=(*mvg_info.primitive_info);
4328 if (status == MagickFalse)
4330 i=j+(ssize_t) primitive_info[j].coordinates;
4335 if (primitive_info[j].coordinates != 2)
4340 status&=(MagickStatusType) TraceLine(primitive_info+j,
4341 primitive_info[j].point,primitive_info[j+1].point);
4342 primitive_info=(*mvg_info.primitive_info);
4343 if (status == MagickFalse)
4345 i=j+(ssize_t) primitive_info[j].coordinates;
4348 case RectanglePrimitive:
4350 if (primitive_info[j].coordinates != 2)
4355 status&=(MagickStatusType) TraceRectangle(primitive_info+j,
4356 primitive_info[j].point,primitive_info[j+1].point);
4357 primitive_info=(*mvg_info.primitive_info);
4358 if (status == MagickFalse)
4360 i=j+(ssize_t) primitive_info[j].coordinates;
4363 case RoundRectanglePrimitive:
4365 if (primitive_info[j].coordinates != 3)
4370 if ((primitive_info[j+2].point.x < 0.0) ||
4371 (primitive_info[j+2].point.y < 0.0))
4376 if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
4381 if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
4386 status&=(MagickStatusType) TraceRoundRectangle(&mvg_info,
4387 primitive_info[j].point,primitive_info[j+1].point,
4388 primitive_info[j+2].point);
4389 primitive_info=(*mvg_info.primitive_info);
4390 if (status == MagickFalse)
4392 i=j+(ssize_t) primitive_info[j].coordinates;
4397 if (primitive_info[j].coordinates != 3)
4402 status&=(MagickStatusType) TraceArc(&mvg_info,primitive_info[j].point,
4403 primitive_info[j+1].point,primitive_info[j+2].point);
4404 primitive_info=(*mvg_info.primitive_info);
4405 if (status == MagickFalse)
4407 i=j+(ssize_t) primitive_info[j].coordinates;
4410 case EllipsePrimitive:
4412 if (primitive_info[j].coordinates != 3)
4417 if ((primitive_info[j+1].point.x < 0.0) ||
4418 (primitive_info[j+1].point.y < 0.0))
4423 status&=(MagickStatusType) TraceEllipse(&mvg_info,
4424 primitive_info[j].point,primitive_info[j+1].point,
4425 primitive_info[j+2].point);
4426 primitive_info=(*mvg_info.primitive_info);
4427 if (status == MagickFalse)
4429 i=j+(ssize_t) primitive_info[j].coordinates;
4432 case CirclePrimitive:
4434 if (primitive_info[j].coordinates != 2)
4439 status&=(MagickStatusType) TraceCircle(&mvg_info,
4440 primitive_info[j].point,primitive_info[j+1].point);
4441 primitive_info=(*mvg_info.primitive_info);
4442 if (status == MagickFalse)
4444 i=j+(ssize_t) primitive_info[j].coordinates;
4447 case PolylinePrimitive:
4449 if (primitive_info[j].coordinates < 1)
4456 case PolygonPrimitive:
4458 if (primitive_info[j].coordinates < 3)
4463 primitive_info[i]=primitive_info[j];
4464 primitive_info[i].coordinates=0;
4465 primitive_info[j].coordinates++;
4466 primitive_info[j].closed_subpath=MagickTrue;
4470 case BezierPrimitive:
4472 if (primitive_info[j].coordinates < 3)
4477 status&=(MagickStatusType) TraceBezier(&mvg_info,
4478 primitive_info[j].coordinates);
4479 primitive_info=(*mvg_info.primitive_info);
4480 if (status == MagickFalse)
4482 i=j+(ssize_t) primitive_info[j].coordinates;
4487 coordinates=(double) TracePath(&mvg_info,token,exception);
4488 primitive_info=(*mvg_info.primitive_info);
4489 if (status == MagickFalse)
4491 if (coordinates < 0.0)
4496 i=(ssize_t) (j+coordinates);
4499 case AlphaPrimitive:
4500 case ColorPrimitive:
4505 if (primitive_info[j].coordinates != 1)
4510 (void) GetNextToken(q,&q,extent,token);
4511 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
4517 primitive_info[j].method=(PaintMethod) method;
4522 if (primitive_info[j].coordinates != 1)
4528 (void) GetNextToken(q,&q,extent,token);
4529 (void) CloneString(&primitive_info[j].text,token);
4533 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4534 if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
4535 (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
4537 mvg_info.point=primitive_info->point;
4538 primitive_info->point.x+=cursor;
4542 mvg_info.point=primitive_info->point;
4545 clone_info->render=MagickFalse;
4546 clone_info->text=AcquireString(token);
4547 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,
4548 &metrics,exception);
4549 clone_info=DestroyDrawInfo(clone_info);
4550 cursor+=metrics.width;
4551 if (graphic_context[n]->compliance != SVGCompliance)
4555 case ImagePrimitive:
4557 if (primitive_info[j].coordinates != 2)
4562 (void) GetNextToken(q,&q,extent,token);
4563 (void) CloneString(&primitive_info[j].text,token);
4570 primitive_info[i].primitive=UndefinedPrimitive;
4571 if ((draw_info->debug != MagickFalse) && (q > p))
4572 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" %.*s",(
int) (q-p),p);
4576 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,ExpandAffine(
4577 &graphic_context[n]->affine));
4578 primitive_info=(*mvg_info.primitive_info);
4581 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(
double)
4582 graphic_context[n]->stroke_width);
4583 primitive_info=(*mvg_info.primitive_info);
4591 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4593 point=primitive_info[i].point;
4594 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
4595 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
4596 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
4597 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
4598 point=primitive_info[i].point;
4599 if (point.x < graphic_context[n]->bounds.x1)
4600 graphic_context[n]->bounds.x1=point.x;
4601 if (point.y < graphic_context[n]->bounds.y1)
4602 graphic_context[n]->bounds.y1=point.y;
4603 if (point.x > graphic_context[n]->bounds.x2)
4604 graphic_context[n]->bounds.x2=point.x;
4605 if (point.y > graphic_context[n]->bounds.y2)
4606 graphic_context[n]->bounds.y2=point.y;
4607 if (primitive_info[i].primitive == ImagePrimitive)
4609 if (i >= (ssize_t) number_points)
4610 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4612 if (graphic_context[n]->render != MagickFalse)
4614 if ((n != 0) && (graphic_context[n]->compliance != SVGCompliance) &&
4615 (graphic_context[n]->clip_mask != (
char *) NULL) &&
4616 (LocaleCompare(graphic_context[n]->clip_mask,
4617 graphic_context[n-1]->clip_mask) != 0))
4622 clip_path=(
const char *) GetValueFromSplayTree(macros,
4623 graphic_context[n]->clip_mask);
4624 if (clip_path != (
const char *) NULL)
4625 (void) SetImageArtifact(image,graphic_context[n]->clip_mask,
4627 status&=(MagickStatusType) DrawClipPath(image,graphic_context[n],
4628 graphic_context[n]->clip_mask,exception);
4630 status&=(MagickStatusType) DrawPrimitive(image,graphic_context[n],
4631 primitive_info,exception);
4633 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
4635 if (proceed == MagickFalse)
4640 if (draw_info->debug != MagickFalse)
4641 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"end draw-image");
4645 macros=DestroySplayTree(macros);
4646 token=DestroyString(token);
4647 if (primitive_info != (PrimitiveInfo *) NULL)
4649 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4650 if (primitive_info[i].text != (
char *) NULL)
4651 primitive_info[i].text=DestroyString(primitive_info[i].text);
4652 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4654 primitive=DestroyString(primitive);
4655 if (stops != (StopInfo *) NULL)
4656 stops=(StopInfo *) RelinquishMagickMemory(stops);
4657 for ( ; n >= 0; n--)
4658 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
4659 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
4660 if ((status == MagickFalse) && (exception->severity < ErrorException))
4661 ThrowBinaryException(DrawError,
"NonconformingDrawingPrimitiveDefinition",
4663 return(status != 0 ? MagickTrue : MagickFalse);
4666MagickExport MagickBooleanType DrawImage(Image *image,
const DrawInfo *draw_info,
4667 ExceptionInfo *exception)
4669 return(RenderMVGContent(image,draw_info,0,exception));
4703MagickExport MagickBooleanType DrawPatternPath(Image *image,
4704 const DrawInfo *draw_info,
const char *name,Image **pattern,
4705 ExceptionInfo *exception)
4708 property[MagickPathExtent];
4724 assert(image != (Image *) NULL);
4725 assert(image->signature == MagickCoreSignature);
4726 assert(draw_info != (
const DrawInfo *) NULL);
4727 assert(name != (
const char *) NULL);
4728 if (IsEventLogging() != MagickFalse)
4729 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4730 (void) FormatLocaleString(property,MagickPathExtent,
"%s",name);
4731 path=GetImageArtifact(image,property);
4732 if (path == (
const char *) NULL)
4733 return(MagickFalse);
4734 (void) FormatLocaleString(property,MagickPathExtent,
"%s-geometry",name);
4735 geometry=GetImageArtifact(image,property);
4736 if (geometry == (
const char *) NULL)
4737 return(MagickFalse);
4738 if ((*pattern) != (Image *) NULL)
4739 *pattern=DestroyImage(*pattern);
4740 image_info=AcquireImageInfo();
4741 image_info->size=AcquireString(geometry);
4742 *pattern=AcquireImage(image_info,exception);
4743 image_info=DestroyImageInfo(image_info);
4744 (void) QueryColorCompliance(
"#00000000",AllCompliance,
4745 &(*pattern)->background_color,exception);
4746 (void) SetImageBackgroundColor(*pattern,exception);
4747 if (draw_info->debug != MagickFalse)
4748 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4749 "begin pattern-path %s %s",name,geometry);
4750 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4751 if (clone_info->fill_pattern != (Image *) NULL)
4752 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
4753 if (clone_info->stroke_pattern != (Image *) NULL)
4754 clone_info->stroke_pattern=DestroyImage(clone_info->stroke_pattern);
4755 (void) FormatLocaleString(property,MagickPathExtent,
"%s-type",name);
4756 type=GetImageArtifact(image,property);
4757 if (type != (
const char *) NULL)
4758 clone_info->gradient.type=(GradientType) ParseCommandOption(
4759 MagickGradientOptions,MagickFalse,type);
4760 (void) CloneString(&clone_info->primitive,path);
4761 status=RenderMVGContent(*pattern,clone_info,0,exception);
4762 clone_info=DestroyDrawInfo(clone_info);
4763 if (draw_info->debug != MagickFalse)
4764 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
"end pattern-path");
4799static PolygonInfo **DestroyPolygonTLS(PolygonInfo **polygon_info)
4804 assert(polygon_info != (PolygonInfo **) NULL);
4805 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
4806 if (polygon_info[i] != (PolygonInfo *) NULL)
4807 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
4808 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
4809 return(polygon_info);
4812static PolygonInfo **AcquirePolygonTLS(
const PrimitiveInfo *primitive_info,
4813 ExceptionInfo *exception)
4816 *magick_restrict path_info;
4824 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4825 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
4826 sizeof(*polygon_info));
4827 if (polygon_info == (PolygonInfo **) NULL)
4829 (void) ThrowMagickException(exception,GetMagickModule(),
4830 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
4831 return((PolygonInfo **) NULL);
4833 (void) memset(polygon_info,0,number_threads*
sizeof(*polygon_info));
4834 path_info=ConvertPrimitiveToPath(primitive_info,exception);
4835 if (path_info == (PathInfo *) NULL)
4836 return(DestroyPolygonTLS(polygon_info));
4837 polygon_info[0]=ConvertPathToPolygon(path_info,exception);
4838 if (polygon_info[0] == (PolygonInfo *) NULL)
4840 (void) ThrowMagickException(exception,GetMagickModule(),
4841 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
4842 return(DestroyPolygonTLS(polygon_info));
4844 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
4845 return(polygon_info);
4848static MagickBooleanType ClonePolygonEdgesTLS(PolygonInfo **polygon_info,
4849 const size_t number_threads,ExceptionInfo *exception)
4854 for (i=1; i < (ssize_t) number_threads; i++)
4862 polygon_info[i]=(PolygonInfo *) AcquireMagickMemory(
4863 sizeof(*polygon_info[i]));
4864 if (polygon_info[i] == (PolygonInfo *) NULL)
4866 (void) ThrowMagickException(exception,GetMagickModule(),
4867 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
4868 return(MagickFalse);
4870 polygon_info[i]->number_edges=0;
4871 edge_info=polygon_info[0]->edges;
4872 polygon_info[i]->edges=(EdgeInfo *) AcquireQuantumMemory(
4873 polygon_info[0]->number_edges,
sizeof(*edge_info));
4874 if (polygon_info[i]->edges == (EdgeInfo *) NULL)
4876 (void) ThrowMagickException(exception,GetMagickModule(),
4877 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
4878 return(MagickFalse);
4880 (void) memcpy(polygon_info[i]->edges,edge_info,
4881 polygon_info[0]->number_edges*
sizeof(*edge_info));
4882 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4883 polygon_info[i]->edges[j].points=(PointInfo *) NULL;
4884 polygon_info[i]->number_edges=polygon_info[0]->number_edges;
4885 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4887 edge_info=polygon_info[0]->edges+j;
4888 polygon_info[i]->edges[j].points=(PointInfo *) AcquireQuantumMemory(
4889 edge_info->number_points,
sizeof(*edge_info));
4890 if (polygon_info[i]->edges[j].points == (PointInfo *) NULL)
4892 (void) ThrowMagickException(exception,GetMagickModule(),
4893 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
4894 return(MagickFalse);
4896 (void) memcpy(polygon_info[i]->edges[j].points,edge_info->points,
4897 edge_info->number_points*
sizeof(*edge_info->points));
4903static size_t DestroyEdge(PolygonInfo *polygon_info,
const ssize_t edge)
4905 assert(edge < (ssize_t) polygon_info->number_edges);
4906 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
4907 polygon_info->edges[edge].points);
4908 polygon_info->number_edges--;
4909 if (edge < (ssize_t) polygon_info->number_edges)
4910 (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
4911 (polygon_info->number_edges-(
size_t) edge)*
sizeof(*polygon_info->edges));
4912 return(polygon_info->number_edges);
4915static double GetFillAlpha(PolygonInfo *polygon_info,
const double mid,
4916 const MagickBooleanType fill,
const FillRule fill_rule,
const ssize_t x,
4917 const ssize_t y,
double *stroke_alpha)
4944 p=polygon_info->edges;
4945 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4947 if ((
double) y <= (p->bounds.y1-mid-0.5))
4949 if ((
double) y > (p->bounds.y2+mid+0.5))
4952 (void) DestroyEdge(polygon_info,j--);
4955 if (((
double) x <= (p->bounds.x1-mid-0.5)) ||
4956 ((
double) x > (p->bounds.x2+mid+0.5)))
4958 i=(ssize_t) MagickMax((
double) p->highwater,1.0);
4959 for ( ; i < (ssize_t) p->number_points; i++)
4961 if ((
double) y <= (p->points[i-1].y-mid-0.5))
4963 if ((
double) y > (p->points[i].y+mid+0.5))
4965 if (p->scanline != (
double) y)
4967 p->scanline=(double) y;
4968 p->highwater=(size_t) i;
4974 delta.x=(q+1)->x-q->x;
4975 delta.y=(q+1)->y-q->y;
4976 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
4982 delta.x=(double) x-q->x;
4983 delta.y=(double) y-q->y;
4984 distance=delta.x*delta.x+delta.y*delta.y;
4988 alpha=delta.x*delta.x+delta.y*delta.y;
4994 delta.x=(double) x-(q+1)->x;
4995 delta.y=(double) y-(q+1)->y;
4996 distance=delta.x*delta.x+delta.y*delta.y;
5003 alpha=MagickSafeReciprocal(alpha);
5004 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
5005 distance=alpha*beta*beta;
5012 if (p->ghostline == MagickFalse)
5015 if ((*stroke_alpha < 1.0) &&
5016 (distance <= ((alpha+0.25)*(alpha+0.25))))
5019 if (distance <= ((alpha+0.25)*(alpha+0.25)))
5024 if (fabs(distance-1.0) >= MagickEpsilon)
5025 beta=sqrt((
double) distance);
5027 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
5028 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
5032 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
5034 if (distance <= 0.0)
5041 if (fabs(beta) < MagickEpsilon)
5044 if (fabs(distance-1.0) >= MagickEpsilon)
5045 beta=sqrt(distance);
5048 if (subpath_alpha < (alpha*alpha))
5049 subpath_alpha=alpha*alpha;
5055 if (fill == MagickFalse)
5057 if (subpath_alpha >= 1.0)
5063 p=polygon_info->edges;
5064 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
5066 if ((
double) y <= p->bounds.y1)
5068 if (((
double) y > p->bounds.y2) || ((
double) x <= p->bounds.x1))
5070 if ((
double) x > p->bounds.x2)
5072 winding_number+=p->direction != 0 ? 1 : -1;
5075 i=(ssize_t) MagickMax((
double) p->highwater,1.0);
5076 for ( ; i < (ssize_t) (p->number_points-1); i++)
5077 if ((
double) y <= p->points[i].y)
5080 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
5081 winding_number+=p->direction != 0 ? 1 : -1;
5083 if (fill_rule != NonZeroRule)
5085 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
5089 if (MagickAbsoluteValue(winding_number) != 0)
5091 return(subpath_alpha);
5094static MagickBooleanType DrawPolygonPrimitive(Image *image,
5095 const DrawInfo *draw_info,
const PrimitiveInfo *primitive_info,
5096 ExceptionInfo *exception)
5098 typedef struct _ExtentInfo
5127 **magick_restrict polygon_info;
5139 assert(image != (Image *) NULL);
5140 assert(image->signature == MagickCoreSignature);
5141 assert(draw_info != (DrawInfo *) NULL);
5142 assert(draw_info->signature == MagickCoreSignature);
5143 assert(primitive_info != (PrimitiveInfo *) NULL);
5144 if (IsEventLogging() != MagickFalse)
5145 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
5146 if (primitive_info->coordinates <= 1)
5151 polygon_info=AcquirePolygonTLS(primitive_info,exception);
5152 if (polygon_info == (PolygonInfo **) NULL)
5153 return(MagickFalse);
5154 if (draw_info->debug != MagickFalse)
5155 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" begin draw-polygon");
5156 fill=(primitive_info->method == FillToBorderMethod) ||
5157 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
5158 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5159 bounds=polygon_info[0]->edges[0].bounds;
5160 artifact=GetImageArtifact(image,
"draw:render-bounding-rectangles");
5161 if (IsStringTrue(artifact) != MagickFalse)
5162 (void) DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
5163 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
5165 p=polygon_info[0]->edges+i;
5166 if (p->bounds.x1 < bounds.x1)
5167 bounds.x1=p->bounds.x1;
5168 if (p->bounds.y1 < bounds.y1)
5169 bounds.y1=p->bounds.y1;
5170 if (p->bounds.x2 > bounds.x2)
5171 bounds.x2=p->bounds.x2;
5172 if (p->bounds.y2 > bounds.y2)
5173 bounds.y2=p->bounds.y2;
5175 bounds.x1-=(mid+1.0);
5176 bounds.y1-=(mid+1.0);
5177 bounds.x2+=(mid+1.0);
5178 bounds.y2+=(mid+1.0);
5179 if ((bounds.x1 >= (
double) image->columns) ||
5180 (bounds.y1 >= (
double) image->rows) ||
5181 (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
5183 polygon_info=DestroyPolygonTLS(polygon_info);
5186 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
5187 (
double) image->columns-1.0 : bounds.x1;
5188 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
5189 (
double) image->rows-1.0 : bounds.y1;
5190 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
5191 (
double) image->columns-1.0 : bounds.x2;
5192 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
5193 (
double) image->rows-1.0 : bounds.y2;
5194 poly_extent.x1=CastDoubleToSsizeT(ceil(bounds.x1-0.5));
5195 poly_extent.y1=CastDoubleToSsizeT(ceil(bounds.y1-0.5));
5196 poly_extent.x2=CastDoubleToSsizeT(floor(bounds.x2+0.5));
5197 poly_extent.y2=CastDoubleToSsizeT(floor(bounds.y2+0.5));
5198 number_threads=(size_t) GetMagickNumberThreads(image,image,(
size_t)
5199 (poly_extent.y2-poly_extent.y1+1),1);
5200 status=ClonePolygonEdgesTLS(polygon_info,number_threads,exception);
5201 if (status == MagickFalse)
5203 polygon_info=DestroyPolygonTLS(polygon_info);
5206 image_view=AcquireAuthenticCacheView(image,exception);
5207 if ((primitive_info->coordinates == 1) ||
5208 (polygon_info[0]->number_edges == 0))
5213#if defined(MAGICKCORE_OPENMP_SUPPORT)
5214 #pragma omp parallel for schedule(static) shared(status) \
5215 num_threads((int) number_threads)
5217 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5228 if (status == MagickFalse)
5231 q=GetCacheViewAuthenticPixels(image_view,x,y,(
size_t) (poly_extent.x2-
5233 if (q == (Quantum *) NULL)
5238 GetPixelInfo(image,&pixel);
5239 for ( ; x <= poly_extent.x2; x++)
5241 if ((x == CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5))) &&
5242 (y == CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5))))
5244 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&pixel,
5246 SetPixelViaPixelInfo(image,&pixel,q);
5248 q+=(ptrdiff_t) GetPixelChannels(image);
5250 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5253 image_view=DestroyCacheView(image_view);
5254 polygon_info=DestroyPolygonTLS(polygon_info);
5255 if (draw_info->debug != MagickFalse)
5256 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5257 " end draw-polygon");
5263#if defined(MAGICKCORE_OPENMP_SUPPORT)
5264 #pragma omp parallel for schedule(static) shared(status) \
5265 num_threads((int) number_threads)
5267 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5270 id = GetOpenMPThreadId();
5278 if (status == MagickFalse)
5280 q=GetCacheViewAuthenticPixels(image_view,poly_extent.x1,y,(
size_t)
5281 (poly_extent.x2-poly_extent.x1+1),1,exception);
5282 if (q == (Quantum *) NULL)
5287 for (x=poly_extent.x1; x <= poly_extent.x2; x++)
5300 fill_alpha=GetFillAlpha(polygon_info[
id],mid,fill,draw_info->fill_rule,
5302 if (draw_info->stroke_antialias == MagickFalse)
5304 fill_alpha=fill_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5305 stroke_alpha=stroke_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5307 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&fill_color,
5309 CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
5310 (
double) GetPixelAlpha(image,q),q);
5311 GetStrokeColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&stroke_color,
5313 CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
5314 (
double) GetPixelAlpha(image,q),q);
5315 q+=(ptrdiff_t) GetPixelChannels(image);
5317 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5320 image_view=DestroyCacheView(image_view);
5321 polygon_info=DestroyPolygonTLS(polygon_info);
5322 if (draw_info->debug != MagickFalse)
5323 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end draw-polygon");
5356static void LogPrimitiveInfo(
const PrimitiveInfo *primitive_info)
5382 x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5383 y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5384 switch (primitive_info->primitive)
5386 case AlphaPrimitive:
5388 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5389 "AlphaPrimitive %.20g,%.20g %s",(
double) x,(
double) y,
5390 methods[primitive_info->method]);
5393 case ColorPrimitive:
5395 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5396 "ColorPrimitive %.20g,%.20g %s",(
double) x,(
double) y,
5397 methods[primitive_info->method]);
5400 case ImagePrimitive:
5402 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5403 "ImagePrimitive %.20g,%.20g",(
double) x,(
double) y);
5406 case PointPrimitive:
5408 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5409 "PointPrimitive %.20g,%.20g %s",(
double) x,(
double) y,
5410 methods[primitive_info->method]);
5415 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5416 "TextPrimitive %.20g,%.20g",(
double) x,(
double) y);
5423 p=primitive_info[0].point;
5426 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5428 point=primitive_info[i].point;
5429 if (coordinates <= 0)
5431 coordinates=(ssize_t) primitive_info[i].coordinates;
5432 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5433 " begin open (%.20g)",(
double) coordinates);
5436 point=primitive_info[i].point;
5437 if ((fabs(q.x-point.x) >= MagickEpsilon) ||
5438 (fabs(q.y-point.y) >= MagickEpsilon))
5439 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5440 " %.20g: %.18g,%.18g",(
double) coordinates,point.x,point.y);
5442 (
void) LogMagickEvent(DrawEvent,GetMagickModule(),
5443 " %.20g: %g %g (duplicate)",(
double) coordinates,point.x,point.y);
5446 if (coordinates > 0)
5448 if ((fabs(p.x-point.x) >= MagickEpsilon) ||
5449 (fabs(p.y-point.y) >= MagickEpsilon))
5450 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end last (%.20g)",
5451 (
double) coordinates);
5453 (
void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end open (%.20g)",
5454 (
double) coordinates);
5458MagickExport MagickBooleanType DrawPrimitive(Image *image,
5459 const DrawInfo *draw_info,
const PrimitiveInfo *primitive_info,
5460 ExceptionInfo *exception)
5475 if (draw_info->debug != MagickFalse)
5477 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5478 " begin draw-primitive");
5479 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5480 " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
5481 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
5482 draw_info->affine.tx,draw_info->affine.ty);
5485 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5486 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
5487 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
5488 status&=(MagickStatusType) SetImageColorspace(image,sRGBColorspace,
5490 if (draw_info->compliance == SVGCompliance)
5492 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5493 draw_info->clipping_mask,exception);
5494 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5495 draw_info->composite_mask,exception);
5497 x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5498 y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5499 image_view=AcquireAuthenticCacheView(image,exception);
5500 switch (primitive_info->primitive)
5502 case AlphaPrimitive:
5504 if ((image->alpha_trait & BlendPixelTrait) == 0)
5505 status&=(MagickStatusType) SetImageAlphaChannel(image,
5506 OpaqueAlphaChannel,exception);
5507 switch (primitive_info->method)
5518 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5519 if (q == (Quantum *) NULL)
5521 GetFillColor(draw_info,x,y,&pixel,exception);
5522 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5523 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5533 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5534 x,y,&target,exception);
5535 GetPixelInfo(image,&pixel);
5536 for (y=0; y < (ssize_t) image->rows; y++)
5541 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5543 if (q == (Quantum *) NULL)
5545 for (x=0; x < (ssize_t) image->columns; x++)
5547 GetPixelInfoPixel(image,q,&pixel);
5548 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5550 q+=(ptrdiff_t) GetPixelChannels(image);
5553 GetFillColor(draw_info,x,y,&pixel,exception);
5554 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5555 q+=(ptrdiff_t) GetPixelChannels(image);
5557 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5559 if (status == MagickFalse)
5564 case FloodfillMethod:
5565 case FillToBorderMethod:
5573 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5574 TileVirtualPixelMethod,x,y,&target,exception);
5575 if (primitive_info->method == FillToBorderMethod)
5577 target.red=(double) draw_info->border_color.red;
5578 target.green=(double) draw_info->border_color.green;
5579 target.blue=(double) draw_info->border_color.blue;
5581 channel_mask=SetImageChannelMask(image,AlphaChannel);
5582 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5583 &target,x,y,primitive_info->method == FloodfillMethod ?
5584 MagickFalse : MagickTrue,exception);
5585 (void) SetImageChannelMask(image,channel_mask);
5593 for (y=0; y < (ssize_t) image->rows; y++)
5598 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5600 if (q == (Quantum *) NULL)
5602 for (x=0; x < (ssize_t) image->columns; x++)
5604 GetFillColor(draw_info,x,y,&pixel,exception);
5605 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5606 q+=(ptrdiff_t) GetPixelChannels(image);
5608 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5610 if (status == MagickFalse)
5618 case ColorPrimitive:
5620 switch (primitive_info->method)
5631 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5632 if (q == (Quantum *) NULL)
5634 GetPixelInfo(image,&pixel);
5635 GetFillColor(draw_info,x,y,&pixel,exception);
5636 SetPixelViaPixelInfo(image,&pixel,q);
5637 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5647 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5648 x,y,&target,exception);
5649 for (y=0; y < (ssize_t) image->rows; y++)
5654 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5656 if (q == (Quantum *) NULL)
5658 for (x=0; x < (ssize_t) image->columns; x++)
5660 GetPixelInfoPixel(image,q,&pixel);
5661 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5663 q+=(ptrdiff_t) GetPixelChannels(image);
5666 GetFillColor(draw_info,x,y,&pixel,exception);
5667 SetPixelViaPixelInfo(image,&pixel,q);
5668 q+=(ptrdiff_t) GetPixelChannels(image);
5670 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5672 if (status == MagickFalse)
5677 case FloodfillMethod:
5678 case FillToBorderMethod:
5683 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5684 TileVirtualPixelMethod,x,y,&target,exception);
5685 if (primitive_info->method == FillToBorderMethod)
5687 target.red=(double) draw_info->border_color.red;
5688 target.green=(double) draw_info->border_color.green;
5689 target.blue=(double) draw_info->border_color.blue;
5691 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5692 &target,x,y,primitive_info->method == FloodfillMethod ?
5693 MagickFalse : MagickTrue,exception);
5701 GetPixelInfo(image,&pixel);
5702 for (y=0; y < (ssize_t) image->rows; y++)
5707 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5709 if (q == (Quantum *) NULL)
5711 for (x=0; x < (ssize_t) image->columns; x++)
5713 GetFillColor(draw_info,x,y,&pixel,exception);
5714 SetPixelViaPixelInfo(image,&pixel,q);
5715 q+=(ptrdiff_t) GetPixelChannels(image);
5717 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5719 if (status == MagickFalse)
5727 case ImagePrimitive:
5733 composite_geometry[MagickPathExtent];
5749 if (primitive_info->text == (
char *) NULL)
5751 clone_info=AcquireImageInfo();
5752 composite_images=(Image *) NULL;
5753 if (LocaleNCompare(primitive_info->text,
"data:",5) == 0)
5754 composite_images=ReadInlineImage(clone_info,primitive_info->text,
5757 if (*primitive_info->text !=
'\0')
5762 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5764 (void) SetImageInfo(clone_info,1,exception);
5765 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5767 if (clone_info->size != (
char *) NULL)
5768 clone_info->size=DestroyString(clone_info->size);
5769 if (clone_info->extract != (
char *) NULL)
5770 clone_info->extract=DestroyString(clone_info->extract);
5771 composite_images=StrictReadImage(clone_info,exception);
5773 clone_info=DestroyImageInfo(clone_info);
5774 if (composite_images == (Image *) NULL)
5779 composite_image=RemoveFirstImageFromList(&composite_images);
5780 composite_images=DestroyImageList(composite_images);
5781 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
5782 NULL,(
void *) NULL);
5783 x1=CastDoubleToSsizeT(ceil(primitive_info[1].point.x-0.5));
5784 y1=CastDoubleToSsizeT(ceil(primitive_info[1].point.y-0.5));
5785 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
5786 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
5791 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5792 "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
5793 composite_image->filter=image->filter;
5794 status&=(MagickStatusType) TransformImage(&composite_image,
5795 (
char *) NULL,composite_geometry,exception);
5797 if (composite_image->alpha_trait == UndefinedPixelTrait)
5798 status&=(MagickStatusType) SetImageAlphaChannel(composite_image,
5799 OpaqueAlphaChannel,exception);
5800 if (draw_info->alpha != OpaqueAlpha)
5801 status&=(MagickStatusType) SetImageAlpha(composite_image,
5802 draw_info->alpha,exception);
5803 SetGeometry(image,&geometry);
5804 image->gravity=draw_info->gravity;
5807 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5808 "%.20gx%.20g%+.20g%+.20g",(
double) composite_image->columns,(
double)
5809 composite_image->rows,(
double) geometry.x,(
double) geometry.y);
5810 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
5811 affine=draw_info->affine;
5812 affine.tx=(double) geometry.x;
5813 affine.ty=(double) geometry.y;
5814 composite_image->interpolate=image->interpolate;
5815 if ((draw_info->compose == OverCompositeOp) ||
5816 (draw_info->compose == SrcOverCompositeOp))
5817 status&=(MagickStatusType) DrawAffineImage(image,composite_image,
5820 status&=(MagickStatusType) CompositeImage(image,composite_image,
5821 draw_info->compose,MagickTrue,geometry.x,geometry.y,exception);
5822 composite_image=DestroyImage(composite_image);
5825 case PointPrimitive:
5833 if ((y < 0) || (y >= (ssize_t) image->rows))
5835 if ((x < 0) || (x >= (ssize_t) image->columns))
5837 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5838 if (q == (Quantum *) NULL)
5840 GetFillColor(draw_info,x,y,&fill_color,exception);
5841 CompositePixelOver(image,&fill_color,(
double) fill_color.alpha,q,(
double)
5842 GetPixelAlpha(image,q),q);
5843 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5850 geometry[MagickPathExtent];
5855 if (primitive_info->text == (
char *) NULL)
5857 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5858 (void) CloneString(&clone_info->text,primitive_info->text);
5859 (void) FormatLocaleString(geometry,MagickPathExtent,
"%+f%+f",
5860 primitive_info->point.x,primitive_info->point.y);
5861 (void) CloneString(&clone_info->geometry,geometry);
5862 status&=(MagickStatusType) AnnotateImage(image,clone_info,exception);
5863 clone_info=DestroyDrawInfo(clone_info);
5875 if (IsEventLogging() != MagickFalse)
5876 LogPrimitiveInfo(primitive_info);
5877 scale=ExpandAffine(&draw_info->affine);
5878 if ((draw_info->dash_pattern != (
double *) NULL) &&
5879 (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
5880 (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
5881 (draw_info->stroke.alpha != (
double) TransparentAlpha))
5886 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5887 clone_info->stroke_width=0.0;
5888 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5889 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5890 primitive_info,exception);
5891 clone_info=DestroyDrawInfo(clone_info);
5892 if (status != MagickFalse)
5893 status&=(MagickStatusType) DrawDashPolygon(draw_info,primitive_info,
5897 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5899 ((draw_info->stroke.alpha != (
double) TransparentAlpha) ||
5900 (draw_info->stroke_pattern != (Image *) NULL)))
5912 closed_path=primitive_info[0].closed_subpath;
5913 i=(ssize_t) primitive_info[0].coordinates;
5914 point_x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
5915 point_y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
5916 if ((point_x < MagickEpsilon) && (point_y < MagickEpsilon))
5917 closed_path=MagickTrue;
5918 if ((((draw_info->linecap == RoundCap) ||
5919 (closed_path != MagickFalse)) &&
5920 (draw_info->linejoin == RoundJoin)) ||
5921 (primitive_info[i].primitive != UndefinedPrimitive))
5923 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5924 primitive_info,exception);
5927 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5928 clone_info->stroke_width=0.0;
5929 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5930 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5931 primitive_info,exception);
5932 clone_info=DestroyDrawInfo(clone_info);
5933 if (status != MagickFalse)
5934 status&=(MagickStatusType) DrawStrokePolygon(image,draw_info,
5935 primitive_info,exception);
5938 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5939 primitive_info,exception);
5943 image_view=DestroyCacheView(image_view);
5944 if (draw_info->compliance == SVGCompliance)
5946 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5947 (Image *) NULL,exception);
5948 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5949 (Image *) NULL,exception);
5951 if (draw_info->debug != MagickFalse)
5952 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
" end draw-primitive");
5953 return(status != 0 ? MagickTrue : MagickFalse);
5986static MagickBooleanType DrawRoundLinecap(Image *image,
5987 const DrawInfo *draw_info,
const PrimitiveInfo *primitive_info,
5988 ExceptionInfo *exception)
5996 if (primitive_info->coordinates < 1)
5997 return(MagickFalse);
5998 for (i=0; i < 4; i++)
5999 linecap[i]=(*primitive_info);
6000 linecap[0].coordinates=4;
6001 linecap[1].point.x+=2.0*MagickEpsilon;
6002 linecap[2].point.x+=2.0*MagickEpsilon;
6003 linecap[2].point.y+=2.0*MagickEpsilon;
6004 linecap[3].point.y+=2.0*MagickEpsilon;
6005 linecap[4].primitive=UndefinedPrimitive;
6006 return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
6009static MagickBooleanType DrawStrokePolygon(Image *image,
6010 const DrawInfo *draw_info,
const PrimitiveInfo *primitive_info,
6011 ExceptionInfo *exception)
6032 if (draw_info->debug != MagickFalse)
6033 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6034 " begin draw-stroke-polygon");
6035 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6036 clone_info->fill=draw_info->stroke;
6037 if (clone_info->fill_pattern != (Image *) NULL)
6038 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
6039 if (clone_info->stroke_pattern != (Image *) NULL)
6040 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
6041 MagickTrue,exception);
6042 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
6043 clone_info->stroke_width=0.0;
6044 clone_info->fill_rule=NonZeroRule;
6046 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=(ptrdiff_t) p->coordinates)
6048 if (p->coordinates == 1)
6050 stroke_polygon=TraceStrokePolygon(draw_info,p,exception);
6051 if (stroke_polygon == (PrimitiveInfo *) NULL)
6056 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
6057 stroke_polygon,exception);
6058 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
6061 q=p+p->coordinates-1;
6062 closed_path=p->closed_subpath;
6063 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
6065 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,p,
6067 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,q,
6071 clone_info=DestroyDrawInfo(clone_info);
6072 if (draw_info->debug != MagickFalse)
6073 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6074 " end draw-stroke-polygon");
6075 return(status != 0 ? MagickTrue : MagickFalse);
6101MagickExport
void GetAffineMatrix(AffineMatrix *affine_matrix)
6103 if (IsEventLogging() != MagickFalse)
6104 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
6105 assert(affine_matrix != (AffineMatrix *) NULL);
6106 (void) memset(affine_matrix,0,
sizeof(*affine_matrix));
6107 affine_matrix->sx=1.0;
6108 affine_matrix->sy=1.0;
6135MagickExport
void GetDrawInfo(
const ImageInfo *image_info,DrawInfo *draw_info)
6149 assert(draw_info != (DrawInfo *) NULL);
6150 if (IsEventLogging() != MagickFalse)
6151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
6152 (void) memset(draw_info,0,
sizeof(*draw_info));
6153 draw_info->image_info=CloneImageInfo(image_info);
6154 GetAffineMatrix(&draw_info->affine);
6155 exception=AcquireExceptionInfo();
6156 (void) QueryColorCompliance(
"#000F",AllCompliance,&draw_info->fill,
6158 (void) QueryColorCompliance(
"#FFF0",AllCompliance,&draw_info->stroke,
6160 draw_info->stroke_antialias=draw_info->image_info->antialias;
6161 draw_info->stroke_width=1.0;
6162 draw_info->fill_rule=EvenOddRule;
6163 draw_info->alpha=OpaqueAlpha;
6164 draw_info->fill_alpha=OpaqueAlpha;
6165 draw_info->stroke_alpha=OpaqueAlpha;
6166 draw_info->linecap=ButtCap;
6167 draw_info->linejoin=MiterJoin;
6168 draw_info->miterlimit=10;
6169 draw_info->decorate=NoDecoration;
6170 draw_info->pointsize=12.0;
6171 draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
6172 draw_info->compose=OverCompositeOp;
6173 draw_info->render=MagickTrue;
6174 draw_info->clip_path=MagickFalse;
6175 draw_info->debug=(GetLogEventMask() & (DrawEvent | AnnotateEvent)) != 0 ?
6176 MagickTrue : MagickFalse;
6177 if (draw_info->image_info->font != (
char *) NULL)
6178 draw_info->font=AcquireString(draw_info->image_info->font);
6179 if (draw_info->image_info->density != (
char *) NULL)
6180 draw_info->density=AcquireString(draw_info->image_info->density);
6181 draw_info->text_antialias=draw_info->image_info->antialias;
6182 if (fabs(draw_info->image_info->pointsize) >= MagickEpsilon)
6183 draw_info->pointsize=draw_info->image_info->pointsize;
6184 draw_info->border_color=draw_info->image_info->border_color;
6185 if (draw_info->image_info->server_name != (
char *) NULL)
6186 draw_info->server_name=AcquireString(draw_info->image_info->server_name);
6187 option=GetImageOption(draw_info->image_info,
"direction");
6188 if (option != (
const char *) NULL)
6189 draw_info->direction=(DirectionType) ParseCommandOption(
6190 MagickDirectionOptions,MagickFalse,option);
6192 draw_info->direction=UndefinedDirection;
6193 option=GetImageOption(draw_info->image_info,
"encoding");
6194 if (option != (
const char *) NULL)
6195 (void) CloneString(&draw_info->encoding,option);
6196 option=GetImageOption(draw_info->image_info,
"family");
6197 if (option != (
const char *) NULL)
6198 (void) CloneString(&draw_info->family,option);
6199 option=GetImageOption(draw_info->image_info,
"fill");
6200 if (option != (
const char *) NULL)
6201 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
6203 option=GetImageOption(draw_info->image_info,
"gravity");
6204 if (option != (
const char *) NULL)
6205 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
6206 MagickFalse,option);
6207 option=GetImageOption(draw_info->image_info,
"interline-spacing");
6208 if (option != (
const char *) NULL)
6209 draw_info->interline_spacing=GetDrawValue(option,&next_token);
6210 option=GetImageOption(draw_info->image_info,
"interword-spacing");
6211 if (option != (
const char *) NULL)
6212 draw_info->interword_spacing=GetDrawValue(option,&next_token);
6213 option=GetImageOption(draw_info->image_info,
"kerning");
6214 if (option != (
const char *) NULL)
6215 draw_info->kerning=GetDrawValue(option,&next_token);
6216 option=GetImageOption(draw_info->image_info,
"stroke");
6217 if (option != (
const char *) NULL)
6218 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
6220 option=GetImageOption(draw_info->image_info,
"strokewidth");
6221 if (option != (
const char *) NULL)
6222 draw_info->stroke_width=GetDrawValue(option,&next_token);
6223 option=GetImageOption(draw_info->image_info,
"style");
6224 if (option != (
const char *) NULL)
6225 draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
6226 MagickFalse,option);
6227 option=GetImageOption(draw_info->image_info,
"undercolor");
6228 if (option != (
const char *) NULL)
6229 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
6231 option=GetImageOption(draw_info->image_info,
"weight");
6232 if (option != (
const char *) NULL)
6237 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
6239 weight=(ssize_t) StringToUnsignedLong(option);
6240 draw_info->weight=(size_t) weight;
6242 option=GetImageOption(draw_info->image_info,
"word-break");
6243 if (option != (
const char *) NULL)
6244 draw_info->word_break=(WordBreakType) ParseCommandOption(
6245 MagickWordBreakOptions,MagickFalse,option);
6246 exception=DestroyExceptionInfo(exception);
6247 draw_info->signature=MagickCoreSignature;
6275static inline double Permutate(
const ssize_t n,
const ssize_t k)
6284 for (i=k+1; i <= n; i++)
6286 for (i=1; i <= (n-k); i++)
6307static MagickBooleanType TraceArc(MVGInfo *mvg_info,
const PointInfo start,
6308 const PointInfo end,
const PointInfo degrees)
6314 center.x=0.5*(end.x+start.x);
6315 center.y=0.5*(end.y+start.y);
6316 radius.x=fabs(center.x-start.x);
6317 radius.y=fabs(center.y-start.y);
6318 return(TraceEllipse(mvg_info,center,radius,degrees));
6321static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,
const PointInfo start,
6322 const PointInfo end,
const PointInfo arc,
const double angle,
6323 const MagickBooleanType large_arc,
const MagickBooleanType sweep)
6360 offset=mvg_info->offset;
6361 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6362 primitive_info->coordinates=0;
6363 if ((fabs(start.x-end.x) < MagickEpsilon) &&
6364 (fabs(start.y-end.y) < MagickEpsilon))
6365 return(TracePoint(primitive_info,end));
6366 radii.x=fabs(arc.x);
6367 radii.y=fabs(arc.y);
6368 if ((radii.x < MagickEpsilon) || (radii.y < MagickEpsilon))
6369 return(TraceLine(primitive_info,start,end));
6370 cosine=cos(DegreesToRadians(fmod((
double) angle,360.0)));
6371 sine=sin(DegreesToRadians(fmod((
double) angle,360.0)));
6372 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
6373 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
6374 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
6376 if (delta < MagickEpsilon)
6377 return(TraceLine(primitive_info,start,end));
6380 radii.x*=sqrt((
double) delta);
6381 radii.y*=sqrt((
double) delta);
6383 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
6384 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
6385 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
6386 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
6387 alpha=points[1].x-points[0].x;
6388 beta=points[1].y-points[0].y;
6389 if (fabs(alpha*alpha+beta*beta) < MagickEpsilon)
6390 return(TraceLine(primitive_info,start,end));
6391 factor=MagickSafeReciprocal(alpha*alpha+beta*beta)-0.25;
6396 factor=sqrt((
double) factor);
6397 if (sweep == large_arc)
6400 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
6401 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
6402 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
6403 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
6404 if ((theta < 0.0) && (sweep != MagickFalse))
6405 theta+=2.0*MagickPI;
6407 if ((theta > 0.0) && (sweep == MagickFalse))
6408 theta-=2.0*MagickPI;
6409 arc_segments=(size_t) CastDoubleToSsizeT(ceil(fabs((
double) (theta/(0.5*
6410 MagickPI+MagickEpsilon)))));
6413 for (i=0; i < (ssize_t) arc_segments; i++)
6415 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
6416 gamma=(8.0/3.0)*sin(fmod((
double) (0.5*beta),DegreesToRadians(360.0)))*
6417 sin(fmod((
double) (0.5*beta),DegreesToRadians(360.0)))/
6418 sin(fmod((
double) beta,DegreesToRadians(360.0)));
6419 points[0].x=(double) (center.x+cos(fmod((
double) (alpha+(
double) i*theta/
6420 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((
double) (alpha+
6421 (
double) i*theta/arc_segments),DegreesToRadians(360.0))));
6422 points[0].y=(double) (center.y+sin(fmod((
double) (alpha+(
double) i*theta/
6423 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((
double) (alpha+
6424 (
double) i*theta/arc_segments),DegreesToRadians(360.0))));
6425 points[2].x=(double) (center.x+cos(fmod((
double) (alpha+(
double) (i+1)*
6426 theta/arc_segments),DegreesToRadians(360.0))));
6427 points[2].y=(double) (center.y+sin(fmod((
double) (alpha+(
double) (i+1)*
6428 theta/arc_segments),DegreesToRadians(360.0))));
6429 points[1].x=(double) (points[2].x+gamma*sin(fmod((
double) (alpha+(
double)
6430 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6431 points[1].y=(double) (points[2].y-gamma*cos(fmod((
double) (alpha+(
double)
6432 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6433 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
6434 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
6435 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
6437 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
6439 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
6441 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
6443 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
6445 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
6447 if (i == (ssize_t) (arc_segments-1))
6449 status&=(MagickStatusType) TraceBezier(mvg_info,4);
6452 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
6453 mvg_info->offset+=(ssize_t) p->coordinates;
6454 p+=(ptrdiff_t) p->coordinates;
6457 return(MagickFalse);
6458 mvg_info->offset=offset;
6459 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6460 primitive_info->coordinates=(size_t) (p-primitive_info);
6461 primitive_info->closed_subpath=MagickFalse;
6462 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6464 p->primitive=primitive_info->primitive;
6470static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
6471 const size_t number_coordinates)
6500 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6501 quantum=number_coordinates;
6502 for (i=0; i < (ssize_t) number_coordinates; i++)
6504 for (j=i+1; j < (ssize_t) number_coordinates; j++)
6506 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
6507 if (alpha > (
double) GetMaxMemoryRequest())
6509 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6510 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
6511 return(MagickFalse);
6513 if (alpha > (
double) quantum)
6514 quantum=(size_t) alpha;
6515 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
6516 if (alpha > (
double) quantum)
6517 quantum=(size_t) alpha;
6520 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6521 quantum=MagickMin(quantum/number_coordinates,BezierQuantum);
6522 coefficients=(
double *) AcquireQuantumMemory(number_coordinates,
6523 sizeof(*coefficients));
6524 points=(PointInfo *) AcquireQuantumMemory(quantum,number_coordinates*
6526 if ((coefficients == (
double *) NULL) || (points == (PointInfo *) NULL))
6528 if (points != (PointInfo *) NULL)
6529 points=(PointInfo *) RelinquishMagickMemory(points);
6530 if (coefficients != (
double *) NULL)
6531 coefficients=(
double *) RelinquishMagickMemory(coefficients);
6532 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6533 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
6534 return(MagickFalse);
6536 control_points=quantum*number_coordinates;
6537 if (CheckPrimitiveExtent(mvg_info,(
double) control_points+1) == MagickFalse)
6539 points=(PointInfo *) RelinquishMagickMemory(points);
6540 coefficients=(
double *) RelinquishMagickMemory(coefficients);
6541 return(MagickFalse);
6543 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6547 end=primitive_info[number_coordinates-1].point;
6548 for (i=0; i < (ssize_t) number_coordinates; i++)
6549 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
6551 for (i=0; i < (ssize_t) control_points; i++)
6556 alpha=pow((
double) (1.0-weight),(
double) number_coordinates-1.0);
6557 for (j=0; j < (ssize_t) number_coordinates; j++)
6559 point.x+=alpha*coefficients[j]*p->point.x;
6560 point.y+=alpha*coefficients[j]*p->point.y;
6561 alpha*=weight/(1.0-weight);
6565 weight+=1.0/control_points;
6571 for (i=0; i < (ssize_t) control_points; i++)
6573 if (TracePoint(p,points[i]) == MagickFalse)
6575 points=(PointInfo *) RelinquishMagickMemory(points);
6576 coefficients=(
double *) RelinquishMagickMemory(coefficients);
6577 return(MagickFalse);
6579 p+=(ptrdiff_t) p->coordinates;
6581 if (TracePoint(p,end) == MagickFalse)
6583 points=(PointInfo *) RelinquishMagickMemory(points);
6584 coefficients=(
double *) RelinquishMagickMemory(coefficients);
6585 return(MagickFalse);
6587 p+=(ptrdiff_t) p->coordinates;
6588 primitive_info->coordinates=(size_t) (p-primitive_info);
6589 primitive_info->closed_subpath=MagickFalse;
6590 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6592 p->primitive=primitive_info->primitive;
6595 points=(PointInfo *) RelinquishMagickMemory(points);
6596 coefficients=(
double *) RelinquishMagickMemory(coefficients);
6600static MagickBooleanType TraceCircle(MVGInfo *mvg_info,
const PointInfo start,
6601 const PointInfo end)
6612 alpha=end.x-start.x;
6614 radius=hypot((
double) alpha,(
double) beta);
6615 offset.x=(double) radius;
6616 offset.y=(double) radius;
6619 return(TraceEllipse(mvg_info,start,offset,degrees));
6622static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,
const PointInfo center,
6623 const PointInfo radii,
const PointInfo arc)
6648 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6649 primitive_info->coordinates=0;
6650 if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
6652 delta=MagickSafeReciprocal(MagickMax(radii.x,radii.y));
6653 step=MagickPI/(MagickPI*MagickSafeReciprocal(delta))/8.0;
6654 angle.x=DegreesToRadians(arc.x);
6658 angle.y=DegreesToRadians(y);
6659 coordinates=ceil((angle.y-angle.x)/step+1.0);
6660 if (CheckPrimitiveExtent(mvg_info,coordinates+1) == MagickFalse)
6661 return(MagickFalse);
6663 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6664 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
6666 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
6667 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
6668 if (i++ >= (ssize_t) coordinates)
6670 if (TracePoint(p,point) == MagickFalse)
6671 return(MagickFalse);
6672 p+=(ptrdiff_t) p->coordinates;
6674 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
6675 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
6676 if (TracePoint(p,point) == MagickFalse)
6677 return(MagickFalse);
6678 p+=(ptrdiff_t) p->coordinates;
6679 primitive_info->coordinates=(size_t) (p-primitive_info);
6680 primitive_info->closed_subpath=MagickFalse;
6681 x=fabs(primitive_info[0].point.x-
6682 primitive_info[primitive_info->coordinates-1].point.x);
6683 y=fabs(primitive_info[0].point.y-
6684 primitive_info[primitive_info->coordinates-1].point.y);
6685 if ((x < MagickEpsilon) && (y < MagickEpsilon))
6686 primitive_info->closed_subpath=MagickTrue;
6687 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6689 p->primitive=primitive_info->primitive;
6695static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
6696 const PointInfo start,
const PointInfo end)
6698 if (TracePoint(primitive_info,start) == MagickFalse)
6699 return(MagickFalse);
6700 if (TracePoint(primitive_info+1,end) == MagickFalse)
6701 return(MagickFalse);
6702 (primitive_info+1)->primitive=primitive_info->primitive;
6703 primitive_info->coordinates=2;
6704 primitive_info->closed_subpath=MagickFalse;
6708static ssize_t TracePath(MVGInfo *mvg_info,
const char *path,
6709 ExceptionInfo *exception)
6713 token[MagickPathExtent] =
"";
6731 points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
6754 subpath_offset=mvg_info->offset;
6755 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6758 number_coordinates=0;
6760 primitive_type=primitive_info->primitive;
6762 for (p=path; *p !=
'\0'; )
6764 if (status == MagickFalse)
6766 while (isspace((
int) ((
unsigned char) *p)) != 0)
6770 last_attribute=attribute;
6771 attribute=(int) (*p++);
6781 large_arc = MagickFalse,
6782 sweep = MagickFalse;
6792 (void) GetNextToken(p,&p,MagickPathExtent,token);
6793 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6794 ThrowPointExpectedException(token,exception);
6796 (void) GetNextToken(p,&p,MagickPathExtent,token);
6797 arc.x=GetDrawValue(token,&next_token);
6798 if (token == next_token)
6799 ThrowPointExpectedException(token,exception);
6800 (void) GetNextToken(p,&p,MagickPathExtent,token);
6801 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6802 ThrowPointExpectedException(token,exception);
6804 (void) GetNextToken(p,&p,MagickPathExtent,token);
6805 arc.y=GetDrawValue(token,&next_token);
6806 if (token == next_token)
6807 ThrowPointExpectedException(token,exception);
6808 (void) GetNextToken(p,&p,MagickPathExtent,token);
6809 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6810 ThrowPointExpectedException(token,exception);
6812 (void) GetNextToken(p,&p,MagickPathExtent,token);
6813 angle=GetDrawValue(token,&next_token);
6814 if (token == next_token)
6815 ThrowPointExpectedException(token,exception);
6816 (void) GetNextToken(p,&p,MagickPathExtent,token);
6817 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6818 ThrowPointExpectedException(token,exception);
6820 (void) GetNextToken(p,&p,MagickPathExtent,token);
6821 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6822 (void) GetNextToken(p,&p,MagickPathExtent,token);
6823 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6824 ThrowPointExpectedException(token,exception);
6826 (void) GetNextToken(p,&p,MagickPathExtent,token);
6827 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6828 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6829 ThrowPointExpectedException(token,exception);
6831 (void) GetNextToken(p,&p,MagickPathExtent,token);
6832 (void) GetNextToken(p,&p,MagickPathExtent,token);
6833 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6834 ThrowPointExpectedException(token,exception);
6836 (void) GetNextToken(p,&p,MagickPathExtent,token);
6837 x=GetDrawValue(token,&next_token);
6838 if (token == next_token)
6839 ThrowPointExpectedException(token,exception);
6840 (void) GetNextToken(p,&p,MagickPathExtent,token);
6841 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6842 ThrowPointExpectedException(token,exception);
6844 (void) GetNextToken(p,&p,MagickPathExtent,token);
6845 y=GetDrawValue(token,&next_token);
6846 if (token == next_token)
6847 ThrowPointExpectedException(token,exception);
6848 end.x=(double) (attribute == (
int)
'A' ? x : point.x+x);
6849 end.y=(double) (attribute == (
int)
'A' ? y : point.y+y);
6850 if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
6852 q=(*mvg_info->primitive_info)+mvg_info->offset;
6853 mvg_info->offset+=(ssize_t) q->coordinates;
6854 q+=(ptrdiff_t) q->coordinates;
6856 while (isspace((
int) ((
unsigned char) *p)) != 0)
6860 }
while (IsValidPoint(p) != MagickFalse);
6872 for (i=1; i < 4; i++)
6874 (void) GetNextToken(p,&p,MagickPathExtent,token);
6875 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6876 ThrowPointExpectedException(token,exception);
6878 (void) GetNextToken(p,&p,MagickPathExtent,token);
6879 x=GetDrawValue(token,&next_token);
6880 if (token == next_token)
6881 ThrowPointExpectedException(token,exception);
6882 (void) GetNextToken(p,&p,MagickPathExtent,token);
6883 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6884 ThrowPointExpectedException(token,exception);
6886 (void) GetNextToken(p,&p,MagickPathExtent,token);
6887 y=GetDrawValue(token,&next_token);
6888 if (token == next_token)
6889 ThrowPointExpectedException(token,exception);
6890 end.x=(double) (attribute == (
int)
'C' ? x : point.x+x);
6891 end.y=(double) (attribute == (
int)
'C' ? y : point.y+y);
6894 for (i=0; i < 4; i++)
6895 (q+i)->point=points[i];
6896 if (TraceBezier(mvg_info,4) == MagickFalse)
6898 q=(*mvg_info->primitive_info)+mvg_info->offset;
6899 mvg_info->offset+=(ssize_t) q->coordinates;
6900 q+=(ptrdiff_t) q->coordinates;
6902 while (isspace((
int) ((
unsigned char) *p)) != 0)
6906 }
while (IsValidPoint(p) != MagickFalse);
6914 (void) GetNextToken(p,&p,MagickPathExtent,token);
6915 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6916 ThrowPointExpectedException(token,exception);
6918 (void) GetNextToken(p,&p,MagickPathExtent,token);
6919 x=GetDrawValue(token,&next_token);
6920 if (token == next_token)
6921 ThrowPointExpectedException(token,exception);
6922 point.x=(double) (attribute == (
int)
'H' ? x: point.x+x);
6923 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6925 q=(*mvg_info->primitive_info)+mvg_info->offset;
6926 if (TracePoint(q,point) == MagickFalse)
6928 mvg_info->offset+=(ssize_t) q->coordinates;
6929 q+=(ptrdiff_t) q->coordinates;
6930 while (isspace((
int) ((
unsigned char) *p)) != 0)
6934 }
while (IsValidPoint(p) != MagickFalse);
6945 (void) GetNextToken(p,&p,MagickPathExtent,token);
6946 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6947 ThrowPointExpectedException(token,exception);
6949 (void) GetNextToken(p,&p,MagickPathExtent,token);
6950 x=GetDrawValue(token,&next_token);
6951 if (token == next_token)
6952 ThrowPointExpectedException(token,exception);
6953 (void) GetNextToken(p,&p,MagickPathExtent,token);
6954 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6955 ThrowPointExpectedException(token,exception);
6957 (void) GetNextToken(p,&p,MagickPathExtent,token);
6958 y=GetDrawValue(token,&next_token);
6959 if (token == next_token)
6960 ThrowPointExpectedException(token,exception);
6961 point.x=(double) (attribute == (
int)
'L' ? x : point.x+x);
6962 point.y=(double) (attribute == (
int)
'L' ? y : point.y+y);
6963 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6965 q=(*mvg_info->primitive_info)+mvg_info->offset;
6966 if (TracePoint(q,point) == MagickFalse)
6968 mvg_info->offset+=(ssize_t) q->coordinates;
6969 q+=(ptrdiff_t) q->coordinates;
6970 while (isspace((
int) ((
unsigned char) *p)) != 0)
6974 }
while (IsValidPoint(p) != MagickFalse);
6983 if (mvg_info->offset != subpath_offset)
6985 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6986 primitive_info->coordinates=(size_t) (q-primitive_info);
6987 number_coordinates+=primitive_info->coordinates;
6989 subpath_offset=mvg_info->offset;
6994 (void) GetNextToken(p,&p,MagickPathExtent,token);
6995 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
6996 ThrowPointExpectedException(token,exception);
6998 (void) GetNextToken(p,&p,MagickPathExtent,token);
6999 x=GetDrawValue(token,&next_token);
7000 if (token == next_token)
7001 ThrowPointExpectedException(token,exception);
7002 (void) GetNextToken(p,&p,MagickPathExtent,token);
7003 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7004 ThrowPointExpectedException(token,exception);
7006 (void) GetNextToken(p,&p,MagickPathExtent,token);
7007 y=GetDrawValue(token,&next_token);
7008 if (token == next_token)
7009 ThrowPointExpectedException(token,exception);
7010 point.x=(double) (attribute == (
int)
'M' ? x : point.x+x);
7011 point.y=(double) (attribute == (
int)
'M' ? y : point.y+y);
7015 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7017 q=(*mvg_info->primitive_info)+mvg_info->offset;
7018 if (TracePoint(q,point) == MagickFalse)
7020 mvg_info->offset+=(ssize_t) q->coordinates;
7021 q+=(ptrdiff_t) q->coordinates;
7022 while (isspace((
int) ((
unsigned char) *p)) != 0)
7026 }
while (IsValidPoint(p) != MagickFalse);
7038 for (i=1; i < 3; i++)
7040 (void) GetNextToken(p,&p,MagickPathExtent,token);
7041 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7042 ThrowPointExpectedException(token,exception);
7044 (void) GetNextToken(p,&p,MagickPathExtent,token);
7045 x=GetDrawValue(token,&next_token);
7046 if (token == next_token)
7047 ThrowPointExpectedException(token,exception);
7048 (void) GetNextToken(p,&p,MagickPathExtent,token);
7049 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7050 ThrowPointExpectedException(token,exception);
7052 (void) GetNextToken(p,&p,MagickPathExtent,token);
7053 y=GetDrawValue(token,&next_token);
7054 if (token == next_token)
7055 ThrowPointExpectedException(token,exception);
7058 end.x=(double) (attribute == (
int)
'Q' ? x : point.x+x);
7059 end.y=(double) (attribute == (
int)
'Q' ? y : point.y+y);
7062 for (i=0; i < 3; i++)
7063 (q+i)->point=points[i];
7064 if (TraceBezier(mvg_info,3) == MagickFalse)
7066 q=(*mvg_info->primitive_info)+mvg_info->offset;
7067 mvg_info->offset+=(ssize_t) q->coordinates;
7068 q+=(ptrdiff_t) q->coordinates;
7070 while (isspace((
int) ((
unsigned char) *p)) != 0)
7074 }
while (IsValidPoint(p) != MagickFalse);
7085 points[0]=points[3];
7086 points[1].x=2.0*points[3].x-points[2].x;
7087 points[1].y=2.0*points[3].y-points[2].y;
7088 for (i=2; i < 4; i++)
7090 (void) GetNextToken(p,&p,MagickPathExtent,token);
7091 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7092 ThrowPointExpectedException(token,exception);
7094 (void) GetNextToken(p,&p,MagickPathExtent,token);
7095 x=GetDrawValue(token,&next_token);
7096 if (token == next_token)
7097 ThrowPointExpectedException(token,exception);
7098 (void) GetNextToken(p,&p,MagickPathExtent,token);
7099 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7100 ThrowPointExpectedException(token,exception);
7102 (void) GetNextToken(p,&p,MagickPathExtent,token);
7103 y=GetDrawValue(token,&next_token);
7104 if (token == next_token)
7105 ThrowPointExpectedException(token,exception);
7108 end.x=(double) (attribute == (
int)
'S' ? x : point.x+x);
7109 end.y=(double) (attribute == (
int)
'S' ? y : point.y+y);
7112 if (strchr(
"CcSs",last_attribute) == (
char *) NULL)
7117 for (i=0; i < 4; i++)
7118 (q+i)->point=points[i];
7119 if (TraceBezier(mvg_info,4) == MagickFalse)
7121 q=(*mvg_info->primitive_info)+mvg_info->offset;
7122 mvg_info->offset+=(ssize_t) q->coordinates;
7123 q+=(ptrdiff_t) q->coordinates;
7125 last_attribute=attribute;
7126 while (isspace((
int) ((
unsigned char) *p)) != 0)
7130 }
while (IsValidPoint(p) != MagickFalse);
7141 points[0]=points[2];
7142 points[1].x=2.0*points[2].x-points[1].x;
7143 points[1].y=2.0*points[2].y-points[1].y;
7144 for (i=2; i < 3; i++)
7146 (void) GetNextToken(p,&p,MagickPathExtent,token);
7147 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7148 ThrowPointExpectedException(token,exception);
7150 (void) GetNextToken(p,&p,MagickPathExtent,token);
7151 x=GetDrawValue(token,&next_token);
7152 if (token == next_token)
7153 ThrowPointExpectedException(token,exception);
7154 (void) GetNextToken(p,&p,MagickPathExtent,token);
7155 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7156 ThrowPointExpectedException(token,exception);
7158 (void) GetNextToken(p,&p,MagickPathExtent,token);
7159 y=GetDrawValue(token,&next_token);
7160 if (token == next_token)
7161 ThrowPointExpectedException(token,exception);
7162 end.x=(double) (attribute == (
int)
'T' ? x : point.x+x);
7163 end.y=(double) (attribute == (
int)
'T' ? y : point.y+y);
7166 if (status == MagickFalse)
7168 if (strchr(
"QqTt",last_attribute) == (
char *) NULL)
7173 for (i=0; i < 3; i++)
7174 (q+i)->point=points[i];
7175 if (TraceBezier(mvg_info,3) == MagickFalse)
7177 q=(*mvg_info->primitive_info)+mvg_info->offset;
7178 mvg_info->offset+=(ssize_t) q->coordinates;
7179 q+=(ptrdiff_t) q->coordinates;
7181 last_attribute=attribute;
7182 while (isspace((
int) ((
unsigned char) *p)) != 0)
7186 }
while (IsValidPoint(p) != MagickFalse);
7197 (void) GetNextToken(p,&p,MagickPathExtent,token);
7198 if (IsValidListChar((
int) ((
unsigned char) *token)) == MagickFalse)
7199 ThrowPointExpectedException(token,exception);
7201 (void) GetNextToken(p,&p,MagickPathExtent,token);
7202 y=GetDrawValue(token,&next_token);
7203 if (token == next_token)
7204 ThrowPointExpectedException(token,exception);
7205 point.y=(double) (attribute == (
int)
'V' ? y : point.y+y);
7206 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7208 q=(*mvg_info->primitive_info)+mvg_info->offset;
7209 if (TracePoint(q,point) == MagickFalse)
7211 mvg_info->offset+=(ssize_t) q->coordinates;
7212 q+=(ptrdiff_t) q->coordinates;
7213 while (isspace((
int) ((
unsigned char) *p)) != 0)
7217 }
while (IsValidPoint(p) != MagickFalse);
7227 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7229 q=(*mvg_info->primitive_info)+mvg_info->offset;
7230 if (TracePoint(q,point) == MagickFalse)
7232 mvg_info->offset+=(ssize_t) q->coordinates;
7233 q+=(ptrdiff_t) q->coordinates;
7234 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7235 primitive_info->coordinates=(size_t) (q-primitive_info);
7236 primitive_info->closed_subpath=MagickTrue;
7237 number_coordinates+=primitive_info->coordinates;
7239 subpath_offset=mvg_info->offset;
7245 ThrowPointExpectedException(token,exception);
7250 if (status == MagickFalse)
7252 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7253 primitive_info->coordinates=(size_t) (q-primitive_info);
7254 number_coordinates+=primitive_info->coordinates;
7255 for (i=0; i < (ssize_t) number_coordinates; i++)
7258 q->primitive=primitive_type;
7260 q->method=FillToBorderMethod;
7263 return((ssize_t) number_coordinates);
7266static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
7267 const PointInfo start,
const PointInfo end)
7279 if (TracePoint(p,start) == MagickFalse)
7280 return(MagickFalse);
7281 p+=(ptrdiff_t) p->coordinates;
7284 if (TracePoint(p,point) == MagickFalse)
7285 return(MagickFalse);
7286 p+=(ptrdiff_t) p->coordinates;
7287 if (TracePoint(p,end) == MagickFalse)
7288 return(MagickFalse);
7289 p+=(ptrdiff_t) p->coordinates;
7292 if (TracePoint(p,point) == MagickFalse)
7293 return(MagickFalse);
7294 p+=(ptrdiff_t) p->coordinates;
7295 if (TracePoint(p,start) == MagickFalse)
7296 return(MagickFalse);
7297 p+=(ptrdiff_t) p->coordinates;
7298 primitive_info->coordinates=(size_t) (p-primitive_info);
7299 primitive_info->closed_subpath=MagickTrue;
7300 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7302 p->primitive=primitive_info->primitive;
7308static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
7309 const PointInfo start,
const PointInfo end,PointInfo arc)
7328 offset=mvg_info->offset;
7329 segment.x=fabs(end.x-start.x);
7330 segment.y=fabs(end.y-start.y);
7331 if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
7333 (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
7336 if (arc.x > (0.5*segment.x))
7337 arc.x=0.5*segment.x;
7338 if (arc.y > (0.5*segment.y))
7339 arc.y=0.5*segment.y;
7340 point.x=start.x+segment.x-arc.x;
7341 point.y=start.y+arc.y;
7344 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7345 return(MagickFalse);
7346 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7347 mvg_info->offset+=(ssize_t) p->coordinates;
7348 point.x=start.x+segment.x-arc.x;
7349 point.y=start.y+segment.y-arc.y;
7352 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7353 return(MagickFalse);
7354 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7355 mvg_info->offset+=(ssize_t) p->coordinates;
7356 point.x=start.x+arc.x;
7357 point.y=start.y+segment.y-arc.y;
7360 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7361 return(MagickFalse);
7362 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7363 mvg_info->offset+=(ssize_t) p->coordinates;
7364 point.x=start.x+arc.x;
7365 point.y=start.y+arc.y;
7368 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7369 return(MagickFalse);
7370 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7371 mvg_info->offset+=(ssize_t) p->coordinates;
7372 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7373 return(MagickFalse);
7374 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7375 if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
7376 return(MagickFalse);
7377 p+=(ptrdiff_t) p->coordinates;
7378 mvg_info->offset=offset;
7379 primitive_info=(*mvg_info->primitive_info)+offset;
7380 primitive_info->coordinates=(size_t) (p-primitive_info);
7381 primitive_info->closed_subpath=MagickTrue;
7382 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7384 p->primitive=primitive_info->primitive;
7390static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
7391 const size_t number_vertices,
const double offset)
7408 for (i=1; i < (ssize_t) number_vertices; i++)
7410 dx=primitive_info[0].point.x-primitive_info[i].point.x;
7411 dy=primitive_info[0].point.y-primitive_info[i].point.y;
7412 if ((fabs((
double) dx) >= MagickEpsilon) ||
7413 (fabs((
double) dy) >= MagickEpsilon))
7416 if (i == (ssize_t) number_vertices)
7417 i=(ssize_t) number_vertices-1L;
7418 distance=hypot((
double) dx,(
double) dy);
7419 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
7420 dx*(distance+offset)/distance);
7421 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
7422 dy*(distance+offset)/distance);
7423 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
7425 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
7426 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
7427 if ((fabs((
double) dx) >= MagickEpsilon) ||
7428 (fabs((
double) dy) >= MagickEpsilon))
7431 distance=hypot((
double) dx,(
double) dy);
7432 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
7433 dx*(distance+offset)/distance);
7434 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
7435 dy*(distance+offset)/distance);
7439static PrimitiveInfo *TraceStrokePolygon(
const DrawInfo *draw_info,
7440 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
7442#define MaxStrokePad (6*BezierQuantum+360)
7443#define CheckPathExtent(pad_p,pad_q) \
7445 if ((pad_p) > MaxBezierCoordinates) \
7446 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7448 if ((p+(ptrdiff_t) (pad_p)) >= (ssize_t) extent_p) \
7450 if (~extent_p < (pad_p)) \
7451 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7454 extent_p+=(pad_p); \
7455 stroke_p=(PointInfo *) ResizeQuantumMemory(stroke_p,extent_p+ \
7456 MaxStrokePad,sizeof(*stroke_p)); \
7459 if ((pad_q) > MaxBezierCoordinates) \
7460 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7462 if ((q+(ptrdiff_t) (pad_q)) >= (ssize_t) extent_q) \
7464 if (~extent_q < (pad_q)) \
7465 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7468 extent_q+=(pad_q); \
7469 stroke_q=(PointInfo *) ResizeQuantumMemory(stroke_q,extent_q+ \
7470 MaxStrokePad,sizeof(*stroke_q)); \
7473 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL)) \
7475 if (stroke_p != (PointInfo *) NULL) \
7476 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7477 if (stroke_q != (PointInfo *) NULL) \
7478 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7479 polygon_primitive=(PrimitiveInfo *) \
7480 RelinquishMagickMemory(polygon_primitive); \
7481 (void) ThrowMagickException(exception,GetMagickModule(), \
7482 ResourceLimitError,"MemoryAllocationFailed","`%s'",""); \
7483 return((PrimitiveInfo *) NULL); \
7487 typedef struct _StrokeSegment
7533 inverse_slope = {0.0, 0.0},
7540 number_vertices=primitive_info->coordinates;
7541 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((
size_t)
7542 number_vertices+2UL,
sizeof(*polygon_primitive));
7543 if (polygon_primitive == (PrimitiveInfo *) NULL)
7545 (void) ThrowMagickException(exception,GetMagickModule(),
7546 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
7547 return((PrimitiveInfo *) NULL);
7549 (void) memcpy(polygon_primitive,primitive_info,(
size_t) number_vertices*
7550 sizeof(*polygon_primitive));
7551 offset.x=primitive_info[number_vertices-1].point.x-primitive_info[0].point.x;
7552 offset.y=primitive_info[number_vertices-1].point.y-primitive_info[0].point.y;
7553 closed_path=(fabs(offset.x) < MagickEpsilon) &&
7554 (fabs(offset.y) < MagickEpsilon) ? MagickTrue : MagickFalse;
7555 if ((draw_info->linejoin == RoundJoin) ||
7556 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
7558 polygon_primitive[number_vertices]=primitive_info[1];
7561 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
7565 closed_path=primitive_info[0].closed_subpath;
7568 for (n=1; n < (ssize_t) number_vertices; n++)
7570 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
7571 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
7572 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
7575 if (n == (ssize_t) number_vertices)
7577 if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
7582 stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
7583 sizeof(*stroke_polygon));
7584 stroke_polygon[0]=polygon_primitive[0];
7585 stroke_polygon[0].coordinates=0;
7586 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7588 return(stroke_polygon);
7590 n=(ssize_t) number_vertices-1L;
7592 extent_p=2*number_vertices;
7593 extent_q=2*number_vertices;
7594 stroke_p=(PointInfo *) AcquireQuantumMemory((
size_t) extent_p+MaxStrokePad,
7596 stroke_q=(PointInfo *) AcquireQuantumMemory((
size_t) extent_q+MaxStrokePad,
7598 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL))
7600 if (stroke_p != (PointInfo *) NULL)
7601 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7602 if (stroke_q != (PointInfo *) NULL)
7603 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7604 polygon_primitive=(PrimitiveInfo *)
7605 RelinquishMagickMemory(polygon_primitive);
7606 (void) ThrowMagickException(exception,GetMagickModule(),
7607 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
7608 return((PrimitiveInfo *) NULL);
7611 inverse_slope.p=0.0;
7612 if (fabs(dx.p) < MagickEpsilon)
7615 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7617 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7620 if (fabs(dy.p) < MagickEpsilon)
7623 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7625 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7630 inverse_slope.p=(-1.0*MagickSafeReciprocal(slope.p));
7632 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
7633 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
7634 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
7635 (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
7636 offset.x=sqrt((
double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
7637 offset.y=(double) (offset.x*inverse_slope.p);
7638 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
7640 box_p[0].x=polygon_primitive[0].point.x-offset.x;
7641 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
7642 box_p[1].x=polygon_primitive[n].point.x-offset.x;
7643 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
7644 box_q[0].x=polygon_primitive[0].point.x+offset.x;
7645 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
7646 box_q[1].x=polygon_primitive[n].point.x+offset.x;
7647 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
7651 box_p[0].x=polygon_primitive[0].point.x+offset.x;
7652 box_p[0].y=polygon_primitive[0].point.y+offset.y;
7653 box_p[1].x=polygon_primitive[n].point.x+offset.x;
7654 box_p[1].y=polygon_primitive[n].point.y+offset.y;
7655 box_q[0].x=polygon_primitive[0].point.x-offset.x;
7656 box_q[0].y=polygon_primitive[0].point.y-offset.y;
7657 box_q[1].x=polygon_primitive[n].point.x-offset.x;
7658 box_q[1].y=polygon_primitive[n].point.y-offset.y;
7665 stroke_q[p++]=box_q[0];
7666 stroke_p[q++]=box_p[0];
7667 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
7672 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
7673 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
7674 dot_product=dx.q*dx.q+dy.q*dy.q;
7675 if (dot_product < 0.25)
7678 inverse_slope.q=0.0;
7679 if (fabs(dx.q) < MagickEpsilon)
7682 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7684 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7687 if (fabs(dy.q) < MagickEpsilon)
7690 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7692 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7697 inverse_slope.q=(-1.0*MagickSafeReciprocal(slope.q));
7699 offset.x=sqrt((
double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
7700 offset.y=(double) (offset.x*inverse_slope.q);
7701 dot_product=dy.q*offset.x-dx.q*offset.y;
7702 if (dot_product > 0.0)
7704 box_p[2].x=polygon_primitive[n].point.x-offset.x;
7705 box_p[2].y=polygon_primitive[n].point.y-offset.y;
7706 box_p[3].x=polygon_primitive[i].point.x-offset.x;
7707 box_p[3].y=polygon_primitive[i].point.y-offset.y;
7708 box_q[2].x=polygon_primitive[n].point.x+offset.x;
7709 box_q[2].y=polygon_primitive[n].point.y+offset.y;
7710 box_q[3].x=polygon_primitive[i].point.x+offset.x;
7711 box_q[3].y=polygon_primitive[i].point.y+offset.y;
7715 box_p[2].x=polygon_primitive[n].point.x+offset.x;
7716 box_p[2].y=polygon_primitive[n].point.y+offset.y;
7717 box_p[3].x=polygon_primitive[i].point.x+offset.x;
7718 box_p[3].y=polygon_primitive[i].point.y+offset.y;
7719 box_q[2].x=polygon_primitive[n].point.x-offset.x;
7720 box_q[2].y=polygon_primitive[n].point.y-offset.y;
7721 box_q[3].x=polygon_primitive[i].point.x-offset.x;
7722 box_q[3].y=polygon_primitive[i].point.y-offset.y;
7724 if (fabs((
double) (slope.p-slope.q)) < MagickEpsilon)
7731 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
7732 box_p[3].y)/(slope.p-slope.q));
7733 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
7734 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
7735 box_q[3].y)/(slope.p-slope.q));
7736 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
7738 DisableMSCWarning(4127)
7739 CheckPathExtent(MaxStrokePad,MaxStrokePad);
7741 dot_product=dx.q*dy.p-dx.p*dy.q;
7742 if (dot_product <= 0.0)
7743 switch (draw_info->linejoin)
7747 stroke_q[q++]=box_q[1];
7748 stroke_q[q++]=box_q[2];
7749 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7750 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7751 if (dot_product <= miterlimit)
7752 stroke_p[p++]=box_p[4];
7755 stroke_p[p++]=box_p[1];
7756 stroke_p[p++]=box_p[2];
7762 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7763 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7764 if (dot_product <= miterlimit)
7766 stroke_q[q++]=box_q[4];
7767 stroke_p[p++]=box_p[4];
7771 stroke_q[q++]=box_q[1];
7772 stroke_q[q++]=box_q[2];
7773 stroke_p[p++]=box_p[1];
7774 stroke_p[p++]=box_p[2];
7780 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7781 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7782 if (dot_product <= miterlimit)
7783 stroke_p[p++]=box_p[4];
7786 stroke_p[p++]=box_p[1];
7787 stroke_p[p++]=box_p[2];
7789 center=polygon_primitive[n].point;
7790 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
7791 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
7792 if (theta.q < theta.p)
7793 theta.q+=2.0*MagickPI;
7794 arc_segments=(size_t) CastDoubleToSsizeT(ceil((
double) ((theta.q-
7795 theta.p)/(2.0*sqrt(MagickSafeReciprocal(mid))))));
7796 DisableMSCWarning(4127)
7797 CheckPathExtent(MaxStrokePad,arc_segments+MaxStrokePad);
7799 stroke_q[q].x=box_q[1].x;
7800 stroke_q[q].y=box_q[1].y;
7802 for (j=1; j < (ssize_t) arc_segments; j++)
7804 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7805 stroke_q[q].x=(double) (center.x+mid*cos(fmod((
double)
7806 (theta.p+delta_theta),DegreesToRadians(360.0))));
7807 stroke_q[q].y=(double) (center.y+mid*sin(fmod((
double)
7808 (theta.p+delta_theta),DegreesToRadians(360.0))));
7811 stroke_q[q++]=box_q[2];
7818 switch (draw_info->linejoin)
7822 stroke_p[p++]=box_p[1];
7823 stroke_p[p++]=box_p[2];
7824 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7825 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7826 if (dot_product <= miterlimit)
7827 stroke_q[q++]=box_q[4];
7830 stroke_q[q++]=box_q[1];
7831 stroke_q[q++]=box_q[2];
7837 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7838 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7839 if (dot_product <= miterlimit)
7841 stroke_q[q++]=box_q[4];
7842 stroke_p[p++]=box_p[4];
7846 stroke_q[q++]=box_q[1];
7847 stroke_q[q++]=box_q[2];
7848 stroke_p[p++]=box_p[1];
7849 stroke_p[p++]=box_p[2];
7855 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7856 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7857 if (dot_product <= miterlimit)
7858 stroke_q[q++]=box_q[4];
7861 stroke_q[q++]=box_q[1];
7862 stroke_q[q++]=box_q[2];
7864 center=polygon_primitive[n].point;
7865 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
7866 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
7867 if (theta.p < theta.q)
7868 theta.p+=2.0*MagickPI;
7869 arc_segments=(size_t) CastDoubleToSsizeT(ceil((
double) ((theta.p-
7870 theta.q)/(2.0*sqrt((
double) (MagickSafeReciprocal(mid)))))));
7871 DisableMSCWarning(4127)
7872 CheckPathExtent(arc_segments+MaxStrokePad,MaxStrokePad);
7874 stroke_p[p++]=box_p[1];
7875 for (j=1; j < (ssize_t) arc_segments; j++)
7877 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7878 stroke_p[p].x=(double) (center.x+mid*cos(fmod((
double)
7879 (theta.p+delta_theta),DegreesToRadians(360.0))));
7880 stroke_p[p].y=(double) (center.y+mid*sin(fmod((
double)
7881 (theta.p+delta_theta),DegreesToRadians(360.0))));
7884 stroke_p[p++]=box_p[2];
7891 inverse_slope.p=inverse_slope.q;
7900 stroke_p[p++]=box_p[1];
7901 stroke_q[q++]=box_q[1];
7905 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((
size_t)
7906 (p+q+2L),(
size_t) (closed_path+2L)*
sizeof(*stroke_polygon));
7907 if (stroke_polygon == (PrimitiveInfo *) NULL)
7909 (void) ThrowMagickException(exception,GetMagickModule(),
7910 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
"");
7911 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7912 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7913 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7915 return(stroke_polygon);
7917 for (i=0; i < (ssize_t) p; i++)
7919 stroke_polygon[i]=polygon_primitive[0];
7920 stroke_polygon[i].point=stroke_p[i];
7922 if (closed_path != MagickFalse)
7924 stroke_polygon[i]=polygon_primitive[0];
7925 stroke_polygon[i].point=stroke_polygon[0].point;
7928 for ( ; i < (ssize_t) (p+q+closed_path); i++)
7930 stroke_polygon[i]=polygon_primitive[0];
7931 stroke_polygon[i].point=stroke_q[p+q+closed_path-(i+1)];
7933 if (closed_path != MagickFalse)
7935 stroke_polygon[i]=polygon_primitive[0];
7936 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
7939 stroke_polygon[i]=polygon_primitive[0];
7940 stroke_polygon[i].point=stroke_polygon[0].point;
7942 stroke_polygon[i].primitive=UndefinedPrimitive;
7943 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
7944 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7945 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7946 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
7947 return(stroke_polygon);