MagickCore 7.1.1-43
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
draw.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD RRRR AAA W W %
7% D D R R A A W W %
8% D D RRRR AAAAA W W W %
9% D D R RN A A WW WW %
10% DDDD R R A A W W %
11% %
12% %
13% MagickCore Image Drawing Methods %
14% %
15% %
16% Software Design %
17% Cristy %
18% July 1998 %
19% %
20% %
21% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
22% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% https://imagemagick.org/license/ %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37% Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon
38% rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion",
39% Graphics Gems, 1990. Leonard Rosenthal and David Harr of Appligent
40% (www.appligent.com) contributed the dash pattern, linecap stroking
41% algorithm, and minor rendering improvements.
42%
43*/
44␌
45/*
46 Include declarations.
47*/
48#include "MagickCore/studio.h"
49#include "MagickCore/annotate.h"
50#include "MagickCore/artifact.h"
51#include "MagickCore/blob.h"
52#include "MagickCore/cache.h"
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"
90␌
91/*
92 Define declarations.
93*/
94#define AntialiasThreshold (1.0/3.0)
95#define BezierQuantum 200
96#define MaxVectorRecursionDepth 400
97#define PrimitiveExtentPad 4296.0
98#define MaxBezierCoordinates 67108864
99#define MacroExpansionLimit 262144
100#define ThrowPointExpectedException(token,exception) \
101{ \
102 (void) ThrowMagickException(exception,GetMagickModule(),DrawError, \
103 "NonconformingDrawingPrimitiveDefinition","`%s'",token); \
104 status=MagickFalse; \
105 break; \
106}
107
108
109static inline float MagickSafeReciprocal(const float x) {
110 return PerceptibleReciprocal(x);
111}
112␌
113/*
114 Typedef declarations.
115*/
116typedef struct _EdgeInfo
117{
119 bounds;
120
121 double
122 scanline;
123
125 *points;
126
127 size_t
128 number_points;
129
130 ssize_t
131 direction;
132
133 MagickBooleanType
134 ghostline;
135
136 size_t
137 highwater;
138} EdgeInfo;
139
140typedef struct _ElementInfo
141{
142 double
143 cx,
144 cy,
145 major,
146 minor,
147 angle;
149
150typedef struct _MVGInfo
151{
153 **primitive_info;
154
155 size_t
156 *extent;
157
158 ssize_t
159 offset;
160
162 point;
163
165 *exception;
166} MVGInfo;
167
168typedef struct _PolygonInfo
169{
171 *edges;
172
173 size_t
174 number_edges;
176
177typedef enum
178{
179 MoveToCode,
180 OpenCode,
181 GhostlineCode,
182 LineToCode,
183 EndCode
184} PathInfoCode;
185
186typedef struct _PathInfo
187{
189 point;
190
191 PathInfoCode
192 code;
193} PathInfo;
194␌
195/*
196 Forward declarations.
197*/
198static Image
199 *DrawClippingMask(Image *,const DrawInfo *,const char *,const char *,
200 const size_t,ExceptionInfo *);
201
202static MagickBooleanType
203 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
204 ExceptionInfo *),
205 RenderMVGContent(Image *,const DrawInfo *,const size_t,ExceptionInfo *),
206 TraceArc(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
207 TraceArcPath(MVGInfo *,const PointInfo,const PointInfo,const PointInfo,
208 const double,const MagickBooleanType,const MagickBooleanType),
209 TraceBezier(MVGInfo *,const size_t),
210 TraceCircle(MVGInfo *,const PointInfo,const PointInfo),
211 TraceEllipse(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
212 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
213 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
214 TraceRoundRectangle(MVGInfo *,const PointInfo,const PointInfo,PointInfo),
215 TraceSquareLinecap(PrimitiveInfo *,const size_t,const double);
216
217static PrimitiveInfo
218 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *,ExceptionInfo *);
219
220static ssize_t
221 TracePath(MVGInfo *,const char *,ExceptionInfo *);
222␌
223/*
224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225% %
226% %
227% %
228% A c q u i r e D r a w I n f o %
229% %
230% %
231% %
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233%
234% AcquireDrawInfo() returns a DrawInfo structure properly initialized.
235%
236% The format of the AcquireDrawInfo method is:
237%
238% DrawInfo *AcquireDrawInfo(void)
239%
240*/
241MagickExport DrawInfo *AcquireDrawInfo(void)
242{
244 *draw_info;
245
246 draw_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*draw_info));
247 GetDrawInfo((ImageInfo *) NULL,draw_info);
248 return(draw_info);
249}
250␌
251/*
252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253% %
254% %
255% %
256% C l o n e D r a w I n f o %
257% %
258% %
259% %
260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261%
262% CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
263% is specified, a new DrawInfo structure is created initialized to default
264% values.
265%
266% The format of the CloneDrawInfo method is:
267%
268% DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
269% const DrawInfo *draw_info)
270%
271% A description of each parameter follows:
272%
273% o image_info: the image info.
274%
275% o draw_info: the draw info.
276%
277*/
278MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
279 const DrawInfo *draw_info)
280{
282 *clone_info;
283
285 *exception;
286
287 clone_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*clone_info));
288 GetDrawInfo(image_info,clone_info);
289 if (draw_info == (DrawInfo *) NULL)
290 return(clone_info);
291 exception=AcquireExceptionInfo();
292 if (draw_info->id != (char *) NULL)
293 (void) CloneString(&clone_info->id,draw_info->id);
294 if (draw_info->primitive != (char *) NULL)
295 (void) CloneString(&clone_info->primitive,draw_info->primitive);
296 if (draw_info->geometry != (char *) NULL)
297 (void) CloneString(&clone_info->geometry,draw_info->geometry);
298 clone_info->compliance=draw_info->compliance;
299 clone_info->viewbox=draw_info->viewbox;
300 clone_info->affine=draw_info->affine;
301 clone_info->gravity=draw_info->gravity;
302 clone_info->fill=draw_info->fill;
303 clone_info->stroke=draw_info->stroke;
304 clone_info->stroke_width=draw_info->stroke_width;
305 if (draw_info->fill_pattern != (Image *) NULL)
306 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
307 exception);
308 if (draw_info->stroke_pattern != (Image *) NULL)
309 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
310 MagickTrue,exception);
311 clone_info->stroke_antialias=draw_info->stroke_antialias;
312 clone_info->text_antialias=draw_info->text_antialias;
313 clone_info->fill_rule=draw_info->fill_rule;
314 clone_info->linecap=draw_info->linecap;
315 clone_info->linejoin=draw_info->linejoin;
316 clone_info->miterlimit=draw_info->miterlimit;
317 clone_info->dash_offset=draw_info->dash_offset;
318 clone_info->decorate=draw_info->decorate;
319 clone_info->compose=draw_info->compose;
320 if (draw_info->text != (char *) NULL)
321 (void) CloneString(&clone_info->text,draw_info->text);
322 if (draw_info->font != (char *) NULL)
323 (void) CloneString(&clone_info->font,draw_info->font);
324 if (draw_info->metrics != (char *) NULL)
325 (void) CloneString(&clone_info->metrics,draw_info->metrics);
326 if (draw_info->family != (char *) NULL)
327 (void) CloneString(&clone_info->family,draw_info->family);
328 clone_info->style=draw_info->style;
329 clone_info->stretch=draw_info->stretch;
330 clone_info->weight=draw_info->weight;
331 if (draw_info->encoding != (char *) NULL)
332 (void) CloneString(&clone_info->encoding,draw_info->encoding);
333 clone_info->pointsize=draw_info->pointsize;
334 clone_info->kerning=draw_info->kerning;
335 clone_info->interline_spacing=draw_info->interline_spacing;
336 clone_info->interword_spacing=draw_info->interword_spacing;
337 clone_info->direction=draw_info->direction;
338 clone_info->word_break=draw_info->word_break;
339 if (draw_info->density != (char *) NULL)
340 (void) CloneString(&clone_info->density,draw_info->density);
341 clone_info->align=draw_info->align;
342 clone_info->undercolor=draw_info->undercolor;
343 clone_info->border_color=draw_info->border_color;
344 if (draw_info->server_name != (char *) NULL)
345 (void) CloneString(&clone_info->server_name,draw_info->server_name);
346 if (draw_info->dash_pattern != (double *) NULL)
347 {
348 ssize_t
349 x;
350
351 for (x=0; fabs(draw_info->dash_pattern[x]) >= MagickEpsilon; x++) ;
352 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) (x+1),
353 sizeof(*clone_info->dash_pattern));
354 if (clone_info->dash_pattern == (double *) NULL)
355 ThrowFatalException(ResourceLimitFatalError,
356 "UnableToAllocateDashPattern");
357 (void) memset(clone_info->dash_pattern,0,(size_t) (x+1)*
358 sizeof(*clone_info->dash_pattern));
359 (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(size_t)
360 x*sizeof(*clone_info->dash_pattern));
361 }
362 clone_info->gradient=draw_info->gradient;
363 if (draw_info->gradient.stops != (StopInfo *) NULL)
364 {
365 size_t
366 number_stops;
367
368 number_stops=clone_info->gradient.number_stops;
369 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
370 number_stops,sizeof(*clone_info->gradient.stops));
371 if (clone_info->gradient.stops == (StopInfo *) NULL)
372 ThrowFatalException(ResourceLimitFatalError,
373 "UnableToAllocateDashPattern");
374 (void) memcpy(clone_info->gradient.stops,draw_info->gradient.stops,
375 (size_t) number_stops*sizeof(*clone_info->gradient.stops));
376 }
377 clone_info->bounds=draw_info->bounds;
378 clone_info->fill_alpha=draw_info->fill_alpha;
379 clone_info->stroke_alpha=draw_info->stroke_alpha;
380 clone_info->element_reference=draw_info->element_reference;
381 clone_info->clip_path=draw_info->clip_path;
382 clone_info->clip_units=draw_info->clip_units;
383 if (draw_info->clip_mask != (char *) NULL)
384 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
385 if (draw_info->clipping_mask != (Image *) NULL)
386 clone_info->clipping_mask=CloneImage(draw_info->clipping_mask,0,0,
387 MagickTrue,exception);
388 if (draw_info->composite_mask != (Image *) NULL)
389 clone_info->composite_mask=CloneImage(draw_info->composite_mask,0,0,
390 MagickTrue,exception);
391 clone_info->render=draw_info->render;
392 clone_info->debug=draw_info->debug;
393 clone_info->macro_expansion=draw_info->macro_expansion;
394 exception=DestroyExceptionInfo(exception);
395 return(clone_info);
396}
397␌
398/*
399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400% %
401% %
402% %
403+ C o n v e r t P a t h T o P o l y g o n %
404% %
405% %
406% %
407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408%
409% ConvertPathToPolygon() converts a path to the more efficient sorted
410% rendering form.
411%
412% The format of the ConvertPathToPolygon method is:
413%
414% PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info,
415% ExceptionInfo *exception)
416%
417% A description of each parameter follows:
418%
419% o ConvertPathToPolygon() returns the path in a more efficient sorted
420% rendering form of type PolygonInfo.
421%
422% o draw_info: Specifies a pointer to an DrawInfo structure.
423%
424% o path_info: Specifies a pointer to an PathInfo structure.
425%
426%
427*/
428
429static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
430{
431 ssize_t
432 i;
433
434 if (polygon_info->edges != (EdgeInfo *) NULL)
435 {
436 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
437 if (polygon_info->edges[i].points != (PointInfo *) NULL)
438 polygon_info->edges[i].points=(PointInfo *)
439 RelinquishMagickMemory(polygon_info->edges[i].points);
440 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(
441 polygon_info->edges);
442 }
443 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
444}
445#if defined(__cplusplus) || defined(c_plusplus)
446extern "C" {
447#endif
448
449static int DrawCompareEdges(const void *p_edge,const void *q_edge)
450{
451#define DrawCompareEdge(p,q) \
452{ \
453 if (((p)-(q)) < 0.0) \
454 return(-1); \
455 if (((p)-(q)) > 0.0) \
456 return(1); \
457}
458
459 const PointInfo
460 *p,
461 *q;
462
463 /*
464 Edge sorting for right-handed coordinate system.
465 */
466 p=((const EdgeInfo *) p_edge)->points;
467 q=((const EdgeInfo *) q_edge)->points;
468 DrawCompareEdge(p[0].y,q[0].y);
469 DrawCompareEdge(p[0].x,q[0].x);
470 DrawCompareEdge((p[1].x-p[0].x)*(q[1].y-q[0].y),(p[1].y-p[0].y)*
471 (q[1].x-q[0].x));
472 DrawCompareEdge(p[1].y,q[1].y);
473 DrawCompareEdge(p[1].x,q[1].x);
474 return(0);
475}
476
477#if defined(__cplusplus) || defined(c_plusplus)
478}
479#endif
480
481static void LogPolygonInfo(const PolygonInfo *polygon_info)
482{
484 *p;
485
486 ssize_t
487 i,
488 j;
489
490 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
491 p=polygon_info->edges;
492 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
493 {
494 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
495 (double) i);
496 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
497 p->direction != MagickFalse ? "down" : "up");
498 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
499 p->ghostline != MagickFalse ? "transparent" : "opaque");
500 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
501 " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
502 p->bounds.x2,p->bounds.y2);
503 for (j=0; j < (ssize_t) p->number_points; j++)
504 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g",
505 p->points[j].x,p->points[j].y);
506 p++;
507 }
508 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
509}
510
511static void ReversePoints(PointInfo *points,const size_t number_points)
512{
514 point;
515
516 size_t
517 i;
518
519 for (i=0; i < (number_points >> 1); i++)
520 {
521 point=points[i];
522 points[i]=points[number_points-(i+1)];
523 points[number_points-(i+1)]=point;
524 }
525}
526
527static PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info,
528 ExceptionInfo *exception)
529{
530 long
531 direction,
532 next_direction;
533
535 point,
536 *points;
537
539 *polygon_info;
540
542 bounds;
543
544 ssize_t
545 i,
546 n;
547
548 MagickBooleanType
549 ghostline;
550
551 size_t
552 edge,
553 number_edges,
554 number_points;
555
556 /*
557 Convert a path to the more efficient sorted rendering form.
558 */
559 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
560 if (polygon_info == (PolygonInfo *) NULL)
561 {
562 (void) ThrowMagickException(exception,GetMagickModule(),
563 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
564 return((PolygonInfo *) NULL);
565 }
566 number_edges=16;
567 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges,
568 sizeof(*polygon_info->edges));
569 if (polygon_info->edges == (EdgeInfo *) NULL)
570 {
571 (void) ThrowMagickException(exception,GetMagickModule(),
572 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
573 return(DestroyPolygonInfo(polygon_info));
574 }
575 (void) memset(polygon_info->edges,0,number_edges*
576 sizeof(*polygon_info->edges));
577 direction=0;
578 edge=0;
579 ghostline=MagickFalse;
580 n=0;
581 number_points=0;
582 points=(PointInfo *) NULL;
583 (void) memset(&point,0,sizeof(point));
584 (void) memset(&bounds,0,sizeof(bounds));
585 polygon_info->edges[edge].number_points=(size_t) n;
586 polygon_info->edges[edge].scanline=0.0;
587 polygon_info->edges[edge].highwater=0;
588 polygon_info->edges[edge].ghostline=ghostline;
589 polygon_info->edges[edge].direction=(ssize_t) direction;
590 polygon_info->edges[edge].points=points;
591 polygon_info->edges[edge].bounds=bounds;
592 polygon_info->number_edges=0;
593 for (i=0; path_info[i].code != EndCode; i++)
594 {
595 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
596 (path_info[i].code == GhostlineCode))
597 {
598 /*
599 Move to.
600 */
601 if ((points != (PointInfo *) NULL) && (n >= 2))
602 {
603 if (edge == number_edges)
604 {
605 number_edges<<=1;
606 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
607 polygon_info->edges,(size_t) number_edges,
608 sizeof(*polygon_info->edges));
609 if (polygon_info->edges == (EdgeInfo *) NULL)
610 {
611 (void) ThrowMagickException(exception,GetMagickModule(),
612 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
613 points=(PointInfo *) RelinquishMagickMemory(points);
614 return(DestroyPolygonInfo(polygon_info));
615 }
616 }
617 polygon_info->edges[edge].number_points=(size_t) n;
618 polygon_info->edges[edge].scanline=(-1.0);
619 polygon_info->edges[edge].highwater=0;
620 polygon_info->edges[edge].ghostline=ghostline;
621 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
622 if (direction < 0)
623 ReversePoints(points,(size_t) n);
624 polygon_info->edges[edge].points=points;
625 polygon_info->edges[edge].bounds=bounds;
626 polygon_info->edges[edge].bounds.y1=points[0].y;
627 polygon_info->edges[edge].bounds.y2=points[n-1].y;
628 points=(PointInfo *) NULL;
629 ghostline=MagickFalse;
630 edge++;
631 polygon_info->number_edges=edge;
632 }
633 if (points == (PointInfo *) NULL)
634 {
635 number_points=16;
636 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
637 sizeof(*points));
638 if (points == (PointInfo *) NULL)
639 {
640 (void) ThrowMagickException(exception,GetMagickModule(),
641 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
642 return(DestroyPolygonInfo(polygon_info));
643 }
644 }
645 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
646 point=path_info[i].point;
647 points[0]=point;
648 bounds.x1=point.x;
649 bounds.x2=point.x;
650 direction=0;
651 n=1;
652 continue;
653 }
654 /*
655 Line to.
656 */
657 next_direction=((path_info[i].point.y > point.y) ||
658 ((fabs(path_info[i].point.y-point.y) < MagickEpsilon) &&
659 (path_info[i].point.x > point.x))) ? 1 : -1;
660 if ((points != (PointInfo *) NULL) && (direction != 0) &&
661 (direction != next_direction))
662 {
663 /*
664 New edge.
665 */
666 point=points[n-1];
667 if (edge == number_edges)
668 {
669 number_edges<<=1;
670 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
671 polygon_info->edges,(size_t) number_edges,
672 sizeof(*polygon_info->edges));
673 if (polygon_info->edges == (EdgeInfo *) NULL)
674 {
675 (void) ThrowMagickException(exception,GetMagickModule(),
676 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
677 points=(PointInfo *) RelinquishMagickMemory(points);
678 return(DestroyPolygonInfo(polygon_info));
679 }
680 }
681 polygon_info->edges[edge].number_points=(size_t) n;
682 polygon_info->edges[edge].scanline=(-1.0);
683 polygon_info->edges[edge].highwater=0;
684 polygon_info->edges[edge].ghostline=ghostline;
685 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
686 if (direction < 0)
687 ReversePoints(points,(size_t) n);
688 polygon_info->edges[edge].points=points;
689 polygon_info->edges[edge].bounds=bounds;
690 polygon_info->edges[edge].bounds.y1=points[0].y;
691 polygon_info->edges[edge].bounds.y2=points[n-1].y;
692 polygon_info->number_edges=edge+1;
693 points=(PointInfo *) NULL;
694 number_points=16;
695 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
696 sizeof(*points));
697 if (points == (PointInfo *) NULL)
698 {
699 (void) ThrowMagickException(exception,GetMagickModule(),
700 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
701 return(DestroyPolygonInfo(polygon_info));
702 }
703 n=1;
704 ghostline=MagickFalse;
705 points[0]=point;
706 bounds.x1=point.x;
707 bounds.x2=point.x;
708 edge++;
709 }
710 direction=next_direction;
711 if (points == (PointInfo *) NULL)
712 continue;
713 if (n == (ssize_t) number_points)
714 {
715 number_points<<=1;
716 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
717 sizeof(*points));
718 if (points == (PointInfo *) NULL)
719 {
720 (void) ThrowMagickException(exception,GetMagickModule(),
721 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
722 return(DestroyPolygonInfo(polygon_info));
723 }
724 }
725 point=path_info[i].point;
726 points[n]=point;
727 if (point.x < bounds.x1)
728 bounds.x1=point.x;
729 if (point.x > bounds.x2)
730 bounds.x2=point.x;
731 n++;
732 }
733 if (points != (PointInfo *) NULL)
734 {
735 if (n < 2)
736 points=(PointInfo *) RelinquishMagickMemory(points);
737 else
738 {
739 if (edge == number_edges)
740 {
741 number_edges<<=1;
742 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
743 polygon_info->edges,(size_t) number_edges,
744 sizeof(*polygon_info->edges));
745 if (polygon_info->edges == (EdgeInfo *) NULL)
746 {
747 (void) ThrowMagickException(exception,GetMagickModule(),
748 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
749 return(DestroyPolygonInfo(polygon_info));
750 }
751 }
752 polygon_info->edges[edge].number_points=(size_t) n;
753 polygon_info->edges[edge].scanline=(-1.0);
754 polygon_info->edges[edge].highwater=0;
755 polygon_info->edges[edge].ghostline=ghostline;
756 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
757 if (direction < 0)
758 ReversePoints(points,(size_t) n);
759 polygon_info->edges[edge].points=points;
760 polygon_info->edges[edge].bounds=bounds;
761 polygon_info->edges[edge].bounds.y1=points[0].y;
762 polygon_info->edges[edge].bounds.y2=points[n-1].y;
763 points=(PointInfo *) NULL;
764 ghostline=MagickFalse;
765 edge++;
766 polygon_info->number_edges=edge;
767 }
768 }
769 polygon_info->number_edges=edge;
770 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(polygon_info->edges,
771 polygon_info->number_edges,sizeof(*polygon_info->edges));
772 if (polygon_info->edges == (EdgeInfo *) NULL)
773 {
774 (void) ThrowMagickException(exception,GetMagickModule(),
775 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
776 return(DestroyPolygonInfo(polygon_info));
777 }
778 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
779 {
781 *edge_info;
782
783 edge_info=polygon_info->edges+i;
784 edge_info->points=(PointInfo *) ResizeQuantumMemory(edge_info->points,
785 edge_info->number_points,sizeof(*edge_info->points));
786 if (edge_info->points == (PointInfo *) NULL)
787 {
788 (void) ThrowMagickException(exception,GetMagickModule(),
789 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
790 return(DestroyPolygonInfo(polygon_info));
791 }
792 }
793 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
794 sizeof(*polygon_info->edges),DrawCompareEdges);
795 if ((GetLogEventMask() & DrawEvent) != 0)
796 LogPolygonInfo(polygon_info);
797 return(polygon_info);
798}
799␌
800/*
801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802% %
803% %
804% %
805+ C o n v e r t P r i m i t i v e T o P a t h %
806% %
807% %
808% %
809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810%
811% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
812% path structure.
813%
814% The format of the ConvertPrimitiveToPath method is:
815%
816% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
817% const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
818%
819% A description of each parameter follows:
820%
821% o ConvertPrimitiveToPath() returns a vector path structure of type
822% PathInfo.
823%
824% o draw_info: a structure of type DrawInfo.
825%
826% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
827%
828*/
829
830static void LogPathInfo(const PathInfo *path_info)
831{
832 const PathInfo
833 *p;
834
835 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
836 for (p=path_info; p->code != EndCode; p++)
837 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
838 " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
839 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
840 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
841 "?");
842 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
843}
844
845static PathInfo *ConvertPrimitiveToPath(const PrimitiveInfo *primitive_info,
846 ExceptionInfo *exception)
847{
848 MagickBooleanType
849 closed_subpath;
850
852 *path_info;
853
854 PathInfoCode
855 code;
856
858 p,
859 q;
860
861 ssize_t
862 n,
863 start;
864
865 size_t
866 coordinates,
867 i;
868
869 /*
870 Converts a PrimitiveInfo structure into a vector path structure.
871 */
872 switch (primitive_info->primitive)
873 {
874 case AlphaPrimitive:
875 case ColorPrimitive:
876 case ImagePrimitive:
877 case PointPrimitive:
878 case TextPrimitive:
879 return((PathInfo *) NULL);
880 default:
881 break;
882 }
883 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
884 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (3UL*i+1UL),
885 sizeof(*path_info));
886 if (path_info == (PathInfo *) NULL)
887 {
888 (void) ThrowMagickException(exception,GetMagickModule(),
889 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
890 return((PathInfo *) NULL);
891 }
892 coordinates=0;
893 closed_subpath=MagickFalse;
894 n=0;
895 p.x=(-1.0);
896 p.y=(-1.0);
897 q.x=(-1.0);
898 q.y=(-1.0);
899 start=0;
900 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
901 {
902 code=LineToCode;
903 if (coordinates <= 0)
904 {
905 /*
906 New subpath.
907 */
908 coordinates=primitive_info[i].coordinates;
909 p=primitive_info[i].point;
910 start=n;
911 code=MoveToCode;
912 closed_subpath=primitive_info[i].closed_subpath;
913 }
914 coordinates--;
915 if ((code == MoveToCode) || (coordinates <= 0) ||
916 (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
917 (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
918 {
919 /*
920 Eliminate duplicate points.
921 */
922 path_info[n].code=code;
923 path_info[n].point=primitive_info[i].point;
924 q=primitive_info[i].point;
925 n++;
926 }
927 if (coordinates > 0)
928 continue; /* next point in current subpath */
929 if (closed_subpath != MagickFalse)
930 {
931 closed_subpath=MagickFalse;
932 continue;
933 }
934 /*
935 Mark the p point as open if the subpath is not closed.
936 */
937 path_info[start].code=OpenCode;
938 path_info[n].code=GhostlineCode;
939 path_info[n].point=primitive_info[i].point;
940 n++;
941 path_info[n].code=LineToCode;
942 path_info[n].point=p;
943 n++;
944 }
945 path_info[n].code=EndCode;
946 path_info[n].point.x=0.0;
947 path_info[n].point.y=0.0;
948 if (IsEventLogging() != MagickFalse)
949 LogPathInfo(path_info);
950 path_info=(PathInfo *) ResizeQuantumMemory(path_info,(size_t) (n+1),
951 sizeof(*path_info));
952 return(path_info);
953}
954␌
955/*
956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957% %
958% %
959% %
960% D e s t r o y D r a w I n f o %
961% %
962% %
963% %
964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
965%
966% DestroyDrawInfo() deallocates memory associated with an DrawInfo structure.
967%
968% The format of the DestroyDrawInfo method is:
969%
970% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
971%
972% A description of each parameter follows:
973%
974% o draw_info: the draw info.
975%
976*/
977MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
978{
979 assert(draw_info != (DrawInfo *) NULL);
980 assert(draw_info->signature == MagickCoreSignature);
981 if (IsEventLogging() != MagickFalse)
982 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
983 if (draw_info->id != (char *) NULL)
984 draw_info->id=DestroyString(draw_info->id);
985 if (draw_info->primitive != (char *) NULL)
986 draw_info->primitive=DestroyString(draw_info->primitive);
987 if (draw_info->text != (char *) NULL)
988 draw_info->text=DestroyString(draw_info->text);
989 if (draw_info->geometry != (char *) NULL)
990 draw_info->geometry=DestroyString(draw_info->geometry);
991 if (draw_info->fill_pattern != (Image *) NULL)
992 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
993 if (draw_info->stroke_pattern != (Image *) NULL)
994 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
995 if (draw_info->font != (char *) NULL)
996 draw_info->font=DestroyString(draw_info->font);
997 if (draw_info->metrics != (char *) NULL)
998 draw_info->metrics=DestroyString(draw_info->metrics);
999 if (draw_info->family != (char *) NULL)
1000 draw_info->family=DestroyString(draw_info->family);
1001 if (draw_info->encoding != (char *) NULL)
1002 draw_info->encoding=DestroyString(draw_info->encoding);
1003 if (draw_info->density != (char *) NULL)
1004 draw_info->density=DestroyString(draw_info->density);
1005 if (draw_info->server_name != (char *) NULL)
1006 draw_info->server_name=(char *)
1007 RelinquishMagickMemory(draw_info->server_name);
1008 if (draw_info->dash_pattern != (double *) NULL)
1009 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
1010 draw_info->dash_pattern);
1011 if (draw_info->gradient.stops != (StopInfo *) NULL)
1012 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
1013 draw_info->gradient.stops);
1014 if (draw_info->clip_mask != (char *) NULL)
1015 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
1016 if (draw_info->clipping_mask != (Image *) NULL)
1017 draw_info->clipping_mask=DestroyImage(draw_info->clipping_mask);
1018 if (draw_info->composite_mask != (Image *) NULL)
1019 draw_info->composite_mask=DestroyImage(draw_info->composite_mask);
1020 if (draw_info->image_info != (ImageInfo *) NULL)
1021 draw_info->image_info=DestroyImageInfo(draw_info->image_info);
1022 draw_info->signature=(~MagickCoreSignature);
1023 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
1024 return(draw_info);
1025}
1026␌
1027/*
1028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1029% %
1030% %
1031% %
1032% D r a w A f f i n e I m a g e %
1033% %
1034% %
1035% %
1036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037%
1038% DrawAffineImage() composites the source over the destination image as
1039% dictated by the affine transform.
1040%
1041% The format of the DrawAffineImage method is:
1042%
1043% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
1044% const AffineMatrix *affine,ExceptionInfo *exception)
1045%
1046% A description of each parameter follows:
1047%
1048% o image: the image.
1049%
1050% o source: the source image.
1051%
1052% o affine: the affine transform.
1053%
1054% o exception: return any errors or warnings in this structure.
1055%
1056*/
1057
1058static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
1059 const double y,const SegmentInfo *edge)
1060{
1061 double
1062 intercept,
1063 z;
1064
1065 double
1066 x;
1067
1069 inverse_edge;
1070
1071 /*
1072 Determine left and right edges.
1073 */
1074 inverse_edge.x1=edge->x1;
1075 inverse_edge.y1=edge->y1;
1076 inverse_edge.x2=edge->x2;
1077 inverse_edge.y2=edge->y2;
1078 z=affine->ry*y+affine->tx;
1079 if (affine->sx >= MagickEpsilon)
1080 {
1081 intercept=(-z/affine->sx);
1082 x=intercept;
1083 if (x > inverse_edge.x1)
1084 inverse_edge.x1=x;
1085 intercept=(-z+(double) image->columns)/affine->sx;
1086 x=intercept;
1087 if (x < inverse_edge.x2)
1088 inverse_edge.x2=x;
1089 }
1090 else
1091 if (affine->sx < -MagickEpsilon)
1092 {
1093 intercept=(-z+(double) image->columns)/affine->sx;
1094 x=intercept;
1095 if (x > inverse_edge.x1)
1096 inverse_edge.x1=x;
1097 intercept=(-z/affine->sx);
1098 x=intercept;
1099 if (x < inverse_edge.x2)
1100 inverse_edge.x2=x;
1101 }
1102 else
1103 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
1104 {
1105 inverse_edge.x2=edge->x1;
1106 return(inverse_edge);
1107 }
1108 /*
1109 Determine top and bottom edges.
1110 */
1111 z=affine->sy*y+affine->ty;
1112 if (affine->rx >= MagickEpsilon)
1113 {
1114 intercept=(-z/affine->rx);
1115 x=intercept;
1116 if (x > inverse_edge.x1)
1117 inverse_edge.x1=x;
1118 intercept=(-z+(double) image->rows)/affine->rx;
1119 x=intercept;
1120 if (x < inverse_edge.x2)
1121 inverse_edge.x2=x;
1122 }
1123 else
1124 if (affine->rx < -MagickEpsilon)
1125 {
1126 intercept=(-z+(double) image->rows)/affine->rx;
1127 x=intercept;
1128 if (x > inverse_edge.x1)
1129 inverse_edge.x1=x;
1130 intercept=(-z/affine->rx);
1131 x=intercept;
1132 if (x < inverse_edge.x2)
1133 inverse_edge.x2=x;
1134 }
1135 else
1136 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
1137 {
1138 inverse_edge.x2=edge->x2;
1139 return(inverse_edge);
1140 }
1141 return(inverse_edge);
1142}
1143
1144static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1145{
1147 inverse_affine;
1148
1149 double
1150 determinant;
1151
1152 determinant=MagickSafeReciprocal(affine->sx*affine->sy-affine->rx*
1153 affine->ry);
1154 inverse_affine.sx=determinant*affine->sy;
1155 inverse_affine.rx=determinant*(-affine->rx);
1156 inverse_affine.ry=determinant*(-affine->ry);
1157 inverse_affine.sy=determinant*affine->sx;
1158 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1159 inverse_affine.ry;
1160 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1161 inverse_affine.sy;
1162 return(inverse_affine);
1163}
1164
1165MagickExport MagickBooleanType DrawAffineImage(Image *image,
1166 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
1167{
1169 inverse_affine;
1170
1171 CacheView
1172 *image_view,
1173 *source_view;
1174
1175 MagickBooleanType
1176 status;
1177
1178 PixelInfo
1179 zero;
1180
1181 PointInfo
1182 extent[4],
1183 min,
1184 max;
1185
1186 ssize_t
1187 i;
1188
1190 edge;
1191
1192 ssize_t
1193 start,
1194 stop,
1195 y;
1196
1197 /*
1198 Determine bounding box.
1199 */
1200 assert(image != (Image *) NULL);
1201 assert(image->signature == MagickCoreSignature);
1202 if (IsEventLogging() != MagickFalse)
1203 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1204 assert(source != (const Image *) NULL);
1205 assert(source->signature == MagickCoreSignature);
1206 assert(affine != (AffineMatrix *) NULL);
1207 extent[0].x=0.0;
1208 extent[0].y=0.0;
1209 extent[1].x=(double) source->columns;
1210 extent[1].y=0.0;
1211 extent[2].x=(double) source->columns;
1212 extent[2].y=(double) source->rows;
1213 extent[3].x=0.0;
1214 extent[3].y=(double) source->rows;
1215 for (i=0; i < 4; i++)
1216 {
1217 PointInfo
1218 point;
1219
1220 point=extent[i];
1221 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1222 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1223 }
1224 min=extent[0];
1225 max=extent[0];
1226 for (i=1; i < 4; i++)
1227 {
1228 if (min.x > extent[i].x)
1229 min.x=extent[i].x;
1230 if (min.y > extent[i].y)
1231 min.y=extent[i].y;
1232 if (max.x < extent[i].x)
1233 max.x=extent[i].x;
1234 if (max.y < extent[i].y)
1235 max.y=extent[i].y;
1236 }
1237 /*
1238 Affine transform image.
1239 */
1240 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1241 return(MagickFalse);
1242 status=MagickTrue;
1243 edge.x1=min.x;
1244 edge.y1=min.y;
1245 edge.x2=max.x;
1246 edge.y2=max.y;
1247 inverse_affine=InverseAffineMatrix(affine);
1248 if (edge.y1 < 0.0)
1249 edge.y1=0.0;
1250 if (edge.y2 > ((double) image->rows-1.0))
1251 edge.y2=(double) image->rows-1.0;
1252 GetPixelInfo(image,&zero);
1253 start=CastDoubleToSsizeT(ceil(edge.y1-0.5));
1254 stop=CastDoubleToSsizeT(floor(edge.y2+0.5));
1255 source_view=AcquireVirtualCacheView(source,exception);
1256 image_view=AcquireAuthenticCacheView(image,exception);
1257#if defined(MAGICKCORE_OPENMP_SUPPORT)
1258 #pragma omp parallel for schedule(static) shared(status) \
1259 magick_number_threads(source,image,(size_t) (stop-start),2)
1260#endif
1261 for (y=start; y <= stop; y++)
1262 {
1263 PixelInfo
1264 composite,
1265 pixel;
1266
1267 PointInfo
1268 point;
1269
1270 Quantum
1271 *magick_restrict q;
1272
1274 inverse_edge;
1275
1276 ssize_t
1277 x;
1278
1279 if (status == MagickFalse)
1280 continue;
1281 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1282 if (inverse_edge.x2 < inverse_edge.x1)
1283 continue;
1284 if (inverse_edge.x1 < 0.0)
1285 inverse_edge.x1=0.0;
1286 if (inverse_edge.x2 > ((double) image->columns-1.0))
1287 inverse_edge.x2=(double) image->columns-1.0;
1288 q=GetCacheViewAuthenticPixels(image_view,CastDoubleToSsizeT(
1289 ceil(inverse_edge.x1-0.5)),y,(size_t) CastDoubleToSsizeT(floor(
1290 inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),1,exception);
1291 if (q == (Quantum *) NULL)
1292 continue;
1293 pixel=zero;
1294 composite=zero;
1295 for (x=CastDoubleToSsizeT(ceil(inverse_edge.x1-0.5));
1296 x <= CastDoubleToSsizeT(floor(inverse_edge.x2+0.5)); x++)
1297 {
1298 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1299 inverse_affine.tx;
1300 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1301 inverse_affine.ty;
1302 status=InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1303 point.x,point.y,&pixel,exception);
1304 if (status == MagickFalse)
1305 break;
1306 GetPixelInfoPixel(image,q,&composite);
1307 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1308 &composite);
1309 SetPixelViaPixelInfo(image,&composite,q);
1310 q+=(ptrdiff_t) GetPixelChannels(image);
1311 }
1312 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1313 status=MagickFalse;
1314 }
1315 source_view=DestroyCacheView(source_view);
1316 image_view=DestroyCacheView(image_view);
1317 return(status);
1318}
1319␌
1320/*
1321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322% %
1323% %
1324% %
1325+ D r a w B o u n d i n g R e c t a n g l e s %
1326% %
1327% %
1328% %
1329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330%
1331% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1332% is only useful for developers debugging the rendering algorithm.
1333%
1334% The format of the DrawBoundingRectangles method is:
1335%
1336% MagickBooleanType DrawBoundingRectangles(Image *image,
1337% const DrawInfo *draw_info,PolygonInfo *polygon_info,
1338% ExceptionInfo *exception)
1339%
1340% A description of each parameter follows:
1341%
1342% o image: the image.
1343%
1344% o draw_info: the draw info.
1345%
1346% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1347%
1348% o exception: return any errors or warnings in this structure.
1349%
1350*/
1351
1352static MagickBooleanType DrawBoundingRectangles(Image *image,
1353 const DrawInfo *draw_info,const PolygonInfo *polygon_info,
1354 ExceptionInfo *exception)
1355{
1356 double
1357 mid;
1358
1359 DrawInfo
1360 *clone_info;
1361
1362 MagickStatusType
1363 status;
1364
1365 PointInfo
1366 end,
1367 resolution,
1368 start;
1369
1371 primitive_info[6];
1372
1373 ssize_t
1374 i;
1375
1377 bounds;
1378
1379 ssize_t
1380 coordinates;
1381
1382 (void) memset(primitive_info,0,sizeof(primitive_info));
1383 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1384 status=QueryColorCompliance("#000F",AllCompliance,&clone_info->fill,
1385 exception);
1386 if (status == MagickFalse)
1387 {
1388 clone_info=DestroyDrawInfo(clone_info);
1389 return(MagickFalse);
1390 }
1391 resolution.x=96.0;
1392 resolution.y=96.0;
1393 if (clone_info->density != (char *) NULL)
1394 {
1396 geometry_info;
1397
1398 MagickStatusType
1399 flags;
1400
1401 flags=ParseGeometry(clone_info->density,&geometry_info);
1402 if ((flags & RhoValue) != 0)
1403 resolution.x=geometry_info.rho;
1404 resolution.y=resolution.x;
1405 if ((flags & SigmaValue) != 0)
1406 resolution.y=geometry_info.sigma;
1407 }
1408 mid=(resolution.x/96.0)*ExpandAffine(&clone_info->affine)*
1409 clone_info->stroke_width/2.0;
1410 bounds.x1=0.0;
1411 bounds.y1=0.0;
1412 bounds.x2=0.0;
1413 bounds.y2=0.0;
1414 if (polygon_info != (PolygonInfo *) NULL)
1415 {
1416 bounds=polygon_info->edges[0].bounds;
1417 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
1418 {
1419 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1420 bounds.x1=polygon_info->edges[i].bounds.x1;
1421 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1422 bounds.y1=polygon_info->edges[i].bounds.y1;
1423 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1424 bounds.x2=polygon_info->edges[i].bounds.x2;
1425 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1426 bounds.y2=polygon_info->edges[i].bounds.y2;
1427 }
1428 bounds.x1-=mid;
1429 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1430 image->columns ? (double) image->columns-1 : bounds.x1;
1431 bounds.y1-=mid;
1432 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1433 image->rows ? (double) image->rows-1 : bounds.y1;
1434 bounds.x2+=mid;
1435 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1436 image->columns ? (double) image->columns-1 : bounds.x2;
1437 bounds.y2+=mid;
1438 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1439 image->rows ? (double) image->rows-1 : bounds.y2;
1440 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
1441 {
1442 if (polygon_info->edges[i].direction != 0)
1443 status=QueryColorCompliance("#f00",AllCompliance,&clone_info->stroke,
1444 exception);
1445 else
1446 status=QueryColorCompliance("#0f0",AllCompliance,&clone_info->stroke,
1447 exception);
1448 if (status == MagickFalse)
1449 break;
1450 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1451 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1452 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1453 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1454 primitive_info[0].primitive=RectanglePrimitive;
1455 status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1456 primitive_info[0].method=ReplaceMethod;
1457 coordinates=(ssize_t) primitive_info[0].coordinates;
1458 primitive_info[coordinates].primitive=UndefinedPrimitive;
1459 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1460 if (status == MagickFalse)
1461 break;
1462 }
1463 if (i < (ssize_t) polygon_info->number_edges)
1464 {
1465 clone_info=DestroyDrawInfo(clone_info);
1466 return(status == 0 ? MagickFalse : MagickTrue);
1467 }
1468 }
1469 status=QueryColorCompliance("#00f",AllCompliance,&clone_info->stroke,
1470 exception);
1471 if (status == MagickFalse)
1472 {
1473 clone_info=DestroyDrawInfo(clone_info);
1474 return(MagickFalse);
1475 }
1476 start.x=(double) (bounds.x1-mid);
1477 start.y=(double) (bounds.y1-mid);
1478 end.x=(double) (bounds.x2+mid);
1479 end.y=(double) (bounds.y2+mid);
1480 primitive_info[0].primitive=RectanglePrimitive;
1481 status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1482 primitive_info[0].method=ReplaceMethod;
1483 coordinates=(ssize_t) primitive_info[0].coordinates;
1484 primitive_info[coordinates].primitive=UndefinedPrimitive;
1485 status=DrawPrimitive(image,clone_info,primitive_info,exception);
1486 clone_info=DestroyDrawInfo(clone_info);
1487 return(status == 0 ? MagickFalse : MagickTrue);
1488}
1489␌
1490/*
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492% %
1493% %
1494% %
1495% D r a w C l i p P a t h %
1496% %
1497% %
1498% %
1499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500%
1501% DrawClipPath() draws the clip path on the image mask.
1502%
1503% The format of the DrawClipPath method is:
1504%
1505% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
1506% const char *id,ExceptionInfo *exception)
1507%
1508% A description of each parameter follows:
1509%
1510% o image: the image.
1511%
1512% o draw_info: the draw info.
1513%
1514% o id: the clip path id.
1515%
1516% o exception: return any errors or warnings in this structure.
1517%
1518*/
1519
1520static MagickBooleanType DrawClipPath_(Image *image,
1521 const DrawInfo *draw_info,const char *id,const size_t depth,
1522 ExceptionInfo *exception)
1523{
1524 const char
1525 *clip_path;
1526
1527 Image
1528 *clipping_mask;
1529
1530 MagickBooleanType
1531 status;
1532
1533 clip_path=GetImageArtifact(image,id);
1534 if (clip_path == (const char *) NULL)
1535 return(MagickFalse);
1536 clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
1537 depth+1,exception);
1538 if (clipping_mask == (Image *) NULL)
1539 return(MagickFalse);
1540 status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
1541 clipping_mask=DestroyImage(clipping_mask);
1542 return(status);
1543}
1544
1545MagickExport MagickBooleanType DrawClipPath(Image *image,
1546 const DrawInfo *draw_info,const char *id,ExceptionInfo *exception)
1547{
1548 return(DrawClipPath_(image,draw_info,id,0,exception));
1549}
1550␌
1551/*
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553% %
1554% %
1555% %
1556% D r a w C l i p p i n g M a s k %
1557% %
1558% %
1559% %
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561%
1562% DrawClippingMask() draws the clip path and returns it as an image clipping
1563% mask.
1564%
1565% The format of the DrawClippingMask method is:
1566%
1567% Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1568% const char *id,const char *clip_path,const size_t depth,
1569% ExceptionInfo *exception)
1570%
1571% A description of each parameter follows:
1572%
1573% o image: the image.
1574%
1575% o draw_info: the draw info.
1576%
1577% o id: the clip path id.
1578%
1579% o clip_path: the clip path.
1580%
1581% o depth: track the vector depth.
1582%
1583% o exception: return any errors or warnings in this structure.
1584%
1585*/
1586static Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1587 const char *id,const char *clip_path,const size_t depth,
1588 ExceptionInfo *exception)
1589{
1590 DrawInfo
1591 *clone_info;
1592
1593 Image
1594 *clip_mask,
1595 *separate_mask;
1596
1597 MagickStatusType
1598 status;
1599
1600 /*
1601 Draw a clip path.
1602 */
1603 assert(image != (Image *) NULL);
1604 assert(image->signature == MagickCoreSignature);
1605 assert(draw_info != (const DrawInfo *) NULL);
1606 if (IsEventLogging() != MagickFalse)
1607 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1608 clip_mask=AcquireImage((const ImageInfo *) NULL,exception);
1609 status=SetImageExtent(clip_mask,image->columns,image->rows,exception);
1610 if (status == MagickFalse)
1611 return(DestroyImage(clip_mask));
1612 status=SetImageMask(clip_mask,WritePixelMask,(Image *) NULL,exception);
1613 status=QueryColorCompliance("#0000",AllCompliance,
1614 &clip_mask->background_color,exception);
1615 clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1616 clip_mask->background_color.alpha_trait=BlendPixelTrait;
1617 status=SetImageBackgroundColor(clip_mask,exception);
1618 if (draw_info->debug != MagickFalse)
1619 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1620 id);
1621 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1622 (void) CloneString(&clone_info->primitive,clip_path);
1623 status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1624 exception);
1625 if (clone_info->clip_mask != (char *) NULL)
1626 clone_info->clip_mask=DestroyString(clone_info->clip_mask);
1627 status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1628 exception);
1629 clone_info->stroke_width=0.0;
1630 clone_info->alpha=OpaqueAlpha;
1631 clone_info->clip_path=MagickTrue;
1632 status=RenderMVGContent(clip_mask,clone_info,depth+1,exception);
1633 clone_info=DestroyDrawInfo(clone_info);
1634 separate_mask=SeparateImage(clip_mask,AlphaChannel,exception);
1635 if (separate_mask == (Image *) NULL)
1636 status=MagickFalse;
1637 else
1638 {
1639 clip_mask=DestroyImage(clip_mask);
1640 clip_mask=separate_mask;
1641 status&=(MagickStatusType) NegateImage(clip_mask,MagickFalse,exception);
1642 }
1643 if (status == MagickFalse)
1644 clip_mask=DestroyImage(clip_mask);
1645 if (draw_info->debug != MagickFalse)
1646 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1647 return(clip_mask);
1648}
1649␌
1650/*
1651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1652% %
1653% %
1654% %
1655% D r a w C o m p o s i t e M a s k %
1656% %
1657% %
1658% %
1659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1660%
1661% DrawCompositeMask() draws the mask path and returns it as an image mask.
1662%
1663% The format of the DrawCompositeMask method is:
1664%
1665% Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1666% const char *id,const char *mask_path,const size_t depth,
1667% ExceptionInfo *exception)
1668%
1669% A description of each parameter follows:
1670%
1671% o image: the image.
1672%
1673% o draw_info: the draw info.
1674%
1675% o id: the mask path id.
1676%
1677% o mask_path: the mask path.
1678%
1679% o depth: track the vector depth.
1680%
1681% o exception: return any errors or warnings in this structure.
1682%
1683*/
1684static Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1685 const char *id,const char *mask_path,const size_t depth,
1686 ExceptionInfo *exception)
1687{
1688 Image
1689 *composite_mask,
1690 *separate_mask;
1691
1692 DrawInfo
1693 *clone_info;
1694
1695 MagickStatusType
1696 status;
1697
1698 /*
1699 Draw a mask path.
1700 */
1701 assert(image != (Image *) NULL);
1702 assert(image->signature == MagickCoreSignature);
1703 assert(draw_info != (const DrawInfo *) NULL);
1704 if (IsEventLogging() != MagickFalse)
1705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1706 composite_mask=AcquireImage((const ImageInfo *) NULL,exception);
1707 status=SetImageExtent(composite_mask,image->columns,image->rows,exception);
1708 if (status == MagickFalse)
1709 return(DestroyImage(composite_mask));
1710 status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
1711 exception);
1712 status=QueryColorCompliance("#0000",AllCompliance,
1713 &composite_mask->background_color,exception);
1714 composite_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1715 composite_mask->background_color.alpha_trait=BlendPixelTrait;
1716 (void) SetImageBackgroundColor(composite_mask,exception);
1717 if (draw_info->debug != MagickFalse)
1718 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin mask-path %s",
1719 id);
1720 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1721 (void) CloneString(&clone_info->primitive,mask_path);
1722 status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1723 exception);
1724 status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1725 exception);
1726 clone_info->stroke_width=0.0;
1727 clone_info->alpha=OpaqueAlpha;
1728 status=RenderMVGContent(composite_mask,clone_info,depth+1,exception);
1729 clone_info=DestroyDrawInfo(clone_info);
1730 separate_mask=SeparateImage(composite_mask,AlphaChannel,exception);
1731 if (separate_mask != (Image *) NULL)
1732 {
1733 composite_mask=DestroyImage(composite_mask);
1734 composite_mask=separate_mask;
1735 status=NegateImage(composite_mask,MagickFalse,exception);
1736 if (status == MagickFalse)
1737 composite_mask=DestroyImage(composite_mask);
1738 }
1739 if (status == MagickFalse)
1740 composite_mask=DestroyImage(composite_mask);
1741 if (draw_info->debug != MagickFalse)
1742 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end mask-path");
1743 return(composite_mask);
1744}
1745␌
1746/*
1747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1748% %
1749% %
1750% %
1751+ D r a w D a s h P o l y g o n %
1752% %
1753% %
1754% %
1755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1756%
1757% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1758% image while respecting the dash offset and dash pattern attributes.
1759%
1760% The format of the DrawDashPolygon method is:
1761%
1762% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1763% const PrimitiveInfo *primitive_info,Image *image,
1764% ExceptionInfo *exception)
1765%
1766% A description of each parameter follows:
1767%
1768% o draw_info: the draw info.
1769%
1770% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1771%
1772% o image: the image.
1773%
1774% o exception: return any errors or warnings in this structure.
1775%
1776*/
1777static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1778 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
1779{
1780 double
1781 dx,
1782 dy,
1783 length,
1784 maximum_length,
1785 offset,
1786 scale,
1787 total_length;
1788
1789 DrawInfo
1790 *clone_info;
1791
1792 MagickStatusType
1793 status;
1794
1796 *dash_polygon;
1797
1798 ssize_t
1799 i;
1800
1801 size_t
1802 number_vertices;
1803
1804 ssize_t
1805 j,
1806 n;
1807
1808 assert(draw_info != (const DrawInfo *) NULL);
1809 if (draw_info->debug != MagickFalse)
1810 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1811 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
1812 number_vertices=(size_t) i;
1813 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1814 (2UL*number_vertices+32UL),sizeof(*dash_polygon));
1815 if (dash_polygon == (PrimitiveInfo *) NULL)
1816 {
1817 (void) ThrowMagickException(exception,GetMagickModule(),
1818 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1819 return(MagickFalse);
1820 }
1821 (void) memset(dash_polygon,0,(2UL*number_vertices+32UL)*
1822 sizeof(*dash_polygon));
1823 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1824 clone_info->miterlimit=0;
1825 dash_polygon[0]=primitive_info[0];
1826 dash_polygon[0].closed_subpath=MagickFalse;
1827 scale=ExpandAffine(&draw_info->affine);
1828 length=scale*draw_info->dash_pattern[0];
1829 offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
1830 scale*draw_info->dash_offset : 0.0;
1831 j=1;
1832 for (n=0; offset > 0.0; j=0)
1833 {
1834 if (draw_info->dash_pattern[n] <= 0.0)
1835 break;
1836 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1837 if (offset > length)
1838 {
1839 offset-=length;
1840 n++;
1841 length=scale*draw_info->dash_pattern[n];
1842 continue;
1843 }
1844 if (offset < length)
1845 {
1846 length-=offset;
1847 offset=0.0;
1848 break;
1849 }
1850 offset=0.0;
1851 n++;
1852 }
1853 status=MagickTrue;
1854 maximum_length=0.0;
1855 total_length=0.0;
1856 for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
1857 {
1858 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1859 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1860 maximum_length=hypot(dx,dy);
1861 if (maximum_length > (double) (MaxBezierCoordinates >> 2))
1862 continue;
1863 if (fabs(length) < MagickEpsilon)
1864 {
1865 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1866 n++;
1867 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1868 n=0;
1869 length=scale*draw_info->dash_pattern[n];
1870 }
1871 for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
1872 {
1873 total_length+=length;
1874 if ((n & 0x01) != 0)
1875 {
1876 dash_polygon[0]=primitive_info[0];
1877 dash_polygon[0].closed_subpath=MagickFalse;
1878 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1879 total_length*MagickSafeReciprocal(maximum_length));
1880 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1881 total_length*MagickSafeReciprocal(maximum_length));
1882 j=1;
1883 }
1884 else
1885 {
1886 if ((j+1) > (ssize_t) number_vertices)
1887 break;
1888 dash_polygon[j]=primitive_info[i-1];
1889 dash_polygon[j].closed_subpath=MagickFalse;
1890 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1891 total_length*MagickSafeReciprocal(maximum_length));
1892 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1893 total_length*MagickSafeReciprocal(maximum_length));
1894 dash_polygon[j].coordinates=1;
1895 j++;
1896 dash_polygon[0].coordinates=(size_t) j;
1897 dash_polygon[j].primitive=UndefinedPrimitive;
1898 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1899 dash_polygon,exception);
1900 if (status == MagickFalse)
1901 break;
1902 }
1903 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1904 n++;
1905 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1906 n=0;
1907 length=scale*draw_info->dash_pattern[n];
1908 }
1909 length-=(maximum_length-total_length);
1910 if ((n & 0x01) != 0)
1911 continue;
1912 dash_polygon[j]=primitive_info[i];
1913 dash_polygon[j].coordinates=1;
1914 j++;
1915 }
1916 if ((status != MagickFalse) && (total_length < maximum_length) &&
1917 ((n & 0x01) == 0) && (j > 1))
1918 {
1919 dash_polygon[j]=primitive_info[i-1];
1920 dash_polygon[j].closed_subpath=MagickFalse;
1921 dash_polygon[j].point.x+=MagickEpsilon;
1922 dash_polygon[j].point.y+=MagickEpsilon;
1923 dash_polygon[j].coordinates=1;
1924 j++;
1925 dash_polygon[0].coordinates=(size_t) j;
1926 dash_polygon[j].primitive=UndefinedPrimitive;
1927 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1928 dash_polygon,exception);
1929 }
1930 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1931 clone_info=DestroyDrawInfo(clone_info);
1932 if (draw_info->debug != MagickFalse)
1933 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1934 return(status != 0 ? MagickTrue : MagickFalse);
1935}
1936␌
1937/*
1938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1939% %
1940% %
1941% %
1942% D r a w G r a d i e n t I m a g e %
1943% %
1944% %
1945% %
1946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1947%
1948% DrawGradientImage() draws a linear gradient on the image.
1949%
1950% The format of the DrawGradientImage method is:
1951%
1952% MagickBooleanType DrawGradientImage(Image *image,
1953% const DrawInfo *draw_info,ExceptionInfo *exception)
1954%
1955% A description of each parameter follows:
1956%
1957% o image: the image.
1958%
1959% o draw_info: the draw info.
1960%
1961% o exception: return any errors or warnings in this structure.
1962%
1963*/
1964
1965static inline double GetStopColorOffset(const GradientInfo *gradient,
1966 const ssize_t x,const ssize_t y)
1967{
1968 switch (gradient->type)
1969 {
1970 case UndefinedGradient:
1971 case LinearGradient:
1972 {
1973 double
1974 gamma,
1975 length,
1976 offset,
1977 scale;
1978
1979 PointInfo
1980 p,
1981 q;
1982
1983 const SegmentInfo
1984 *gradient_vector;
1985
1986 gradient_vector=(&gradient->gradient_vector);
1987 p.x=gradient_vector->x2-gradient_vector->x1;
1988 p.y=gradient_vector->y2-gradient_vector->y1;
1989 q.x=(double) x-gradient_vector->x1;
1990 q.y=(double) y-gradient_vector->y1;
1991 length=sqrt(q.x*q.x+q.y*q.y);
1992 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
1993 gamma=MagickSafeReciprocal(gamma);
1994 scale=p.x*q.x+p.y*q.y;
1995 offset=gamma*scale*length;
1996 return(offset);
1997 }
1998 case RadialGradient:
1999 {
2000 PointInfo
2001 v;
2002
2003 if (gradient->spread == RepeatSpread)
2004 {
2005 v.x=(double) x-gradient->center.x;
2006 v.y=(double) y-gradient->center.y;
2007 return(sqrt(v.x*v.x+v.y*v.y));
2008 }
2009 v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
2010 gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
2011 gradient->angle))))*MagickSafeReciprocal(gradient->radii.x);
2012 v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
2013 gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
2014 gradient->angle))))*MagickSafeReciprocal(gradient->radii.y);
2015 return(sqrt(v.x*v.x+v.y*v.y));
2016 }
2017 }
2018 return(0.0);
2019}
2020
2021static int StopInfoCompare(const void *x,const void *y)
2022{
2023 StopInfo
2024 *stop_1,
2025 *stop_2;
2026
2027 stop_1=(StopInfo *) x;
2028 stop_2=(StopInfo *) y;
2029 if (stop_1->offset > stop_2->offset)
2030 return(1);
2031 if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
2032 return(0);
2033 return(-1);
2034}
2035
2036MagickExport MagickBooleanType DrawGradientImage(Image *image,
2037 const DrawInfo *draw_info,ExceptionInfo *exception)
2038{
2039 CacheView
2040 *image_view;
2041
2042 const GradientInfo
2043 *gradient;
2044
2045 const SegmentInfo
2046 *gradient_vector;
2047
2048 double
2049 length;
2050
2051 MagickBooleanType
2052 status;
2053
2054 PixelInfo
2055 zero;
2056
2057 PointInfo
2058 point;
2059
2061 bounding_box;
2062
2063 size_t
2064 height;
2065
2066 ssize_t
2067 y;
2068
2069 /*
2070 Draw linear or radial gradient on image.
2071 */
2072 assert(image != (Image *) NULL);
2073 assert(image->signature == MagickCoreSignature);
2074 assert(draw_info != (const DrawInfo *) NULL);
2075 if (IsEventLogging() != MagickFalse)
2076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2077 gradient=(&draw_info->gradient);
2078 qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
2079 StopInfoCompare);
2080 gradient_vector=(&gradient->gradient_vector);
2081 point.x=gradient_vector->x2-gradient_vector->x1;
2082 point.y=gradient_vector->y2-gradient_vector->y1;
2083 length=sqrt(point.x*point.x+point.y*point.y);
2084 bounding_box=gradient->bounding_box;
2085 status=MagickTrue;
2086 GetPixelInfo(image,&zero);
2087 image_view=AcquireAuthenticCacheView(image,exception);
2088 height=(size_t) (bounding_box.y+(ssize_t) bounding_box.height);
2089#if defined(MAGICKCORE_OPENMP_SUPPORT)
2090 #pragma omp parallel for schedule(static) shared(status) \
2091 magick_number_threads(image,image,height,1)
2092#endif
2093 for (y=bounding_box.y; y < (ssize_t) height; y++)
2094 {
2095 double
2096 alpha,
2097 offset;
2098
2099 PixelInfo
2100 composite,
2101 pixel;
2102
2103 Quantum
2104 *magick_restrict q;
2105
2106 size_t
2107 width;
2108
2109 ssize_t
2110 i,
2111 j,
2112 x;
2113
2114 if (status == MagickFalse)
2115 continue;
2116 q=GetCacheViewAuthenticPixels(image_view,bounding_box.x,y,(size_t)
2117 bounding_box.width,1,exception);
2118 if (q == (Quantum *) NULL)
2119 {
2120 status=MagickFalse;
2121 continue;
2122 }
2123 pixel=zero;
2124 composite=zero;
2125 offset=GetStopColorOffset(gradient,0,y);
2126 if (gradient->type != RadialGradient)
2127 offset*=MagickSafeReciprocal(length);
2128 width=(size_t) (bounding_box.x+(ssize_t) bounding_box.width);
2129 for (x=bounding_box.x; x < (ssize_t) width; x++)
2130 {
2131 GetPixelInfoPixel(image,q,&pixel);
2132 switch (gradient->spread)
2133 {
2134 case UndefinedSpread:
2135 case PadSpread:
2136 {
2137 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2138 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2139 {
2140 offset=GetStopColorOffset(gradient,x,y);
2141 if (gradient->type != RadialGradient)
2142 offset*=MagickSafeReciprocal(length);
2143 }
2144 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2145 if (offset < gradient->stops[i].offset)
2146 break;
2147 if ((offset < 0.0) || (i == 0))
2148 composite=gradient->stops[0].color;
2149 else
2150 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
2151 composite=gradient->stops[gradient->number_stops-1].color;
2152 else
2153 {
2154 j=i;
2155 i--;
2156 alpha=(offset-gradient->stops[i].offset)/
2157 (gradient->stops[j].offset-gradient->stops[i].offset);
2158 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2159 &gradient->stops[j].color,alpha,&composite);
2160 }
2161 break;
2162 }
2163 case ReflectSpread:
2164 {
2165 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2166 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2167 {
2168 offset=GetStopColorOffset(gradient,x,y);
2169 if (gradient->type != RadialGradient)
2170 offset*=MagickSafeReciprocal(length);
2171 }
2172 if (offset < 0.0)
2173 offset=(-offset);
2174 if ((ssize_t) fmod(offset,2.0) == 0)
2175 offset=fmod(offset,1.0);
2176 else
2177 offset=1.0-fmod(offset,1.0);
2178 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2179 if (offset < gradient->stops[i].offset)
2180 break;
2181 if (i == 0)
2182 composite=gradient->stops[0].color;
2183 else
2184 if (i == (ssize_t) gradient->number_stops)
2185 composite=gradient->stops[gradient->number_stops-1].color;
2186 else
2187 {
2188 j=i;
2189 i--;
2190 alpha=(offset-gradient->stops[i].offset)/
2191 (gradient->stops[j].offset-gradient->stops[i].offset);
2192 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2193 &gradient->stops[j].color,alpha,&composite);
2194 }
2195 break;
2196 }
2197 case RepeatSpread:
2198 {
2199 double
2200 repeat;
2201
2202 MagickBooleanType
2203 antialias;
2204
2205 antialias=MagickFalse;
2206 repeat=0.0;
2207 if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2208 (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2209 {
2210 offset=GetStopColorOffset(gradient,x,y);
2211 if (gradient->type == LinearGradient)
2212 {
2213 repeat=fmod(offset,length);
2214 if (repeat < 0.0)
2215 repeat=length-fmod(-repeat,length);
2216 else
2217 repeat=fmod(offset,length);
2218 antialias=(repeat < length) && ((repeat+1.0) > length) ?
2219 MagickTrue : MagickFalse;
2220 offset=MagickSafeReciprocal(length)*repeat;
2221 }
2222 else
2223 {
2224 repeat=fmod(offset,gradient->radius);
2225 if (repeat < 0.0)
2226 repeat=gradient->radius-fmod(-repeat,gradient->radius);
2227 else
2228 repeat=fmod(offset,gradient->radius);
2229 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
2230 MagickFalse;
2231 offset=repeat*MagickSafeReciprocal(gradient->radius);
2232 }
2233 }
2234 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2235 if (offset < gradient->stops[i].offset)
2236 break;
2237 if (i == 0)
2238 composite=gradient->stops[0].color;
2239 else
2240 if (i == (ssize_t) gradient->number_stops)
2241 composite=gradient->stops[gradient->number_stops-1].color;
2242 else
2243 {
2244 j=i;
2245 i--;
2246 alpha=(offset-gradient->stops[i].offset)/
2247 (gradient->stops[j].offset-gradient->stops[i].offset);
2248 if (antialias != MagickFalse)
2249 {
2250 if (gradient->type == LinearGradient)
2251 alpha=length-repeat;
2252 else
2253 alpha=gradient->radius-repeat;
2254 i=0;
2255 j=(ssize_t) gradient->number_stops-1L;
2256 }
2257 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2258 &gradient->stops[j].color,alpha,&composite);
2259 }
2260 break;
2261 }
2262 }
2263 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
2264 &pixel);
2265 SetPixelViaPixelInfo(image,&pixel,q);
2266 q+=(ptrdiff_t) GetPixelChannels(image);
2267 }
2268 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2269 status=MagickFalse;
2270 }
2271 image_view=DestroyCacheView(image_view);
2272 return(status);
2273}
2274␌
2275/*
2276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2277% %
2278% %
2279% %
2280% D r a w I m a g e %
2281% %
2282% %
2283% %
2284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2285%
2286% DrawImage() draws a graphic primitive on your image. The primitive
2287% may be represented as a string or filename. Precede the filename with an
2288% "at" sign (@) and the contents of the file are drawn on the image. You
2289% can affect how text is drawn by setting one or more members of the draw
2290% info structure.
2291%
2292% The format of the DrawImage method is:
2293%
2294% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
2295% ExceptionInfo *exception)
2296%
2297% A description of each parameter follows:
2298%
2299% o image: the image.
2300%
2301% o draw_info: the draw info.
2302%
2303% o exception: return any errors or warnings in this structure.
2304%
2305*/
2306
2307static inline MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
2308 const double pad)
2309{
2310 double
2311 proposed_extent;
2312
2314 *primitive_info;
2315
2316 size_t
2317 extent;
2318
2319 ssize_t
2320 i;
2321
2322 if ((mvg_info == (MVGInfo *) NULL) ||
2323 (mvg_info->primitive_info == (PrimitiveInfo **) NULL) ||
2324 (*mvg_info->primitive_info == (PrimitiveInfo *) NULL) ||
2325 (mvg_info->extent == (size_t *) NULL))
2326 return(MagickFalse);
2327 proposed_extent=mvg_info->offset+pad+PrimitiveExtentPad+1.0;
2328 if ((proposed_extent <= 0.0) || (proposed_extent > (double) MAGICK_SIZE_MAX))
2329 return(MagickFalse);
2330 extent=CastDoubleToSizeT(ceil(proposed_extent));
2331 if (extent <= *mvg_info->extent)
2332 return(MagickTrue);
2333 if (extent > (GetMaxMemoryRequest()/sizeof(PrimitiveInfo)))
2334 return(MagickFalse);
2335 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
2336 *mvg_info->primitive_info,extent+1,sizeof(PrimitiveInfo));
2337 if (primitive_info == (PrimitiveInfo *) NULL)
2338 {
2339 /*
2340 Create a stack to unwind; report failure.
2341 */
2342 extent=(size_t) PrimitiveExtentPad;
2343 primitive_info=(PrimitiveInfo *) AcquireCriticalMemory(extent*
2344 sizeof(*primitive_info));
2345 (void) memset(primitive_info,0,extent*sizeof(*primitive_info));
2346 *mvg_info->primitive_info=primitive_info;
2347 *mvg_info->extent=extent;
2348 mvg_info->offset=0;
2349 ThrowMagickException(mvg_info->exception,GetMagickModule(),
2350 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2351 return(MagickFalse);
2352 }
2353 primitive_info[extent].primitive=UndefinedPrimitive;
2354 primitive_info[extent].text=(char *) NULL;
2355 /*
2356 Commit updated buffer.
2357 */
2358 for (i=(ssize_t) *mvg_info->extent; i < (ssize_t) extent; i++)
2359 {
2360 primitive_info[i].primitive=UndefinedPrimitive;
2361 primitive_info[i].text=(char *) NULL;
2362 }
2363 *mvg_info->primitive_info=primitive_info;
2364 *mvg_info->extent=extent;
2365 return(MagickTrue);
2366}
2367
2368static inline double GetDrawValue(const char *magick_restrict string,
2369 char **magick_restrict sentinel)
2370{
2371 char
2372 **magick_restrict q;
2373
2374 double
2375 value;
2376
2377 q=sentinel;
2378 value=InterpretLocaleValue(string,q);
2379 sentinel=q;
2380 return(value);
2381}
2382
2383static int MVGMacroCompare(const void *target,const void *source)
2384{
2385 const char
2386 *p,
2387 *q;
2388
2389 p=(const char *) target;
2390 q=(const char *) source;
2391 return(strcmp(p,q));
2392}
2393
2394static SplayTreeInfo *GetMVGMacros(const char *primitive,
2395 ExceptionInfo *exception)
2396{
2397 char
2398 *macro,
2399 *token;
2400
2401 const char
2402 *q;
2403
2404 size_t
2405 extent;
2406
2408 *macros;
2409
2410 /*
2411 Scan graphic primitives for definitions and classes.
2412 */
2413 if (primitive == (const char *) NULL)
2414 return((SplayTreeInfo *) NULL);
2415 macros=NewSplayTree(MVGMacroCompare,RelinquishMagickMemory,
2416 RelinquishMagickMemory);
2417 macro=AcquireString(primitive);
2418 token=AcquireString(primitive);
2419 extent=strlen(token)+MagickPathExtent;
2420 for (q=primitive; *q != '\0'; )
2421 {
2422 if (GetNextToken(q,&q,extent,token) < 1)
2423 break;
2424 if (*token == '\0')
2425 break;
2426 if (LocaleCompare("push",token) == 0)
2427 {
2428 const char
2429 *end,
2430 *start;
2431
2432 (void) GetNextToken(q,&q,extent,token);
2433 if (*q == '"')
2434 {
2435 char
2436 name[MagickPathExtent];
2437
2438 const char
2439 *p;
2440
2441 ssize_t
2442 n;
2443
2444 /*
2445 Named macro (e.g. push graphic-context "wheel").
2446 */
2447 (void) GetNextToken(q,&q,extent,token);
2448 start=q;
2449 end=q;
2450 (void) CopyMagickString(name,token,MagickPathExtent);
2451 n=1;
2452 for (p=q; *p != '\0'; )
2453 {
2454 if (GetNextToken(p,&p,extent,token) < 1)
2455 break;
2456 if (*token == '\0')
2457 break;
2458 if (LocaleCompare(token,"pop") == 0)
2459 {
2460 end=p-strlen(token)-1;
2461 n--;
2462 }
2463 if (LocaleCompare(token,"push") == 0)
2464 {
2465 if (n++ >= MaxVectorRecursionDepth)
2466 {
2467 (void) ThrowMagickException(exception,GetMagickModule(),
2468 DrawError,"VectorGraphicsNestedTooDeeply","`%s'",token);
2469 break;
2470 }
2471 }
2472 if ((n == 0) && (end >= start))
2473 {
2474 size_t
2475 length=(size_t) (end-start);
2476
2477 /*
2478 Extract macro.
2479 */
2480 (void) GetNextToken(p,&p,extent,token);
2481 if (length > 0)
2482 {
2483 (void) CopyMagickString(macro,start,length);
2484 (void) AddValueToSplayTree(macros,ConstantString(name),
2485 ConstantString(macro));
2486 }
2487 break;
2488 }
2489 }
2490 }
2491 }
2492 }
2493 token=DestroyString(token);
2494 macro=DestroyString(macro);
2495 return(macros);
2496}
2497
2498static inline MagickBooleanType IsValidListChar(int c)
2499{
2500 if ((c >= '0') && (c <= '9'))
2501 return(MagickTrue);
2502 switch (c)
2503 {
2504 case '.':
2505 case '+':
2506 case '-':
2507 case ',':
2508 case ' ':
2509 case '\t':
2510 case '\r':
2511 case '\n':
2512 break;
2513 default:
2514 return(MagickFalse);
2515 }
2516 return(MagickTrue);
2517}
2518
2519static inline MagickBooleanType IsValidPoint(const char *point)
2520{
2521 char
2522 *p;
2523
2524 double
2525 value;
2526
2527 value=GetDrawValue(point,&p);
2528 return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
2529 MagickTrue);
2530}
2531
2532static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
2533 const PointInfo point)
2534{
2535 primitive_info->point=point;
2536 primitive_info->coordinates=1;
2537 primitive_info->closed_subpath=MagickFalse;
2538 primitive_info->text=(char *) NULL;
2539 return(MagickTrue);
2540}
2541
2542static MagickBooleanType RenderMVGContent(Image *image,
2543 const DrawInfo *draw_info,const size_t depth,ExceptionInfo *exception)
2544{
2545#define RenderImageTag "Render/Image"
2546
2548 affine,
2549 current;
2550
2551 char
2552 keyword[MagickPathExtent],
2553 geometry[MagickPathExtent],
2554 *next_token,
2555 pattern[MagickPathExtent],
2556 *primitive,
2557 *token;
2558
2559 const char
2560 *p,
2561 *q;
2562
2563 double
2564 angle,
2565 coordinates,
2566 cursor,
2567 factor,
2568 primitive_extent;
2569
2570 DrawInfo
2571 *clone_info,
2572 **graphic_context;
2573
2574 MagickBooleanType
2575 proceed;
2576
2577 MagickStatusType
2578 status;
2579
2580 MVGInfo
2581 mvg_info;
2582
2583 PointInfo
2584 point;
2585
2587 *primitive_info;
2588
2589 PrimitiveType
2590 primitive_type;
2591
2593 bounds;
2594
2595 size_t
2596 extent,
2597 number_points,
2598 number_stops;
2599
2601 *macros;
2602
2603 ssize_t
2604 classDepth = 0,
2605 defsDepth,
2606 i,
2607 j,
2608 k,
2609 n,
2610 symbolDepth,
2611 x;
2612
2613 StopInfo
2614 *stops;
2615
2617 metrics;
2618
2619 assert(image != (Image *) NULL);
2620 assert(image->signature == MagickCoreSignature);
2621 assert(draw_info != (DrawInfo *) NULL);
2622 assert(draw_info->signature == MagickCoreSignature);
2623 if (IsEventLogging() != MagickFalse)
2624 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2625 if (depth >= MaxVectorRecursionDepth)
2626 ThrowBinaryException(DrawError,"VectorGraphicsNestedTooDeeply",
2627 image->filename);
2628 if ((draw_info->primitive == (char *) NULL) ||
2629 (*draw_info->primitive == '\0'))
2630 return(MagickFalse);
2631 if (draw_info->debug != MagickFalse)
2632 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
2633 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2634 return(MagickFalse);
2635 if ((image->alpha_trait & BlendPixelTrait) == 0)
2636 {
2637 status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2638 if (status == MagickFalse)
2639 return(MagickFalse);
2640 }
2641 if ((*draw_info->primitive == '@') && (strlen(draw_info->primitive) > 1) &&
2642 (*(draw_info->primitive+1) != '-') && (depth == 0))
2643 primitive=FileToString(draw_info->primitive,~0UL,exception);
2644 else
2645 primitive=AcquireString(draw_info->primitive);
2646 if (primitive == (char *) NULL)
2647 return(MagickFalse);
2648 primitive_extent=(double) strlen(primitive);
2649 (void) SetImageArtifact(image,"mvg:vector-graphics",primitive);
2650 n=0;
2651 number_stops=0;
2652 stops=(StopInfo *) NULL;
2653 /*
2654 Allocate primitive info memory.
2655 */
2656 graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
2657 if (graphic_context == (DrawInfo **) NULL)
2658 {
2659 primitive=DestroyString(primitive);
2660 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2661 image->filename);
2662 }
2663 number_points=(size_t) PrimitiveExtentPad;
2664 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
2665 (number_points+1),sizeof(*primitive_info));
2666 if (primitive_info == (PrimitiveInfo *) NULL)
2667 {
2668 primitive=DestroyString(primitive);
2669 for ( ; n >= 0; n--)
2670 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2671 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
2672 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2673 image->filename);
2674 }
2675 (void) memset(primitive_info,0,(size_t) (number_points+1)*
2676 sizeof(*primitive_info));
2677 (void) memset(&mvg_info,0,sizeof(mvg_info));
2678 mvg_info.primitive_info=(&primitive_info);
2679 mvg_info.extent=(&number_points);
2680 mvg_info.exception=exception;
2681 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2682 graphic_context[n]->viewbox=image->page;
2683 if ((image->page.width == 0) || (image->page.height == 0))
2684 {
2685 graphic_context[n]->viewbox.width=image->columns;
2686 graphic_context[n]->viewbox.height=image->rows;
2687 }
2688 token=AcquireString(primitive);
2689 extent=strlen(token)+MagickPathExtent;
2690 defsDepth=0;
2691 symbolDepth=0;
2692 cursor=0.0;
2693 macros=GetMVGMacros(primitive,exception);
2694 status=MagickTrue;
2695 for (q=primitive; *q != '\0'; )
2696 {
2697 /*
2698 Interpret graphic primitive.
2699 */
2700 if (GetNextToken(q,&q,MagickPathExtent,keyword) < 1)
2701 break;
2702 if (*keyword == '\0')
2703 break;
2704 if (*keyword == '#')
2705 {
2706 /*
2707 Comment.
2708 */
2709 while ((*q != '\n') && (*q != '\0'))
2710 q++;
2711 continue;
2712 }
2713 p=q-strlen(keyword)-1;
2714 primitive_type=UndefinedPrimitive;
2715 current=graphic_context[n]->affine;
2716 GetAffineMatrix(&affine);
2717 *token='\0';
2718 switch (*keyword)
2719 {
2720 case ';':
2721 break;
2722 case 'a':
2723 case 'A':
2724 {
2725 if (LocaleCompare("affine",keyword) == 0)
2726 {
2727 (void) GetNextToken(q,&q,extent,token);
2728 affine.sx=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);
2734 if (*token == ',')
2735 (void) GetNextToken(q,&q,extent,token);
2736 affine.ry=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);
2742 if (*token == ',')
2743 (void) GetNextToken(q,&q,extent,token);
2744 affine.rx=GetDrawValue(token,&next_token);
2745 if (token == next_token)
2746 ThrowPointExpectedException(token,exception);
2747 (void) GetNextToken(q,&q,extent,token);
2748 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2749 ThrowPointExpectedException(token,exception);
2750 if (*token == ',')
2751 (void) GetNextToken(q,&q,extent,token);
2752 affine.sy=GetDrawValue(token,&next_token);
2753 if (token == next_token)
2754 ThrowPointExpectedException(token,exception);
2755 (void) GetNextToken(q,&q,extent,token);
2756 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2757 ThrowPointExpectedException(token,exception);
2758 if (*token == ',')
2759 (void) GetNextToken(q,&q,extent,token);
2760 affine.tx=GetDrawValue(token,&next_token);
2761 if (token == next_token)
2762 ThrowPointExpectedException(token,exception);
2763 (void) GetNextToken(q,&q,extent,token);
2764 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2765 ThrowPointExpectedException(token,exception);
2766 if (*token == ',')
2767 (void) GetNextToken(q,&q,extent,token);
2768 affine.ty=GetDrawValue(token,&next_token);
2769 if (token == next_token)
2770 ThrowPointExpectedException(token,exception);
2771 break;
2772 }
2773 if (LocaleCompare("alpha",keyword) == 0)
2774 {
2775 primitive_type=AlphaPrimitive;
2776 break;
2777 }
2778 if (LocaleCompare("arc",keyword) == 0)
2779 {
2780 primitive_type=ArcPrimitive;
2781 break;
2782 }
2783 status=MagickFalse;
2784 break;
2785 }
2786 case 'b':
2787 case 'B':
2788 {
2789 if (LocaleCompare("bezier",keyword) == 0)
2790 {
2791 primitive_type=BezierPrimitive;
2792 break;
2793 }
2794 if (LocaleCompare("border-color",keyword) == 0)
2795 {
2796 (void) GetNextToken(q,&q,extent,token);
2797 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
2798 &graphic_context[n]->border_color,exception);
2799 break;
2800 }
2801 status=MagickFalse;
2802 break;
2803 }
2804 case 'c':
2805 case 'C':
2806 {
2807 if (LocaleCompare("class",keyword) == 0)
2808 {
2809 const char
2810 *mvg_class;
2811
2812 (void) GetNextToken(q,&q,extent,token);
2813 if ((*token == '\0') || (*token == ';'))
2814 {
2815 status=MagickFalse;
2816 break;
2817 }
2818 /*
2819 Identify recursion.
2820 */
2821 for (i=0; i <= n; i++)
2822 if (LocaleCompare(token,graphic_context[i]->id) == 0)
2823 break;
2824 if (i <= n)
2825 break;
2826 if (classDepth++ >= MaxVectorRecursionDepth)
2827 {
2828 (void) ThrowMagickException(exception,GetMagickModule(),
2829 DrawError,"VectorGraphicsNestedTooDeeply","`%s'",token);
2830 status=MagickFalse;
2831 break;
2832 }
2833 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
2834 if ((graphic_context[n]->render != MagickFalse) &&
2835 (mvg_class != (const char *) NULL) && (p > primitive))
2836 {
2837 char
2838 *elements;
2839
2840 ssize_t
2841 offset;
2842
2843 /*
2844 Inject class elements in stream.
2845 */
2846 (void) CloneString(&graphic_context[n]->id,token);
2847 offset=(ssize_t) (p-primitive);
2848 elements=AcquireString(primitive);
2849 elements[offset]='\0';
2850 (void) ConcatenateString(&elements,mvg_class);
2851 (void) ConcatenateString(&elements,"\n");
2852 (void) ConcatenateString(&elements,q);
2853 primitive=DestroyString(primitive);
2854 primitive=elements;
2855 q=primitive+offset;
2856 }
2857 break;
2858 }
2859 if (LocaleCompare("clip-path",keyword) == 0)
2860 {
2861 const char
2862 *clip_path;
2863
2864 /*
2865 Take a node from within the MVG document, and duplicate it here.
2866 */
2867 (void) GetNextToken(q,&q,extent,token);
2868 if (*token == '\0')
2869 {
2870 status=MagickFalse;
2871 break;
2872 }
2873 (void) CloneString(&graphic_context[n]->clip_mask,token);
2874 clip_path=(const char *) GetValueFromSplayTree(macros,token);
2875 if (clip_path != (const char *) NULL)
2876 {
2877 if (graphic_context[n]->clipping_mask != (Image *) NULL)
2878 graphic_context[n]->clipping_mask=
2879 DestroyImage(graphic_context[n]->clipping_mask);
2880 graphic_context[n]->clipping_mask=DrawClippingMask(image,
2881 graphic_context[n],token,clip_path,depth+1,exception);
2882 if (graphic_context[n]->compliance != SVGCompliance)
2883 {
2884 clip_path=(const char *) GetValueFromSplayTree(macros,
2885 graphic_context[n]->clip_mask);
2886 if (clip_path != (const char *) NULL)
2887 (void) SetImageArtifact(image,
2888 graphic_context[n]->clip_mask,clip_path);
2889 status&=(MagickStatusType) DrawClipPath_(image,
2890 graphic_context[n],graphic_context[n]->clip_mask,
2891 depth+1,exception);
2892 }
2893 }
2894 break;
2895 }
2896 if (LocaleCompare("clip-rule",keyword) == 0)
2897 {
2898 ssize_t
2899 fill_rule;
2900
2901 (void) GetNextToken(q,&q,extent,token);
2902 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2903 token);
2904 if (fill_rule == -1)
2905 {
2906 status=MagickFalse;
2907 break;
2908 }
2909 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2910 break;
2911 }
2912 if (LocaleCompare("clip-units",keyword) == 0)
2913 {
2914 ssize_t
2915 clip_units;
2916
2917 (void) GetNextToken(q,&q,extent,token);
2918 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
2919 token);
2920 if (clip_units == -1)
2921 {
2922 status=MagickFalse;
2923 break;
2924 }
2925 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
2926 if (clip_units == ObjectBoundingBox)
2927 {
2928 GetAffineMatrix(&current);
2929 affine.sx=draw_info->bounds.x2;
2930 affine.sy=draw_info->bounds.y2;
2931 affine.tx=draw_info->bounds.x1;
2932 affine.ty=draw_info->bounds.y1;
2933 break;
2934 }
2935 break;
2936 }
2937 if (LocaleCompare("circle",keyword) == 0)
2938 {
2939 primitive_type=CirclePrimitive;
2940 break;
2941 }
2942 if (LocaleCompare("color",keyword) == 0)
2943 {
2944 primitive_type=ColorPrimitive;
2945 break;
2946 }
2947 if (LocaleCompare("compliance",keyword) == 0)
2948 {
2949 /*
2950 MVG compliance associates a clipping mask with an image; SVG
2951 compliance associates a clipping mask with a graphics context.
2952 */
2953 (void) GetNextToken(q,&q,extent,token);
2954 graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
2955 MagickComplianceOptions,MagickFalse,token);
2956 break;
2957 }
2958 if (LocaleCompare("currentColor",keyword) == 0)
2959 {
2960 (void) GetNextToken(q,&q,extent,token);
2961 break;
2962 }
2963 status=MagickFalse;
2964 break;
2965 }
2966 case 'd':
2967 case 'D':
2968 {
2969 if (LocaleCompare("decorate",keyword) == 0)
2970 {
2971 ssize_t
2972 decorate;
2973
2974 (void) GetNextToken(q,&q,extent,token);
2975 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
2976 token);
2977 if (decorate == -1)
2978 {
2979 status=MagickFalse;
2980 break;
2981 }
2982 graphic_context[n]->decorate=(DecorationType) decorate;
2983 break;
2984 }
2985 if (LocaleCompare("density",keyword) == 0)
2986 {
2987 (void) GetNextToken(q,&q,extent,token);
2988 (void) CloneString(&graphic_context[n]->density,token);
2989 break;
2990 }
2991 if (LocaleCompare("direction",keyword) == 0)
2992 {
2993 ssize_t
2994 direction;
2995
2996 (void) GetNextToken(q,&q,extent,token);
2997 direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
2998 token);
2999 if (direction == -1)
3000 status=MagickFalse;
3001 else
3002 graphic_context[n]->direction=(DirectionType) direction;
3003 break;
3004 }
3005 status=MagickFalse;
3006 break;
3007 }
3008 case 'e':
3009 case 'E':
3010 {
3011 if (LocaleCompare("ellipse",keyword) == 0)
3012 {
3013 primitive_type=EllipsePrimitive;
3014 break;
3015 }
3016 if (LocaleCompare("encoding",keyword) == 0)
3017 {
3018 (void) GetNextToken(q,&q,extent,token);
3019 (void) CloneString(&graphic_context[n]->encoding,token);
3020 break;
3021 }
3022 status=MagickFalse;
3023 break;
3024 }
3025 case 'f':
3026 case 'F':
3027 {
3028 if (LocaleCompare("fill",keyword) == 0)
3029 {
3030 const char
3031 *mvg_class;
3032
3033 (void) GetNextToken(q,&q,extent,token);
3034 if (graphic_context[n]->clip_path != MagickFalse)
3035 break;
3036 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
3037 if (mvg_class != (const char *) NULL)
3038 {
3039 (void) DrawPatternPath(image,draw_info,mvg_class,
3040 &graphic_context[n]->fill_pattern,exception);
3041 break;
3042 }
3043 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3044 if (GetImageArtifact(image,pattern) != (const char *) NULL)
3045 {
3046 (void) DrawPatternPath(image,draw_info,token,
3047 &graphic_context[n]->fill_pattern,exception);
3048 break;
3049 }
3050 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3051 &graphic_context[n]->fill,exception);
3052 if (graphic_context[n]->fill_alpha != (double) OpaqueAlpha)
3053 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3054 break;
3055 }
3056 if (LocaleCompare("fill-opacity",keyword) == 0)
3057 {
3058 double
3059 opacity;
3060
3061 (void) GetNextToken(q,&q,extent,token);
3062 if (graphic_context[n]->clip_path != MagickFalse)
3063 break;
3064 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3065 opacity=MagickMin(MagickMax(factor*
3066 GetDrawValue(token,&next_token),0.0),1.0);
3067 if (token == next_token)
3068 ThrowPointExpectedException(token,exception);
3069 if (graphic_context[n]->compliance == SVGCompliance)
3070 graphic_context[n]->fill_alpha*=opacity;
3071 else
3072 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3073 if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3074 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3075 else
3076 graphic_context[n]->fill.alpha=(MagickRealType)
3077 ClampToQuantum((double) QuantumRange*opacity);
3078 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3079 break;
3080 }
3081 if (LocaleCompare("fill-rule",keyword) == 0)
3082 {
3083 ssize_t
3084 fill_rule;
3085
3086 (void) GetNextToken(q,&q,extent,token);
3087 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
3088 token);
3089 if (fill_rule == -1)
3090 {
3091 status=MagickFalse;
3092 break;
3093 }
3094 graphic_context[n]->fill_rule=(FillRule) fill_rule;
3095 break;
3096 }
3097 if (LocaleCompare("font",keyword) == 0)
3098 {
3099 (void) GetNextToken(q,&q,extent,token);
3100 (void) CloneString(&graphic_context[n]->font,token);
3101 if (LocaleCompare("none",token) == 0)
3102 graphic_context[n]->font=(char *) RelinquishMagickMemory(
3103 graphic_context[n]->font);
3104 break;
3105 }
3106 if (LocaleCompare("font-family",keyword) == 0)
3107 {
3108 (void) GetNextToken(q,&q,extent,token);
3109 (void) CloneString(&graphic_context[n]->family,token);
3110 break;
3111 }
3112 if (LocaleCompare("font-size",keyword) == 0)
3113 {
3114 (void) GetNextToken(q,&q,extent,token);
3115 graphic_context[n]->pointsize=GetDrawValue(token,&next_token);
3116 if (token == next_token)
3117 ThrowPointExpectedException(token,exception);
3118 break;
3119 }
3120 if (LocaleCompare("font-stretch",keyword) == 0)
3121 {
3122 ssize_t
3123 stretch;
3124
3125 (void) GetNextToken(q,&q,extent,token);
3126 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
3127 if (stretch == -1)
3128 {
3129 status=MagickFalse;
3130 break;
3131 }
3132 graphic_context[n]->stretch=(StretchType) stretch;
3133 break;
3134 }
3135 if (LocaleCompare("font-style",keyword) == 0)
3136 {
3137 ssize_t
3138 style;
3139
3140 (void) GetNextToken(q,&q,extent,token);
3141 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
3142 if (style == -1)
3143 {
3144 status=MagickFalse;
3145 break;
3146 }
3147 graphic_context[n]->style=(StyleType) style;
3148 break;
3149 }
3150 if (LocaleCompare("font-weight",keyword) == 0)
3151 {
3152 ssize_t
3153 weight;
3154
3155 (void) GetNextToken(q,&q,extent,token);
3156 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
3157 if (weight == -1)
3158 weight=(ssize_t) StringToUnsignedLong(token);
3159 graphic_context[n]->weight=(size_t) weight;
3160 break;
3161 }
3162 status=MagickFalse;
3163 break;
3164 }
3165 case 'g':
3166 case 'G':
3167 {
3168 if (LocaleCompare("gradient-units",keyword) == 0)
3169 {
3170 (void) GetNextToken(q,&q,extent,token);
3171 break;
3172 }
3173 if (LocaleCompare("gravity",keyword) == 0)
3174 {
3175 ssize_t
3176 gravity;
3177
3178 (void) GetNextToken(q,&q,extent,token);
3179 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
3180 if (gravity == -1)
3181 {
3182 status=MagickFalse;
3183 break;
3184 }
3185 graphic_context[n]->gravity=(GravityType) gravity;
3186 break;
3187 }
3188 status=MagickFalse;
3189 break;
3190 }
3191 case 'i':
3192 case 'I':
3193 {
3194 if (LocaleCompare("image",keyword) == 0)
3195 {
3196 ssize_t
3197 compose;
3198
3199 primitive_type=ImagePrimitive;
3200 (void) GetNextToken(q,&q,extent,token);
3201 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
3202 if (compose == -1)
3203 {
3204 status=MagickFalse;
3205 break;
3206 }
3207 graphic_context[n]->compose=(CompositeOperator) compose;
3208 break;
3209 }
3210 if (LocaleCompare("interline-spacing",keyword) == 0)
3211 {
3212 (void) GetNextToken(q,&q,extent,token);
3213 graphic_context[n]->interline_spacing=GetDrawValue(token,
3214 &next_token);
3215 if (token == next_token)
3216 ThrowPointExpectedException(token,exception);
3217 break;
3218 }
3219 if (LocaleCompare("interword-spacing",keyword) == 0)
3220 {
3221 (void) GetNextToken(q,&q,extent,token);
3222 graphic_context[n]->interword_spacing=GetDrawValue(token,
3223 &next_token);
3224 if (token == next_token)
3225 ThrowPointExpectedException(token,exception);
3226 break;
3227 }
3228 status=MagickFalse;
3229 break;
3230 }
3231 case 'k':
3232 case 'K':
3233 {
3234 if (LocaleCompare("kerning",keyword) == 0)
3235 {
3236 (void) GetNextToken(q,&q,extent,token);
3237 graphic_context[n]->kerning=GetDrawValue(token,&next_token);
3238 if (token == next_token)
3239 ThrowPointExpectedException(token,exception);
3240 break;
3241 }
3242 status=MagickFalse;
3243 break;
3244 }
3245 case 'l':
3246 case 'L':
3247 {
3248 if (LocaleCompare("letter-spacing",keyword) == 0)
3249 {
3250 (void) GetNextToken(q,&q,extent,token);
3251 if (IsValidPoint(token) == MagickFalse)
3252 break;
3253 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3254 clone_info->text=AcquireString(" ");
3255 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,&metrics,
3256 exception);
3257 graphic_context[n]->kerning=metrics.width*
3258 GetDrawValue(token,&next_token);
3259 clone_info=DestroyDrawInfo(clone_info);
3260 if (token == next_token)
3261 ThrowPointExpectedException(token,exception);
3262 break;
3263 }
3264 if (LocaleCompare("line",keyword) == 0)
3265 {
3266 primitive_type=LinePrimitive;
3267 break;
3268 }
3269 status=MagickFalse;
3270 break;
3271 }
3272 case 'm':
3273 case 'M':
3274 {
3275 if (LocaleCompare("mask",keyword) == 0)
3276 {
3277 const char
3278 *mask_path;
3279
3280 /*
3281 Take a node from within the MVG document, and duplicate it here.
3282 */
3283 (void) GetNextToken(q,&q,extent,token);
3284 mask_path=(const char *) GetValueFromSplayTree(macros,token);
3285 if (mask_path != (const char *) NULL)
3286 {
3287 if (graphic_context[n]->composite_mask != (Image *) NULL)
3288 graphic_context[n]->composite_mask=
3289 DestroyImage(graphic_context[n]->composite_mask);
3290 graphic_context[n]->composite_mask=DrawCompositeMask(image,
3291 graphic_context[n],token,mask_path,depth+1,exception);
3292 if (graphic_context[n]->compliance != SVGCompliance)
3293 status=SetImageMask(image,CompositePixelMask,
3294 graphic_context[n]->composite_mask,exception);
3295 }
3296 break;
3297 }
3298 status=MagickFalse;
3299 break;
3300 }
3301 case 'o':
3302 case 'O':
3303 {
3304 if (LocaleCompare("offset",keyword) == 0)
3305 {
3306 (void) GetNextToken(q,&q,extent,token);
3307 break;
3308 }
3309 if (LocaleCompare("opacity",keyword) == 0)
3310 {
3311 double
3312 opacity;
3313
3314 (void) GetNextToken(q,&q,extent,token);
3315 if (graphic_context[n]->clip_path != MagickFalse)
3316 break;
3317 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3318 opacity=MagickMin(MagickMax(factor*
3319 GetDrawValue(token,&next_token),0.0),1.0);
3320 if (token == next_token)
3321 ThrowPointExpectedException(token,exception);
3322 if (graphic_context[n]->compliance == SVGCompliance)
3323 {
3324 graphic_context[n]->fill_alpha*=opacity;
3325 graphic_context[n]->stroke_alpha*=opacity;
3326 }
3327 else
3328 {
3329 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3330 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3331 }
3332 if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3333 {
3334 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3335 graphic_context[n]->stroke.alpha=
3336 graphic_context[n]->stroke_alpha;
3337 }
3338 else
3339 {
3340 graphic_context[n]->fill.alpha=(MagickRealType)
3341 ClampToQuantum((double) QuantumRange*opacity);
3342 graphic_context[n]->stroke.alpha=(MagickRealType)
3343 ClampToQuantum((double) QuantumRange*opacity);
3344 }
3345 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3346 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3347 break;
3348 }
3349 status=MagickFalse;
3350 break;
3351 }
3352 case 'p':
3353 case 'P':
3354 {
3355 if (LocaleCompare("path",keyword) == 0)
3356 {
3357 primitive_type=PathPrimitive;
3358 break;
3359 }
3360 if (LocaleCompare("point",keyword) == 0)
3361 {
3362 primitive_type=PointPrimitive;
3363 break;
3364 }
3365 if (LocaleCompare("polyline",keyword) == 0)
3366 {
3367 primitive_type=PolylinePrimitive;
3368 break;
3369 }
3370 if (LocaleCompare("polygon",keyword) == 0)
3371 {
3372 primitive_type=PolygonPrimitive;
3373 break;
3374 }
3375 if (LocaleCompare("pop",keyword) == 0)
3376 {
3377 if (GetNextToken(q,&q,extent,token) < 1)
3378 break;
3379 if (LocaleCompare("class",token) == 0)
3380 break;
3381 if (LocaleCompare("clip-path",token) == 0)
3382 break;
3383 if (LocaleCompare("defs",token) == 0)
3384 {
3385 defsDepth--;
3386 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3387 MagickTrue;
3388 break;
3389 }
3390 if (LocaleCompare("gradient",token) == 0)
3391 break;
3392 if (LocaleCompare("graphic-context",token) == 0)
3393 {
3394 if (n <= 0)
3395 {
3396 (void) ThrowMagickException(exception,GetMagickModule(),
3397 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
3398 status=MagickFalse;
3399 n=0;
3400 break;
3401 }
3402 if ((graphic_context[n]->clip_mask != (char *) NULL) &&
3403 (graphic_context[n]->compliance != SVGCompliance))
3404 if (LocaleCompare(graphic_context[n]->clip_mask,
3405 graphic_context[n-1]->clip_mask) != 0)
3406 status=SetImageMask(image,WritePixelMask,(Image *) NULL,
3407 exception);
3408 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3409 n--;
3410 break;
3411 }
3412 if (LocaleCompare("mask",token) == 0)
3413 break;
3414 if (LocaleCompare("pattern",token) == 0)
3415 break;
3416 if (LocaleCompare("symbol",token) == 0)
3417 {
3418 symbolDepth--;
3419 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3420 MagickTrue;
3421 break;
3422 }
3423 status=MagickFalse;
3424 break;
3425 }
3426 if (LocaleCompare("push",keyword) == 0)
3427 {
3428 if (GetNextToken(q,&q,extent,token) < 1)
3429 break;
3430 if (LocaleCompare("class",token) == 0)
3431 {
3432 /*
3433 Class context.
3434 */
3435 for (p=q; *q != '\0'; )
3436 {
3437 if (GetNextToken(q,&q,extent,token) < 1)
3438 break;
3439 if (LocaleCompare(token,"pop") != 0)
3440 continue;
3441 (void) GetNextToken(q,(const char **) NULL,extent,token);
3442 if (LocaleCompare(token,"class") != 0)
3443 continue;
3444 break;
3445 }
3446 (void) GetNextToken(q,&q,extent,token);
3447 break;
3448 }
3449 if (LocaleCompare("clip-path",token) == 0)
3450 {
3451 (void) GetNextToken(q,&q,extent,token);
3452 for (p=q; *q != '\0'; )
3453 {
3454 if (GetNextToken(q,&q,extent,token) < 1)
3455 break;
3456 if (LocaleCompare(token,"pop") != 0)
3457 continue;
3458 (void) GetNextToken(q,(const char **) NULL,extent,token);
3459 if (LocaleCompare(token,"clip-path") != 0)
3460 continue;
3461 break;
3462 }
3463 if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
3464 {
3465 status=MagickFalse;
3466 break;
3467 }
3468 (void) GetNextToken(q,&q,extent,token);
3469 break;
3470 }
3471 if (LocaleCompare("defs",token) == 0)
3472 {
3473 defsDepth++;
3474 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3475 MagickTrue;
3476 break;
3477 }
3478 if (LocaleCompare("gradient",token) == 0)
3479 {
3480 char
3481 key[2*MagickPathExtent],
3482 name[MagickPathExtent],
3483 type[MagickPathExtent];
3484
3486 segment;
3487
3488 (void) GetNextToken(q,&q,extent,token);
3489 (void) CopyMagickString(name,token,MagickPathExtent);
3490 (void) GetNextToken(q,&q,extent,token);
3491 (void) CopyMagickString(type,token,MagickPathExtent);
3492 (void) GetNextToken(q,&q,extent,token);
3493 segment.x1=GetDrawValue(token,&next_token);
3494 if (token == next_token)
3495 ThrowPointExpectedException(token,exception);
3496 (void) GetNextToken(q,&q,extent,token);
3497 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3498 ThrowPointExpectedException(token,exception);
3499 if (*token == ',')
3500 (void) GetNextToken(q,&q,extent,token);
3501 segment.y1=GetDrawValue(token,&next_token);
3502 if (token == next_token)
3503 ThrowPointExpectedException(token,exception);
3504 (void) GetNextToken(q,&q,extent,token);
3505 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3506 ThrowPointExpectedException(token,exception);
3507 if (*token == ',')
3508 (void) GetNextToken(q,&q,extent,token);
3509 segment.x2=GetDrawValue(token,&next_token);
3510 if (token == next_token)
3511 ThrowPointExpectedException(token,exception);
3512 (void) GetNextToken(q,&q,extent,token);
3513 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3514 ThrowPointExpectedException(token,exception);
3515 if (*token == ',')
3516 (void) GetNextToken(q,&q,extent,token);
3517 segment.y2=GetDrawValue(token,&next_token);
3518 if (token == next_token)
3519 ThrowPointExpectedException(token,exception);
3520 if (LocaleCompare(type,"radial") == 0)
3521 {
3522 (void) GetNextToken(q,&q,extent,token);
3523 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3524 ThrowPointExpectedException(token,exception);
3525 if (*token == ',')
3526 (void) GetNextToken(q,&q,extent,token);
3527 }
3528 for (p=q; *q != '\0'; )
3529 {
3530 if (GetNextToken(q,&q,extent,token) < 1)
3531 break;
3532 if (LocaleCompare(token,"pop") != 0)
3533 continue;
3534 (void) GetNextToken(q,(const char **) NULL,extent,token);
3535 if (LocaleCompare(token,"gradient") != 0)
3536 continue;
3537 break;
3538 }
3539 if ((q == (char *) NULL) || (*q == '\0') ||
3540 (p == (char *) NULL) || ((q-4) < p) ||
3541 ((size_t) (q-p+4+1) > extent))
3542 {
3543 status=MagickFalse;
3544 break;
3545 }
3546 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3547 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3548 graphic_context[n]->affine.ry*segment.y1+
3549 graphic_context[n]->affine.tx;
3550 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3551 graphic_context[n]->affine.sy*segment.y1+
3552 graphic_context[n]->affine.ty;
3553 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3554 graphic_context[n]->affine.ry*segment.y2+
3555 graphic_context[n]->affine.tx;
3556 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3557 graphic_context[n]->affine.sy*segment.y2+
3558 graphic_context[n]->affine.ty;
3559 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3560 (void) SetImageArtifact(image,key,token);
3561 (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name);
3562 (void) SetImageArtifact(image,key,type);
3563 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3564 name);
3565 (void) FormatLocaleString(geometry,MagickPathExtent,
3566 "%gx%g%+.15g%+.15g",
3567 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
3568 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
3569 bounds.x1,bounds.y1);
3570 (void) SetImageArtifact(image,key,geometry);
3571 (void) GetNextToken(q,&q,extent,token);
3572 break;
3573 }
3574 if (LocaleCompare("graphic-context",token) == 0)
3575 {
3576 n++;
3577 graphic_context=(DrawInfo **) ResizeQuantumMemory(
3578 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
3579 if (graphic_context == (DrawInfo **) NULL)
3580 {
3581 (void) ThrowMagickException(exception,GetMagickModule(),
3582 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3583 image->filename);
3584 status=MagickFalse;
3585 break;
3586 }
3587 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
3588 graphic_context[n-1]);
3589 if (*q == '"')
3590 {
3591 (void) GetNextToken(q,&q,extent,token);
3592 (void) CloneString(&graphic_context[n]->id,token);
3593 }
3594 if (n >= MaxVectorRecursionDepth)
3595 {
3596 (void) ThrowMagickException(exception,GetMagickModule(),
3597 DrawError,"VectorGraphicsNestedTooDeeply","`%s'",
3598 image->filename);
3599 status=MagickFalse;
3600 }
3601 break;
3602 }
3603 if (LocaleCompare("mask",token) == 0)
3604 {
3605 (void) GetNextToken(q,&q,extent,token);
3606 break;
3607 }
3608 if (LocaleCompare("pattern",token) == 0)
3609 {
3610 char
3611 key[2*MagickPathExtent],
3612 name[MagickPathExtent];
3613
3615 region;
3616
3617 (void) GetNextToken(q,&q,extent,token);
3618 (void) CopyMagickString(name,token,MagickPathExtent);
3619 (void) GetNextToken(q,&q,extent,token);
3620 region.x=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3621 &next_token)-0.5));
3622 if (token == next_token)
3623 ThrowPointExpectedException(token,exception);
3624 (void) GetNextToken(q,&q,extent,token);
3625 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3626 ThrowPointExpectedException(token,exception);
3627 if (*token == ',')
3628 (void) GetNextToken(q,&q,extent,token);
3629 region.y=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3630 &next_token)-0.5));
3631 if (token == next_token)
3632 ThrowPointExpectedException(token,exception);
3633 (void) GetNextToken(q,&q,extent,token);
3634 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3635 ThrowPointExpectedException(token,exception);
3636 if (*token == ',')
3637 (void) GetNextToken(q,&q,extent,token);
3638 region.width=CastDoubleToSizeT(floor(GetDrawValue(token,
3639 &next_token)+0.5));
3640 if (token == next_token)
3641 ThrowPointExpectedException(token,exception);
3642 (void) GetNextToken(q,&q,extent,token);
3643 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3644 ThrowPointExpectedException(token,exception);
3645 if (*token == ',')
3646 (void) GetNextToken(q,&q,extent,token);
3647 region.height=CastDoubleToSizeT(GetDrawValue(token,
3648 &next_token)+0.5);
3649 if (token == next_token)
3650 ThrowPointExpectedException(token,exception);
3651 for (p=q; *q != '\0'; )
3652 {
3653 if (GetNextToken(q,&q,extent,token) < 1)
3654 break;
3655 if (LocaleCompare(token,"pop") != 0)
3656 continue;
3657 (void) GetNextToken(q,(const char **) NULL,extent,token);
3658 if (LocaleCompare(token,"pattern") != 0)
3659 continue;
3660 break;
3661 }
3662 if ((q == (char *) NULL) || (p == (char *) NULL) ||
3663 ((q-4) < p) || ((size_t) (q-p+4+1) > extent))
3664 {
3665 status=MagickFalse;
3666 break;
3667 }
3668 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3669 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3670 (void) SetImageArtifact(image,key,token);
3671 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3672 name);
3673 (void) FormatLocaleString(geometry,MagickPathExtent,
3674 "%.20gx%.20g%+.20g%+.20g",(double) region.width,(double)
3675 region.height,(double) region.x,(double) region.y);
3676 (void) SetImageArtifact(image,key,geometry);
3677 (void) GetNextToken(q,&q,extent,token);
3678 break;
3679 }
3680 if (LocaleCompare("symbol",token) == 0)
3681 {
3682 symbolDepth++;
3683 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3684 MagickTrue;
3685 break;
3686 }
3687 status=MagickFalse;
3688 break;
3689 }
3690 status=MagickFalse;
3691 break;
3692 }
3693 case 'r':
3694 case 'R':
3695 {
3696 if (LocaleCompare("rectangle",keyword) == 0)
3697 {
3698 primitive_type=RectanglePrimitive;
3699 break;
3700 }
3701 if (LocaleCompare("rotate",keyword) == 0)
3702 {
3703 (void) GetNextToken(q,&q,extent,token);
3704 angle=GetDrawValue(token,&next_token);
3705 if (token == next_token)
3706 ThrowPointExpectedException(token,exception);
3707 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
3708 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
3709 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
3710 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
3711 break;
3712 }
3713 if (LocaleCompare("roundRectangle",keyword) == 0)
3714 {
3715 primitive_type=RoundRectanglePrimitive;
3716 break;
3717 }
3718 status=MagickFalse;
3719 break;
3720 }
3721 case 's':
3722 case 'S':
3723 {
3724 if (LocaleCompare("scale",keyword) == 0)
3725 {
3726 (void) GetNextToken(q,&q,extent,token);
3727 affine.sx=GetDrawValue(token,&next_token);
3728 if (token == next_token)
3729 ThrowPointExpectedException(token,exception);
3730 (void) GetNextToken(q,&q,extent,token);
3731 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3732 ThrowPointExpectedException(token,exception);
3733 if (*token == ',')
3734 (void) GetNextToken(q,&q,extent,token);
3735 affine.sy=GetDrawValue(token,&next_token);
3736 if (token == next_token)
3737 ThrowPointExpectedException(token,exception);
3738 break;
3739 }
3740 if (LocaleCompare("skewX",keyword) == 0)
3741 {
3742 (void) GetNextToken(q,&q,extent,token);
3743 angle=GetDrawValue(token,&next_token);
3744 if (token == next_token)
3745 ThrowPointExpectedException(token,exception);
3746 affine.ry=sin(DegreesToRadians(angle));
3747 break;
3748 }
3749 if (LocaleCompare("skewY",keyword) == 0)
3750 {
3751 (void) GetNextToken(q,&q,extent,token);
3752 angle=GetDrawValue(token,&next_token);
3753 if (token == next_token)
3754 ThrowPointExpectedException(token,exception);
3755 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
3756 break;
3757 }
3758 if (LocaleCompare("stop-color",keyword) == 0)
3759 {
3760 PixelInfo
3761 stop_color;
3762
3763 number_stops++;
3764 if (number_stops == 1)
3765 stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops));
3766 else
3767 if (number_stops > 2)
3768 stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
3769 sizeof(*stops));
3770 if (stops == (StopInfo *) NULL)
3771 {
3772 (void) ThrowMagickException(exception,GetMagickModule(),
3773 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3774 image->filename);
3775 status=MagickFalse;
3776 break;
3777 }
3778 (void) GetNextToken(q,&q,extent,token);
3779 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3780 &stop_color,exception);
3781 stops[number_stops-1].color=stop_color;
3782 (void) GetNextToken(q,&q,extent,token);
3783 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3784 stops[number_stops-1].offset=factor*GetDrawValue(token,
3785 &next_token);
3786 if (token == next_token)
3787 ThrowPointExpectedException(token,exception);
3788 break;
3789 }
3790 if (LocaleCompare("stroke",keyword) == 0)
3791 {
3792 const char
3793 *mvg_class;
3794
3795 (void) GetNextToken(q,&q,extent,token);
3796 if (graphic_context[n]->clip_path != MagickFalse)
3797 break;
3798 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
3799 if (mvg_class != (const char *) NULL)
3800 {
3801 (void) DrawPatternPath(image,draw_info,mvg_class,
3802 &graphic_context[n]->stroke_pattern,exception);
3803 break;
3804 }
3805 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3806 if (GetImageArtifact(image,pattern) != (const char *) NULL)
3807 {
3808 (void) DrawPatternPath(image,draw_info,token,
3809 &graphic_context[n]->stroke_pattern,exception);
3810 break;
3811 }
3812 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3813 &graphic_context[n]->stroke,exception);
3814 if (graphic_context[n]->stroke_alpha != (double) OpaqueAlpha)
3815 graphic_context[n]->stroke.alpha=
3816 graphic_context[n]->stroke_alpha;
3817 break;
3818 }
3819 if (LocaleCompare("stroke-antialias",keyword) == 0)
3820 {
3821 (void) GetNextToken(q,&q,extent,token);
3822 graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
3823 MagickTrue : MagickFalse;
3824 break;
3825 }
3826 if (LocaleCompare("stroke-dasharray",keyword) == 0)
3827 {
3828 if (graphic_context[n]->dash_pattern != (double *) NULL)
3829 graphic_context[n]->dash_pattern=(double *)
3830 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
3831 if (IsValidPoint(q) != MagickFalse)
3832 {
3833 const char
3834 *r;
3835
3836 r=q;
3837 (void) GetNextToken(r,&r,extent,token);
3838 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3839 ThrowPointExpectedException(token,exception);
3840 if (*token == ',')
3841 (void) GetNextToken(r,&r,extent,token);
3842 for (x=0; IsValidPoint(token) != MagickFalse; x++)
3843 {
3844 (void) GetNextToken(r,&r,extent,token);
3845 if (*token == ',')
3846 (void) GetNextToken(r,&r,extent,token);
3847 }
3848 graphic_context[n]->dash_pattern=(double *)
3849 AcquireQuantumMemory((size_t) (2*x+2),
3850 sizeof(*graphic_context[n]->dash_pattern));
3851 if (graphic_context[n]->dash_pattern == (double *) NULL)
3852 {
3853 (void) ThrowMagickException(exception,GetMagickModule(),
3854 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3855 image->filename);
3856 status=MagickFalse;
3857 break;
3858 }
3859 (void) memset(graphic_context[n]->dash_pattern,0,(size_t)
3860 (2*x+2)*sizeof(*graphic_context[n]->dash_pattern));
3861 for (j=0; j < x; j++)
3862 {
3863 (void) GetNextToken(q,&q,extent,token);
3864 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3865 ThrowPointExpectedException(token,exception);
3866 if (*token == ',')
3867 (void) GetNextToken(q,&q,extent,token);
3868 graphic_context[n]->dash_pattern[j]=GetDrawValue(token,
3869 &next_token);
3870 if (token == next_token)
3871 ThrowPointExpectedException(token,exception);
3872 if (graphic_context[n]->dash_pattern[j] <= 0.0)
3873 status=MagickFalse;
3874 }
3875 if (((x & 0x01) != 0) && (j == x))
3876 for ( ; j < (2*x); j++)
3877 graphic_context[n]->dash_pattern[j]=
3878 graphic_context[n]->dash_pattern[j-x];
3879 graphic_context[n]->dash_pattern[j]=0.0;
3880 break;
3881 }
3882 (void) GetNextToken(q,&q,extent,token);
3883 break;
3884 }
3885 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
3886 {
3887 (void) GetNextToken(q,&q,extent,token);
3888 graphic_context[n]->dash_offset=GetDrawValue(token,&next_token);
3889 if (token == next_token)
3890 ThrowPointExpectedException(token,exception);
3891 break;
3892 }
3893 if (LocaleCompare("stroke-linecap",keyword) == 0)
3894 {
3895 ssize_t
3896 linecap;
3897
3898 (void) GetNextToken(q,&q,extent,token);
3899 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
3900 if (linecap == -1)
3901 {
3902 status=MagickFalse;
3903 break;
3904 }
3905 graphic_context[n]->linecap=(LineCap) linecap;
3906 break;
3907 }
3908 if (LocaleCompare("stroke-linejoin",keyword) == 0)
3909 {
3910 ssize_t
3911 linejoin;
3912
3913 (void) GetNextToken(q,&q,extent,token);
3914 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
3915 token);
3916 if (linejoin == -1)
3917 {
3918 status=MagickFalse;
3919 break;
3920 }
3921 graphic_context[n]->linejoin=(LineJoin) linejoin;
3922 break;
3923 }
3924 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
3925 {
3926 (void) GetNextToken(q,&q,extent,token);
3927 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
3928 break;
3929 }
3930 if (LocaleCompare("stroke-opacity",keyword) == 0)
3931 {
3932 double
3933 opacity;
3934
3935 (void) GetNextToken(q,&q,extent,token);
3936 if (graphic_context[n]->clip_path != MagickFalse)
3937 break;
3938 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3939 opacity=MagickMin(MagickMax(factor*GetDrawValue(token,&next_token),
3940 0.0),1.0);
3941 if (token == next_token)
3942 ThrowPointExpectedException(token,exception);
3943 if (graphic_context[n]->compliance == SVGCompliance)
3944 graphic_context[n]->stroke_alpha*=opacity;
3945 else
3946 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3947 if (graphic_context[n]->stroke.alpha != (double) TransparentAlpha)
3948 graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3949 else
3950 graphic_context[n]->stroke.alpha=(MagickRealType)
3951 ClampToQuantum((double) QuantumRange*opacity);
3952 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3953 break;
3954 }
3955 if (LocaleCompare("stroke-width",keyword) == 0)
3956 {
3957 (void) GetNextToken(q,&q,extent,token);
3958 if (graphic_context[n]->clip_path != MagickFalse)
3959 break;
3960 graphic_context[n]->stroke_width=GetDrawValue(token,&next_token);
3961 if ((token == next_token) ||
3962 (graphic_context[n]->stroke_width < 0.0))
3963 ThrowPointExpectedException(token,exception);
3964 break;
3965 }
3966 status=MagickFalse;
3967 break;
3968 }
3969 case 't':
3970 case 'T':
3971 {
3972 if (LocaleCompare("text",keyword) == 0)
3973 {
3974 primitive_type=TextPrimitive;
3975 cursor=0.0;
3976 break;
3977 }
3978 if (LocaleCompare("text-align",keyword) == 0)
3979 {
3980 ssize_t
3981 align;
3982
3983 (void) GetNextToken(q,&q,extent,token);
3984 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3985 if (align == -1)
3986 {
3987 status=MagickFalse;
3988 break;
3989 }
3990 graphic_context[n]->align=(AlignType) align;
3991 break;
3992 }
3993 if (LocaleCompare("text-anchor",keyword) == 0)
3994 {
3995 ssize_t
3996 align;
3997
3998 (void) GetNextToken(q,&q,extent,token);
3999 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
4000 if (align == -1)
4001 {
4002 status=MagickFalse;
4003 break;
4004 }
4005 graphic_context[n]->align=(AlignType) align;
4006 break;
4007 }
4008 if (LocaleCompare("text-antialias",keyword) == 0)
4009 {
4010 (void) GetNextToken(q,&q,extent,token);
4011 graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
4012 MagickTrue : MagickFalse;
4013 break;
4014 }
4015 if (LocaleCompare("text-undercolor",keyword) == 0)
4016 {
4017 (void) GetNextToken(q,&q,extent,token);
4018 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
4019 &graphic_context[n]->undercolor,exception);
4020 break;
4021 }
4022 if (LocaleCompare("translate",keyword) == 0)
4023 {
4024 (void) GetNextToken(q,&q,extent,token);
4025 affine.tx=GetDrawValue(token,&next_token);
4026 if (token == next_token)
4027 ThrowPointExpectedException(token,exception);
4028 (void) GetNextToken(q,&q,extent,token);
4029 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4030 ThrowPointExpectedException(token,exception);
4031 if (*token == ',')
4032 (void) GetNextToken(q,&q,extent,token);
4033 affine.ty=GetDrawValue(token,&next_token);
4034 if (token == next_token)
4035 ThrowPointExpectedException(token,exception);
4036 cursor=0.0;
4037 break;
4038 }
4039 status=MagickFalse;
4040 break;
4041 }
4042 case 'u':
4043 case 'U':
4044 {
4045 if (LocaleCompare("use",keyword) == 0)
4046 {
4047 const char
4048 *use;
4049
4050 /*
4051 Get a macro from the MVG document, and "use" it here.
4052 */
4053 (void) GetNextToken(q,&q,extent,token);
4054 use=(const char *) GetValueFromSplayTree(macros,token);
4055 if (use != (const char *) NULL)
4056 {
4057 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4058 if (draw_info->macro_expansion != (size_t *) NULL)
4059 {
4060 if (*draw_info->macro_expansion >= MacroExpansionLimit)
4061 {
4062 (void) ThrowMagickException(exception,GetMagickModule(),
4063 DrawError,"NonconformingDrawingPrimitiveDefinition",
4064 "`%s'",token);
4065 clone_info=DestroyDrawInfo(clone_info);
4066 return(MagickFalse);
4067 }
4068 (*draw_info->macro_expansion)++;
4069 }
4070 (void) CloneString(&clone_info->primitive,use);
4071 status=RenderMVGContent(image,clone_info,depth+1,exception);
4072 clone_info=DestroyDrawInfo(clone_info);
4073 }
4074 break;
4075 }
4076 status=MagickFalse;
4077 break;
4078 }
4079 case 'v':
4080 case 'V':
4081 {
4082 if (LocaleCompare("viewbox",keyword) == 0)
4083 {
4084 (void) GetNextToken(q,&q,extent,token);
4085 graphic_context[n]->viewbox.x=CastDoubleToSsizeT(ceil(
4086 GetDrawValue(token,&next_token)-0.5));
4087 if (token == next_token)
4088 ThrowPointExpectedException(token,exception);
4089 (void) GetNextToken(q,&q,extent,token);
4090 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4091 ThrowPointExpectedException(token,exception);
4092 if (*token == ',')
4093 (void) GetNextToken(q,&q,extent,token);
4094 graphic_context[n]->viewbox.y=CastDoubleToSsizeT(
4095 ceil(GetDrawValue(token,&next_token)-0.5));
4096 if (token == next_token)
4097 ThrowPointExpectedException(token,exception);
4098 (void) GetNextToken(q,&q,extent,token);
4099 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4100 ThrowPointExpectedException(token,exception);
4101 if (*token == ',')
4102 (void) GetNextToken(q,&q,extent,token);
4103 graphic_context[n]->viewbox.width=CastDoubleToSizeT(floor(
4104 GetDrawValue(token,&next_token)+0.5));
4105 if (token == next_token)
4106 ThrowPointExpectedException(token,exception);
4107 (void) GetNextToken(q,&q,extent,token);
4108 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4109 ThrowPointExpectedException(token,exception);
4110 if (*token == ',')
4111 (void) GetNextToken(q,&q,extent,token);
4112 graphic_context[n]->viewbox.height=CastDoubleToSizeT(floor(
4113 GetDrawValue(token,&next_token)+0.5));
4114 if (token == next_token)
4115 ThrowPointExpectedException(token,exception);
4116 break;
4117 }
4118 status=MagickFalse;
4119 break;
4120 }
4121 case 'w':
4122 case 'W':
4123 {
4124 if (LocaleCompare("word-spacing",keyword) == 0)
4125 {
4126 (void) GetNextToken(q,&q,extent,token);
4127 graphic_context[n]->interword_spacing=GetDrawValue(token,
4128 &next_token);
4129 if (token == next_token)
4130 ThrowPointExpectedException(token,exception);
4131 break;
4132 }
4133 status=MagickFalse;
4134 break;
4135 }
4136 default:
4137 {
4138 status=MagickFalse;
4139 break;
4140 }
4141 }
4142 if (status == MagickFalse)
4143 break;
4144 if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
4145 (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
4146 (fabs(affine.sy-1.0) >= MagickEpsilon) ||
4147 (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
4148 {
4149 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
4150 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
4151 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
4152 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
4153 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
4154 current.tx;
4155 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
4156 current.ty;
4157 }
4158 if (primitive_type == UndefinedPrimitive)
4159 {
4160 if (*q == '\0')
4161 {
4162 if (number_stops > 1)
4163 {
4164 GradientType
4165 type;
4166
4167 type=LinearGradient;
4168 if (draw_info->gradient.type == RadialGradient)
4169 type=RadialGradient;
4170 (void) GradientImage(image,type,PadSpread,stops,number_stops,
4171 exception);
4172 }
4173 if (number_stops > 0)
4174 stops=(StopInfo *) RelinquishMagickMemory(stops);
4175 }
4176 if ((draw_info->debug != MagickFalse) && (q > p))
4177 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int)
4178 (q-p-1),p);
4179 continue;
4180 }
4181 /*
4182 Parse the primitive attributes.
4183 */
4184 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4185 if (primitive_info[i].text != (char *) NULL)
4186 primitive_info[i].text=DestroyString(primitive_info[i].text);
4187 i=0;
4188 mvg_info.offset=i;
4189 j=0;
4190 primitive_info[0].primitive=primitive_type;
4191 primitive_info[0].point.x=0.0;
4192 primitive_info[0].point.y=0.0;
4193 primitive_info[0].coordinates=0;
4194 primitive_info[0].method=FloodfillMethod;
4195 primitive_info[0].closed_subpath=MagickFalse;
4196 for (x=0; *q != '\0'; x++)
4197 {
4198 /*
4199 Define points.
4200 */
4201 if (IsValidPoint(q) == MagickFalse)
4202 break;
4203 (void) GetNextToken(q,&q,extent,token);
4204 point.x=GetDrawValue(token,&next_token);
4205 if (token == next_token)
4206 ThrowPointExpectedException(token,exception);
4207 (void) GetNextToken(q,&q,extent,token);
4208 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4209 ThrowPointExpectedException(token,exception);
4210 if (*token == ',')
4211 (void) GetNextToken(q,&q,extent,token);
4212 point.y=GetDrawValue(token,&next_token);
4213 if (token == next_token)
4214 ThrowPointExpectedException(token,exception);
4215 (void) GetNextToken(q,(const char **) NULL,extent,token);
4216 if (*token == ',')
4217 (void) GetNextToken(q,&q,extent,token);
4218 primitive_info[i].primitive=primitive_type;
4219 primitive_info[i].point=point;
4220 primitive_info[i].coordinates=0;
4221 primitive_info[i].method=FloodfillMethod;
4222 primitive_info[i].closed_subpath=MagickFalse;
4223 i++;
4224 mvg_info.offset=i;
4225 if (i < (ssize_t) number_points)
4226 continue;
4227 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4228 number_points);
4229 primitive_info=(*mvg_info.primitive_info);
4230 if (status == MagickFalse)
4231 break;
4232 }
4233 if (status == MagickFalse)
4234 break;
4235 if (primitive_info[j].text != (char *) NULL)
4236 primitive_info[j].text=DestroyString(primitive_info[j].text);
4237 primitive_info[j].primitive=primitive_type;
4238 primitive_info[j].coordinates=(size_t) x;
4239 primitive_info[j].method=FloodfillMethod;
4240 primitive_info[j].closed_subpath=MagickFalse;
4241 /*
4242 Circumscribe primitive within a circle.
4243 */
4244 bounds.x1=primitive_info[j].point.x;
4245 bounds.y1=primitive_info[j].point.y;
4246 bounds.x2=primitive_info[j].point.x;
4247 bounds.y2=primitive_info[j].point.y;
4248 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
4249 {
4250 point=primitive_info[j+k].point;
4251 if (point.x < bounds.x1)
4252 bounds.x1=point.x;
4253 if (point.y < bounds.y1)
4254 bounds.y1=point.y;
4255 if (point.x > bounds.x2)
4256 bounds.x2=point.x;
4257 if (point.y > bounds.y2)
4258 bounds.y2=point.y;
4259 }
4260 /*
4261 Speculate how many points our primitive might consume.
4262 */
4263 coordinates=(double) primitive_info[j].coordinates;
4264 switch (primitive_type)
4265 {
4266 case RectanglePrimitive:
4267 {
4268 coordinates*=5.0;
4269 break;
4270 }
4271 case RoundRectanglePrimitive:
4272 {
4273 double
4274 alpha,
4275 beta,
4276 radius;
4277
4278 alpha=bounds.x2-bounds.x1;
4279 beta=bounds.y2-bounds.y1;
4280 radius=hypot(alpha,beta);
4281 coordinates*=5.0;
4282 coordinates+=2.0*((size_t) ceil((double) MagickPI*radius))+6.0*
4283 BezierQuantum+360.0;
4284 break;
4285 }
4286 case BezierPrimitive:
4287 {
4288 coordinates=(BezierQuantum*(double) primitive_info[j].coordinates);
4289 break;
4290 }
4291 case PathPrimitive:
4292 {
4293 char
4294 *s,
4295 *t;
4296
4297 (void) GetNextToken(q,&q,extent,token);
4298 coordinates=1.0;
4299 t=token;
4300 for (s=token; *s != '\0'; s=t)
4301 {
4302 double
4303 value;
4304
4305 value=GetDrawValue(s,&t);
4306 (void) value;
4307 if (s == t)
4308 {
4309 t++;
4310 continue;
4311 }
4312 coordinates++;
4313 }
4314 for (s=token; *s != '\0'; s++)
4315 if (strspn(s,"AaCcQqSsTt") != 0)
4316 coordinates+=(20.0*BezierQuantum)+360.0;
4317 break;
4318 }
4319 default:
4320 break;
4321 }
4322 if (status == MagickFalse)
4323 break;
4324 if (((size_t) (i+coordinates)) >= number_points)
4325 {
4326 /*
4327 Resize based on speculative points required by primitive.
4328 */
4329 number_points+=(size_t) coordinates+1;
4330 if (number_points < (size_t) coordinates)
4331 {
4332 (void) ThrowMagickException(exception,GetMagickModule(),
4333 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4334 image->filename);
4335 status=MagickFalse;
4336 break;
4337 }
4338 mvg_info.offset=i;
4339 status&=CheckPrimitiveExtent(&mvg_info,(double) number_points+
4340 (double) coordinates+1.0);
4341 primitive_info=(*mvg_info.primitive_info);
4342 if (status == MagickFalse)
4343 break;
4344 }
4345 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,
4346 PrimitiveExtentPad);
4347 primitive_info=(*mvg_info.primitive_info);
4348 if (status == MagickFalse)
4349 break;
4350 mvg_info.offset=j;
4351 switch (primitive_type)
4352 {
4353 case PointPrimitive:
4354 default:
4355 {
4356 if (primitive_info[j].coordinates != 1)
4357 {
4358 status=MagickFalse;
4359 break;
4360 }
4361 status&=(MagickStatusType) TracePoint(primitive_info+j,
4362 primitive_info[j].point);
4363 primitive_info=(*mvg_info.primitive_info);
4364 if (status == MagickFalse)
4365 break;
4366 i=j+(ssize_t) primitive_info[j].coordinates;
4367 break;
4368 }
4369 case LinePrimitive:
4370 {
4371 if (primitive_info[j].coordinates != 2)
4372 {
4373 status=MagickFalse;
4374 break;
4375 }
4376 status&=(MagickStatusType) TraceLine(primitive_info+j,
4377 primitive_info[j].point,primitive_info[j+1].point);
4378 primitive_info=(*mvg_info.primitive_info);
4379 if (status == MagickFalse)
4380 break;
4381 i=j+(ssize_t) primitive_info[j].coordinates;
4382 break;
4383 }
4384 case RectanglePrimitive:
4385 {
4386 if (primitive_info[j].coordinates != 2)
4387 {
4388 status=MagickFalse;
4389 break;
4390 }
4391 status&=(MagickStatusType) TraceRectangle(primitive_info+j,
4392 primitive_info[j].point,primitive_info[j+1].point);
4393 primitive_info=(*mvg_info.primitive_info);
4394 if (status == MagickFalse)
4395 break;
4396 i=j+(ssize_t) primitive_info[j].coordinates;
4397 break;
4398 }
4399 case RoundRectanglePrimitive:
4400 {
4401 if (primitive_info[j].coordinates != 3)
4402 {
4403 status=MagickFalse;
4404 break;
4405 }
4406 if ((primitive_info[j+2].point.x < 0.0) ||
4407 (primitive_info[j+2].point.y < 0.0))
4408 {
4409 status=MagickFalse;
4410 break;
4411 }
4412 if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
4413 {
4414 status=MagickFalse;
4415 break;
4416 }
4417 if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
4418 {
4419 status=MagickFalse;
4420 break;
4421 }
4422 status&=(MagickStatusType) TraceRoundRectangle(&mvg_info,
4423 primitive_info[j].point,primitive_info[j+1].point,
4424 primitive_info[j+2].point);
4425 primitive_info=(*mvg_info.primitive_info);
4426 if (status == MagickFalse)
4427 break;
4428 i=j+(ssize_t) primitive_info[j].coordinates;
4429 break;
4430 }
4431 case ArcPrimitive:
4432 {
4433 if (primitive_info[j].coordinates != 3)
4434 {
4435 status=MagickFalse;
4436 break;
4437 }
4438 status&=(MagickStatusType) TraceArc(&mvg_info,primitive_info[j].point,
4439 primitive_info[j+1].point,primitive_info[j+2].point);
4440 primitive_info=(*mvg_info.primitive_info);
4441 if (status == MagickFalse)
4442 break;
4443 i=j+(ssize_t) primitive_info[j].coordinates;
4444 break;
4445 }
4446 case EllipsePrimitive:
4447 {
4448 if (primitive_info[j].coordinates != 3)
4449 {
4450 status=MagickFalse;
4451 break;
4452 }
4453 if ((primitive_info[j+1].point.x < 0.0) ||
4454 (primitive_info[j+1].point.y < 0.0))
4455 {
4456 status=MagickFalse;
4457 break;
4458 }
4459 status&=(MagickStatusType) TraceEllipse(&mvg_info,
4460 primitive_info[j].point,primitive_info[j+1].point,
4461 primitive_info[j+2].point);
4462 primitive_info=(*mvg_info.primitive_info);
4463 if (status == MagickFalse)
4464 break;
4465 i=j+(ssize_t) primitive_info[j].coordinates;
4466 break;
4467 }
4468 case CirclePrimitive:
4469 {
4470 if (primitive_info[j].coordinates != 2)
4471 {
4472 status=MagickFalse;
4473 break;
4474 }
4475 status&=(MagickStatusType) TraceCircle(&mvg_info,
4476 primitive_info[j].point,primitive_info[j+1].point);
4477 primitive_info=(*mvg_info.primitive_info);
4478 if (status == MagickFalse)
4479 break;
4480 i=j+(ssize_t) primitive_info[j].coordinates;
4481 break;
4482 }
4483 case PolylinePrimitive:
4484 {
4485 if (primitive_info[j].coordinates < 1)
4486 {
4487 status=MagickFalse;
4488 break;
4489 }
4490 break;
4491 }
4492 case PolygonPrimitive:
4493 {
4494 if (primitive_info[j].coordinates < 3)
4495 {
4496 status=MagickFalse;
4497 break;
4498 }
4499 primitive_info[i]=primitive_info[j];
4500 primitive_info[i].coordinates=0;
4501 primitive_info[j].coordinates++;
4502 primitive_info[j].closed_subpath=MagickTrue;
4503 i++;
4504 break;
4505 }
4506 case BezierPrimitive:
4507 {
4508 if (primitive_info[j].coordinates < 3)
4509 {
4510 status=MagickFalse;
4511 break;
4512 }
4513 status&=(MagickStatusType) TraceBezier(&mvg_info,
4514 primitive_info[j].coordinates);
4515 primitive_info=(*mvg_info.primitive_info);
4516 if (status == MagickFalse)
4517 break;
4518 i=j+(ssize_t) primitive_info[j].coordinates;
4519 break;
4520 }
4521 case PathPrimitive:
4522 {
4523 coordinates=(double) TracePath(&mvg_info,token,exception);
4524 primitive_info=(*mvg_info.primitive_info);
4525 if (status == MagickFalse)
4526 break;
4527 if (coordinates < 0.0)
4528 {
4529 status=MagickFalse;
4530 break;
4531 }
4532 i=(ssize_t) (j+coordinates);
4533 break;
4534 }
4535 case AlphaPrimitive:
4536 case ColorPrimitive:
4537 {
4538 ssize_t
4539 method;
4540
4541 if (primitive_info[j].coordinates != 1)
4542 {
4543 status=MagickFalse;
4544 break;
4545 }
4546 (void) GetNextToken(q,&q,extent,token);
4547 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
4548 if (method == -1)
4549 {
4550 status=MagickFalse;
4551 break;
4552 }
4553 primitive_info[j].method=(PaintMethod) method;
4554 break;
4555 }
4556 case TextPrimitive:
4557 {
4558 if (primitive_info[j].coordinates != 1)
4559 {
4560 status=MagickFalse;
4561 break;
4562 }
4563 if (*token != ',')
4564 (void) GetNextToken(q,&q,extent,token);
4565 (void) CloneString(&primitive_info[j].text,token);
4566 /*
4567 Compute text cursor offset.
4568 */
4569 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4570 if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
4571 (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
4572 {
4573 mvg_info.point=primitive_info->point;
4574 primitive_info->point.x+=cursor;
4575 }
4576 else
4577 {
4578 mvg_info.point=primitive_info->point;
4579 cursor=0.0;
4580 }
4581 clone_info->render=MagickFalse;
4582 clone_info->text=AcquireString(token);
4583 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,
4584 &metrics,exception);
4585 clone_info=DestroyDrawInfo(clone_info);
4586 cursor+=metrics.width;
4587 if (graphic_context[n]->compliance != SVGCompliance)
4588 cursor=0.0;
4589 break;
4590 }
4591 case ImagePrimitive:
4592 {
4593 if (primitive_info[j].coordinates != 2)
4594 {
4595 status=MagickFalse;
4596 break;
4597 }
4598 (void) GetNextToken(q,&q,extent,token);
4599 (void) CloneString(&primitive_info[j].text,token);
4600 break;
4601 }
4602 }
4603 mvg_info.offset=i;
4604 if (status == 0)
4605 break;
4606 primitive_info[i].primitive=UndefinedPrimitive;
4607 if ((draw_info->debug != MagickFalse) && (q > p))
4608 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
4609 /*
4610 Sanity check.
4611 */
4612 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,ExpandAffine(
4613 &graphic_context[n]->affine));
4614 primitive_info=(*mvg_info.primitive_info);
4615 if (status == 0)
4616 break;
4617 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4618 graphic_context[n]->stroke_width);
4619 primitive_info=(*mvg_info.primitive_info);
4620 if (status == 0)
4621 break;
4622 if (i == 0)
4623 continue;
4624 /*
4625 Transform points.
4626 */
4627 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4628 {
4629 point=primitive_info[i].point;
4630 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
4631 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
4632 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
4633 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
4634 point=primitive_info[i].point;
4635 if (point.x < graphic_context[n]->bounds.x1)
4636 graphic_context[n]->bounds.x1=point.x;
4637 if (point.y < graphic_context[n]->bounds.y1)
4638 graphic_context[n]->bounds.y1=point.y;
4639 if (point.x > graphic_context[n]->bounds.x2)
4640 graphic_context[n]->bounds.x2=point.x;
4641 if (point.y > graphic_context[n]->bounds.y2)
4642 graphic_context[n]->bounds.y2=point.y;
4643 if (primitive_info[i].primitive == ImagePrimitive)
4644 break;
4645 if (i >= (ssize_t) number_points)
4646 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
4647 }
4648 if (graphic_context[n]->render != MagickFalse)
4649 {
4650 if ((n != 0) && (graphic_context[n]->compliance != SVGCompliance) &&
4651 (graphic_context[n]->clip_mask != (char *) NULL) &&
4652 (LocaleCompare(graphic_context[n]->clip_mask,
4653 graphic_context[n-1]->clip_mask) != 0))
4654 {
4655 const char
4656 *clip_path;
4657
4658 clip_path=(const char *) GetValueFromSplayTree(macros,
4659 graphic_context[n]->clip_mask);
4660 if (clip_path != (const char *) NULL)
4661 (void) SetImageArtifact(image,graphic_context[n]->clip_mask,
4662 clip_path);
4663 status&=(MagickStatusType) DrawClipPath_(image,graphic_context[n],
4664 graphic_context[n]->clip_mask,depth+1,exception);
4665 }
4666 status&=(MagickStatusType) DrawPrimitive(image,graphic_context[n],
4667 primitive_info,exception);
4668 }
4669 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
4670 primitive_extent);
4671 if (proceed == MagickFalse)
4672 break;
4673 if (status == 0)
4674 break;
4675 }
4676 if (draw_info->debug != MagickFalse)
4677 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
4678 /*
4679 Relinquish resources.
4680 */
4681 macros=DestroySplayTree(macros);
4682 token=DestroyString(token);
4683 if (primitive_info != (PrimitiveInfo *) NULL)
4684 {
4685 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4686 if (primitive_info[i].text != (char *) NULL)
4687 primitive_info[i].text=DestroyString(primitive_info[i].text);
4688 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4689 }
4690 primitive=DestroyString(primitive);
4691 if (stops != (StopInfo *) NULL)
4692 stops=(StopInfo *) RelinquishMagickMemory(stops);
4693 for ( ; n >= 0; n--)
4694 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
4695 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
4696 if ((status == MagickFalse) && (exception->severity < ErrorException))
4697 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
4698 keyword);
4699 return(status != 0 ? MagickTrue : MagickFalse);
4700}
4701
4702MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
4703 ExceptionInfo *exception)
4704{
4705 MagickBooleanType
4706 status;
4707
4708 size_t
4709 macro_expansion = 0;
4710
4711 ((DrawInfo *) draw_info)->macro_expansion=(&macro_expansion);
4712 status=RenderMVGContent(image,draw_info,0,exception);
4713 ((DrawInfo *) draw_info)->macro_expansion=(size_t *) NULL;
4714 return(status);
4715}
4716␌
4717/*
4718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4719% %
4720% %
4721% %
4722% D r a w P a t t e r n P a t h %
4723% %
4724% %
4725% %
4726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4727%
4728% DrawPatternPath() draws a pattern.
4729%
4730% The format of the DrawPatternPath method is:
4731%
4732% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
4733% const char *name,Image **pattern,ExceptionInfo *exception)
4734%
4735% A description of each parameter follows:
4736%
4737% o image: the image.
4738%
4739% o draw_info: the draw info.
4740%
4741% o name: the pattern name.
4742%
4743% o image: the image.
4744%
4745% o exception: return any errors or warnings in this structure.
4746%
4747*/
4748MagickExport MagickBooleanType DrawPatternPath(Image *image,
4749 const DrawInfo *draw_info,const char *name,Image **pattern,
4750 ExceptionInfo *exception)
4751{
4752 char
4753 property[MagickPathExtent];
4754
4755 const char
4756 *geometry,
4757 *path,
4758 *type;
4759
4760 DrawInfo
4761 *clone_info;
4762
4763 ImageInfo
4764 *image_info;
4765
4766 MagickBooleanType
4767 status;
4768
4769 assert(image != (Image *) NULL);
4770 assert(image->signature == MagickCoreSignature);
4771 assert(draw_info != (const DrawInfo *) NULL);
4772 assert(name != (const char *) NULL);
4773 if (IsEventLogging() != MagickFalse)
4774 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4775 (void) FormatLocaleString(property,MagickPathExtent,"%s",name);
4776 path=GetImageArtifact(image,property);
4777 if (path == (const char *) NULL)
4778 return(MagickFalse);
4779 (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name);
4780 geometry=GetImageArtifact(image,property);
4781 if (geometry == (const char *) NULL)
4782 return(MagickFalse);
4783 if ((*pattern) != (Image *) NULL)
4784 *pattern=DestroyImage(*pattern);
4785 image_info=AcquireImageInfo();
4786 image_info->size=AcquireString(geometry);
4787 *pattern=AcquireImage(image_info,exception);
4788 image_info=DestroyImageInfo(image_info);
4789 (void) QueryColorCompliance("#00000000",AllCompliance,
4790 &(*pattern)->background_color,exception);
4791 (void) SetImageBackgroundColor(*pattern,exception);
4792 if (draw_info->debug != MagickFalse)
4793 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4794 "begin pattern-path %s %s",name,geometry);
4795 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4796 if (clone_info->fill_pattern != (Image *) NULL)
4797 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
4798 if (clone_info->stroke_pattern != (Image *) NULL)
4799 clone_info->stroke_pattern=DestroyImage(clone_info->stroke_pattern);
4800 (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name);
4801 type=GetImageArtifact(image,property);
4802 if (type != (const char *) NULL)
4803 clone_info->gradient.type=(GradientType) ParseCommandOption(
4804 MagickGradientOptions,MagickFalse,type);
4805 (void) CloneString(&clone_info->primitive,path);
4806 status=RenderMVGContent(*pattern,clone_info,0,exception);
4807 clone_info=DestroyDrawInfo(clone_info);
4808 if (draw_info->debug != MagickFalse)
4809 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
4810 return(status);
4811}
4812␌
4813/*
4814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4815% %
4816% %
4817% %
4818+ D r a w P o l y g o n P r i m i t i v e %
4819% %
4820% %
4821% %
4822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4823%
4824% DrawPolygonPrimitive() draws a polygon on the image.
4825%
4826% The format of the DrawPolygonPrimitive method is:
4827%
4828% MagickBooleanType DrawPolygonPrimitive(Image *image,
4829% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4830% ExceptionInfo *exception)
4831%
4832% A description of each parameter follows:
4833%
4834% o image: the image.
4835%
4836% o draw_info: the draw info.
4837%
4838% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4839%
4840% o exception: return any errors or warnings in this structure.
4841%
4842*/
4843
4844static PolygonInfo **DestroyPolygonTLS(PolygonInfo **polygon_info)
4845{
4846 ssize_t
4847 i;
4848
4849 assert(polygon_info != (PolygonInfo **) NULL);
4850 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
4851 if (polygon_info[i] != (PolygonInfo *) NULL)
4852 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
4853 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
4854 return(polygon_info);
4855}
4856
4857static PolygonInfo **AcquirePolygonTLS(const PrimitiveInfo *primitive_info,
4858 ExceptionInfo *exception)
4859{
4860 PathInfo
4861 *magick_restrict path_info;
4862
4864 **polygon_info;
4865
4866 size_t
4867 number_threads;
4868
4869 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4870 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
4871 sizeof(*polygon_info));
4872 if (polygon_info == (PolygonInfo **) NULL)
4873 {
4874 (void) ThrowMagickException(exception,GetMagickModule(),
4875 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4876 return((PolygonInfo **) NULL);
4877 }
4878 (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
4879 path_info=ConvertPrimitiveToPath(primitive_info,exception);
4880 if (path_info == (PathInfo *) NULL)
4881 return(DestroyPolygonTLS(polygon_info));
4882 polygon_info[0]=ConvertPathToPolygon(path_info,exception);
4883 if (polygon_info[0] == (PolygonInfo *) NULL)
4884 {
4885 (void) ThrowMagickException(exception,GetMagickModule(),
4886 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4887 return(DestroyPolygonTLS(polygon_info));
4888 }
4889 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
4890 return(polygon_info);
4891}
4892
4893static MagickBooleanType ClonePolygonEdgesTLS(PolygonInfo **polygon_info,
4894 const size_t number_threads,ExceptionInfo *exception)
4895{
4896 ssize_t
4897 i;
4898
4899 for (i=1; i < (ssize_t) number_threads; i++)
4900 {
4901 EdgeInfo
4902 *edge_info;
4903
4904 ssize_t
4905 j;
4906
4907 polygon_info[i]=(PolygonInfo *) AcquireMagickMemory(
4908 sizeof(*polygon_info[i]));
4909 if (polygon_info[i] == (PolygonInfo *) NULL)
4910 {
4911 (void) ThrowMagickException(exception,GetMagickModule(),
4912 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4913 return(MagickFalse);
4914 }
4915 polygon_info[i]->number_edges=0;
4916 edge_info=polygon_info[0]->edges;
4917 polygon_info[i]->edges=(EdgeInfo *) AcquireQuantumMemory(
4918 polygon_info[0]->number_edges,sizeof(*edge_info));
4919 if (polygon_info[i]->edges == (EdgeInfo *) NULL)
4920 {
4921 (void) ThrowMagickException(exception,GetMagickModule(),
4922 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4923 return(MagickFalse);
4924 }
4925 (void) memcpy(polygon_info[i]->edges,edge_info,
4926 polygon_info[0]->number_edges*sizeof(*edge_info));
4927 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4928 polygon_info[i]->edges[j].points=(PointInfo *) NULL;
4929 polygon_info[i]->number_edges=polygon_info[0]->number_edges;
4930 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4931 {
4932 edge_info=polygon_info[0]->edges+j;
4933 polygon_info[i]->edges[j].points=(PointInfo *) AcquireQuantumMemory(
4934 edge_info->number_points,sizeof(*edge_info));
4935 if (polygon_info[i]->edges[j].points == (PointInfo *) NULL)
4936 {
4937 (void) ThrowMagickException(exception,GetMagickModule(),
4938 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4939 return(MagickFalse);
4940 }
4941 (void) memcpy(polygon_info[i]->edges[j].points,edge_info->points,
4942 edge_info->number_points*sizeof(*edge_info->points));
4943 }
4944 }
4945 return(MagickTrue);
4946}
4947
4948static size_t DestroyEdge(PolygonInfo *polygon_info,const ssize_t edge)
4949{
4950 assert(edge < (ssize_t) polygon_info->number_edges);
4951 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
4952 polygon_info->edges[edge].points);
4953 polygon_info->number_edges--;
4954 if (edge < (ssize_t) polygon_info->number_edges)
4955 (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
4956 (polygon_info->number_edges-(size_t) edge)*sizeof(*polygon_info->edges));
4957 return(polygon_info->number_edges);
4958}
4959
4960static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
4961 const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
4962 const ssize_t y,double *stroke_alpha)
4963{
4964 double
4965 alpha,
4966 beta,
4967 distance,
4968 subpath_alpha;
4969
4970 const PointInfo
4971 *q;
4972
4973 EdgeInfo
4974 *p;
4975
4976 PointInfo
4977 delta;
4978
4979 ssize_t
4980 i,
4981 j,
4982 winding_number;
4983
4984 /*
4985 Compute fill & stroke opacity for this (x,y) point.
4986 */
4987 *stroke_alpha=0.0;
4988 subpath_alpha=0.0;
4989 p=polygon_info->edges;
4990 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4991 {
4992 if ((double) y <= (p->bounds.y1-mid-0.5))
4993 break;
4994 if ((double) y > (p->bounds.y2+mid+0.5))
4995 {
4996 p--;
4997 (void) DestroyEdge(polygon_info,j--);
4998 continue;
4999 }
5000 if (((double) x <= (p->bounds.x1-mid-0.5)) ||
5001 ((double) x > (p->bounds.x2+mid+0.5)))
5002 continue;
5003 i=(ssize_t) MagickMax((double) p->highwater,1.0);
5004 for ( ; i < (ssize_t) p->number_points; i++)
5005 {
5006 if ((double) y <= (p->points[i-1].y-mid-0.5))
5007 break;
5008 if ((double) y > (p->points[i].y+mid+0.5))
5009 continue;
5010 if (p->scanline != (double) y)
5011 {
5012 p->scanline=(double) y;
5013 p->highwater=(size_t) i;
5014 }
5015 /*
5016 Compute distance between a point and an edge.
5017 */
5018 q=p->points+i-1;
5019 delta.x=(q+1)->x-q->x;
5020 delta.y=(q+1)->y-q->y;
5021 beta=delta.x*(x-q->x)+delta.y*(y-q->y); /* segLen*point-cos(theta) */
5022 if (beta <= 0.0)
5023 {
5024 /*
5025 Cosine <= 0, point is closest to q.
5026 */
5027 delta.x=(double) x-q->x;
5028 delta.y=(double) y-q->y;
5029 distance=delta.x*delta.x+delta.y*delta.y;
5030 }
5031 else
5032 {
5033 alpha=delta.x*delta.x+delta.y*delta.y; /* segLen*segLen */
5034 if (beta >= alpha)
5035 {
5036 /*
5037 Point is closest to q+1.
5038 */
5039 delta.x=(double) x-(q+1)->x;
5040 delta.y=(double) y-(q+1)->y;
5041 distance=delta.x*delta.x+delta.y*delta.y;
5042 }
5043 else
5044 {
5045 /*
5046 Point is closest to point between q & q+1.
5047 */
5048 alpha=MagickSafeReciprocal(alpha);
5049 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
5050 distance=alpha*beta*beta;
5051 }
5052 }
5053 /*
5054 Compute stroke & subpath opacity.
5055 */
5056 beta=0.0;
5057 if (p->ghostline == MagickFalse)
5058 {
5059 alpha=mid+0.5;
5060 if ((*stroke_alpha < 1.0) &&
5061 (distance <= ((alpha+0.25)*(alpha+0.25))))
5062 {
5063 alpha=mid-0.5;
5064 if (distance <= ((alpha+0.25)*(alpha+0.25)))
5065 *stroke_alpha=1.0;
5066 else
5067 {
5068 beta=1.0;
5069 if (fabs(distance-1.0) >= MagickEpsilon)
5070 beta=sqrt((double) distance);
5071 alpha=beta-mid-0.5;
5072 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
5073 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
5074 }
5075 }
5076 }
5077 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
5078 continue;
5079 if (distance <= 0.0)
5080 {
5081 subpath_alpha=1.0;
5082 continue;
5083 }
5084 if (distance > 1.0)
5085 continue;
5086 if (fabs(beta) < MagickEpsilon)
5087 {
5088 beta=1.0;
5089 if (fabs(distance-1.0) >= MagickEpsilon)
5090 beta=sqrt(distance);
5091 }
5092 alpha=beta-1.0;
5093 if (subpath_alpha < (alpha*alpha))
5094 subpath_alpha=alpha*alpha;
5095 }
5096 }
5097 /*
5098 Compute fill opacity.
5099 */
5100 if (fill == MagickFalse)
5101 return(0.0);
5102 if (subpath_alpha >= 1.0)
5103 return(1.0);
5104 /*
5105 Determine winding number.
5106 */
5107 winding_number=0;
5108 p=polygon_info->edges;
5109 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
5110 {
5111 if ((double) y <= p->bounds.y1)
5112 break;
5113 if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
5114 continue;
5115 if ((double) x > p->bounds.x2)
5116 {
5117 winding_number+=p->direction != 0 ? 1 : -1;
5118 continue;
5119 }
5120 i=(ssize_t) MagickMax((double) p->highwater,1.0);
5121 for ( ; i < (ssize_t) (p->number_points-1); i++)
5122 if ((double) y <= p->points[i].y)
5123 break;
5124 q=p->points+i-1;
5125 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
5126 winding_number+=p->direction != 0 ? 1 : -1;
5127 }
5128 if (fill_rule != NonZeroRule)
5129 {
5130 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
5131 return(1.0);
5132 }
5133 else
5134 if (MagickAbsoluteValue(winding_number) != 0)
5135 return(1.0);
5136 return(subpath_alpha);
5137}
5138
5139static MagickBooleanType DrawPolygonPrimitive(Image *image,
5140 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5141 ExceptionInfo *exception)
5142{
5143 typedef struct _ExtentInfo
5144 {
5145 ssize_t
5146 x1,
5147 y1,
5148 x2,
5149 y2;
5150 } ExtentInfo;
5151
5152 CacheView
5153 *image_view;
5154
5155 const char
5156 *artifact;
5157
5158 double
5159 mid;
5160
5161 EdgeInfo
5162 *p;
5163
5164 ExtentInfo
5165 poly_extent;
5166
5167 MagickBooleanType
5168 fill,
5169 status;
5170
5172 **magick_restrict polygon_info;
5173
5175 bounds;
5176
5177 size_t
5178 number_threads;
5179
5180 ssize_t
5181 i,
5182 y;
5183
5184 assert(image != (Image *) NULL);
5185 assert(image->signature == MagickCoreSignature);
5186 assert(draw_info != (DrawInfo *) NULL);
5187 assert(draw_info->signature == MagickCoreSignature);
5188 assert(primitive_info != (PrimitiveInfo *) NULL);
5189 if (IsEventLogging() != MagickFalse)
5190 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5191 if (primitive_info->coordinates <= 1)
5192 return(MagickTrue);
5193 /*
5194 Compute bounding box.
5195 */
5196 polygon_info=AcquirePolygonTLS(primitive_info,exception);
5197 if (polygon_info == (PolygonInfo **) NULL)
5198 return(MagickFalse);
5199 if (draw_info->debug != MagickFalse)
5200 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
5201 fill=(primitive_info->method == FillToBorderMethod) ||
5202 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
5203 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5204 bounds=polygon_info[0]->edges[0].bounds;
5205 artifact=GetImageArtifact(image,"draw:render-bounding-rectangles");
5206 if (IsStringTrue(artifact) != MagickFalse)
5207 (void) DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
5208 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
5209 {
5210 p=polygon_info[0]->edges+i;
5211 if (p->bounds.x1 < bounds.x1)
5212 bounds.x1=p->bounds.x1;
5213 if (p->bounds.y1 < bounds.y1)
5214 bounds.y1=p->bounds.y1;
5215 if (p->bounds.x2 > bounds.x2)
5216 bounds.x2=p->bounds.x2;
5217 if (p->bounds.y2 > bounds.y2)
5218 bounds.y2=p->bounds.y2;
5219 }
5220 bounds.x1-=(mid+1.0);
5221 bounds.y1-=(mid+1.0);
5222 bounds.x2+=(mid+1.0);
5223 bounds.y2+=(mid+1.0);
5224 if ((bounds.x1 >= (double) image->columns) ||
5225 (bounds.y1 >= (double) image->rows) ||
5226 (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
5227 {
5228 polygon_info=DestroyPolygonTLS(polygon_info);
5229 return(MagickTrue); /* virtual polygon */
5230 }
5231 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
5232 (double) image->columns-1.0 : bounds.x1;
5233 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
5234 (double) image->rows-1.0 : bounds.y1;
5235 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
5236 (double) image->columns-1.0 : bounds.x2;
5237 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
5238 (double) image->rows-1.0 : bounds.y2;
5239 poly_extent.x1=CastDoubleToSsizeT(ceil(bounds.x1-0.5));
5240 poly_extent.y1=CastDoubleToSsizeT(ceil(bounds.y1-0.5));
5241 poly_extent.x2=CastDoubleToSsizeT(floor(bounds.x2+0.5));
5242 poly_extent.y2=CastDoubleToSsizeT(floor(bounds.y2+0.5));
5243 number_threads=(size_t) GetMagickNumberThreads(image,image,(size_t)
5244 (poly_extent.y2-poly_extent.y1+1),1);
5245 status=ClonePolygonEdgesTLS(polygon_info,number_threads,exception);
5246 if (status == MagickFalse)
5247 {
5248 polygon_info=DestroyPolygonTLS(polygon_info);
5249 return(status);
5250 }
5251 image_view=AcquireAuthenticCacheView(image,exception);
5252 if ((primitive_info->coordinates == 1) ||
5253 (polygon_info[0]->number_edges == 0))
5254 {
5255 /*
5256 Draw point.
5257 */
5258#if defined(MAGICKCORE_OPENMP_SUPPORT)
5259 #pragma omp parallel for schedule(static) shared(status) \
5260 num_threads((int) number_threads)
5261#endif
5262 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5263 {
5264 PixelInfo
5265 pixel;
5266
5267 ssize_t
5268 x;
5269
5270 Quantum
5271 *magick_restrict q;
5272
5273 if (status == MagickFalse)
5274 continue;
5275 x=poly_extent.x1;
5276 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (poly_extent.x2-
5277 x+1),1,exception);
5278 if (q == (Quantum *) NULL)
5279 {
5280 status=MagickFalse;
5281 continue;
5282 }
5283 GetPixelInfo(image,&pixel);
5284 for ( ; x <= poly_extent.x2; x++)
5285 {
5286 if ((x == CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5))) &&
5287 (y == CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5))))
5288 {
5289 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&pixel,
5290 exception);
5291 SetPixelViaPixelInfo(image,&pixel,q);
5292 }
5293 q+=(ptrdiff_t) GetPixelChannels(image);
5294 }
5295 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5296 status=MagickFalse;
5297 }
5298 image_view=DestroyCacheView(image_view);
5299 polygon_info=DestroyPolygonTLS(polygon_info);
5300 if (draw_info->debug != MagickFalse)
5301 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5302 " end draw-polygon");
5303 return(status);
5304 }
5305 /*
5306 Draw polygon or line.
5307 */
5308#if defined(MAGICKCORE_OPENMP_SUPPORT)
5309 #pragma omp parallel for schedule(static) shared(status) \
5310 num_threads((int) number_threads)
5311#endif
5312 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5313 {
5314 const int
5315 id = GetOpenMPThreadId();
5316
5317 Quantum
5318 *magick_restrict q;
5319
5320 ssize_t
5321 x;
5322
5323 if (status == MagickFalse)
5324 continue;
5325 q=GetCacheViewAuthenticPixels(image_view,poly_extent.x1,y,(size_t)
5326 (poly_extent.x2-poly_extent.x1+1),1,exception);
5327 if (q == (Quantum *) NULL)
5328 {
5329 status=MagickFalse;
5330 continue;
5331 }
5332 for (x=poly_extent.x1; x <= poly_extent.x2; x++)
5333 {
5334 double
5335 fill_alpha,
5336 stroke_alpha;
5337
5338 PixelInfo
5339 fill_color,
5340 stroke_color;
5341
5342 /*
5343 Fill and/or stroke.
5344 */
5345 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
5346 x,y,&stroke_alpha);
5347 if (draw_info->stroke_antialias == MagickFalse)
5348 {
5349 fill_alpha=fill_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5350 stroke_alpha=stroke_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5351 }
5352 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&fill_color,
5353 exception);
5354 CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
5355 (double) GetPixelAlpha(image,q),q);
5356 GetStrokeColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&stroke_color,
5357 exception);
5358 CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
5359 (double) GetPixelAlpha(image,q),q);
5360 q+=(ptrdiff_t) GetPixelChannels(image);
5361 }
5362 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5363 status=MagickFalse;
5364 }
5365 image_view=DestroyCacheView(image_view);
5366 polygon_info=DestroyPolygonTLS(polygon_info);
5367 if (draw_info->debug != MagickFalse)
5368 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
5369 return(status);
5370}
5371␌
5372/*
5373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5374% %
5375% %
5376% %
5377% D r a w P r i m i t i v e %
5378% %
5379% %
5380% %
5381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5382%
5383% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
5384%
5385% The format of the DrawPrimitive method is:
5386%
5387% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
5388% PrimitiveInfo *primitive_info,ExceptionInfo *exception)
5389%
5390% A description of each parameter follows:
5391%
5392% o image: the image.
5393%
5394% o draw_info: the draw info.
5395%
5396% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5397%
5398% o exception: return any errors or warnings in this structure.
5399%
5400*/
5401static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
5402{
5403 const char
5404 *methods[] =
5405 {
5406 "point",
5407 "replace",
5408 "floodfill",
5409 "filltoborder",
5410 "reset",
5411 "?"
5412 };
5413
5414 PointInfo
5415 p,
5416 point,
5417 q;
5418
5419 ssize_t
5420 i,
5421 x;
5422
5423 ssize_t
5424 coordinates,
5425 y;
5426
5427 x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5428 y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5429 switch (primitive_info->primitive)
5430 {
5431 case AlphaPrimitive:
5432 {
5433 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5434 "AlphaPrimitive %.20g,%.20g %s",(double) x,(double) y,
5435 methods[primitive_info->method]);
5436 return;
5437 }
5438 case ColorPrimitive:
5439 {
5440 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5441 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
5442 methods[primitive_info->method]);
5443 return;
5444 }
5445 case ImagePrimitive:
5446 {
5447 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5448 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
5449 return;
5450 }
5451 case PointPrimitive:
5452 {
5453 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5454 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
5455 methods[primitive_info->method]);
5456 return;
5457 }
5458 case TextPrimitive:
5459 {
5460 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5461 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
5462 return;
5463 }
5464 default:
5465 break;
5466 }
5467 coordinates=0;
5468 p=primitive_info[0].point;
5469 q.x=(-1.0);
5470 q.y=(-1.0);
5471 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5472 {
5473 point=primitive_info[i].point;
5474 if (coordinates <= 0)
5475 {
5476 coordinates=(ssize_t) primitive_info[i].coordinates;
5477 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5478 " begin open (%.20g)",(double) coordinates);
5479 p=point;
5480 }
5481 point=primitive_info[i].point;
5482 if ((fabs(q.x-point.x) >= MagickEpsilon) ||
5483 (fabs(q.y-point.y) >= MagickEpsilon))
5484 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5485 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
5486 else
5487 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5488 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
5489 q=point;
5490 coordinates--;
5491 if (coordinates > 0)
5492 continue;
5493 if ((fabs(p.x-point.x) >= MagickEpsilon) ||
5494 (fabs(p.y-point.y) >= MagickEpsilon))
5495 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
5496 (double) coordinates);
5497 else
5498 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
5499 (double) coordinates);
5500 }
5501}
5502
5503MagickExport MagickBooleanType DrawPrimitive(Image *image,
5504 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5505 ExceptionInfo *exception)
5506{
5507 CacheView
5508 *image_view;
5509
5510 MagickStatusType
5511 status;
5512
5513 ssize_t
5514 i,
5515 x;
5516
5517 ssize_t
5518 y;
5519
5520 if (draw_info->debug != MagickFalse)
5521 {
5522 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5523 " begin draw-primitive");
5524 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5525 " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
5526 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
5527 draw_info->affine.tx,draw_info->affine.ty);
5528 }
5529 status=MagickTrue;
5530 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5531 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
5532 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
5533 status&=(MagickStatusType) SetImageColorspace(image,sRGBColorspace,
5534 exception);
5535 if (draw_info->compliance == SVGCompliance)
5536 {
5537 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5538 draw_info->clipping_mask,exception);
5539 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5540 draw_info->composite_mask,exception);
5541 }
5542 x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5543 y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5544 image_view=AcquireAuthenticCacheView(image,exception);
5545 switch (primitive_info->primitive)
5546 {
5547 case AlphaPrimitive:
5548 {
5549 if ((image->alpha_trait & BlendPixelTrait) == 0)
5550 status&=(MagickStatusType) SetImageAlphaChannel(image,
5551 OpaqueAlphaChannel,exception);
5552 switch (primitive_info->method)
5553 {
5554 case PointMethod:
5555 default:
5556 {
5557 PixelInfo
5558 pixel;
5559
5560 Quantum
5561 *q;
5562
5563 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5564 if (q == (Quantum *) NULL)
5565 break;
5566 GetFillColor(draw_info,x,y,&pixel,exception);
5567 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5568 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5569 exception);
5570 break;
5571 }
5572 case ReplaceMethod:
5573 {
5574 PixelInfo
5575 pixel,
5576 target;
5577
5578 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5579 x,y,&target,exception);
5580 GetPixelInfo(image,&pixel);
5581 for (y=0; y < (ssize_t) image->rows; y++)
5582 {
5583 Quantum
5584 *magick_restrict q;
5585
5586 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5587 exception);
5588 if (q == (Quantum *) NULL)
5589 break;
5590 for (x=0; x < (ssize_t) image->columns; x++)
5591 {
5592 GetPixelInfoPixel(image,q,&pixel);
5593 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5594 {
5595 q+=(ptrdiff_t) GetPixelChannels(image);
5596 continue;
5597 }
5598 GetFillColor(draw_info,x,y,&pixel,exception);
5599 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5600 q+=(ptrdiff_t) GetPixelChannels(image);
5601 }
5602 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5603 exception);
5604 if (status == MagickFalse)
5605 break;
5606 }
5607 break;
5608 }
5609 case FloodfillMethod:
5610 case FillToBorderMethod:
5611 {
5612 ChannelType
5613 channel_mask;
5614
5615 PixelInfo
5616 target;
5617
5618 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5619 TileVirtualPixelMethod,x,y,&target,exception);
5620 if (primitive_info->method == FillToBorderMethod)
5621 {
5622 target.red=(double) draw_info->border_color.red;
5623 target.green=(double) draw_info->border_color.green;
5624 target.blue=(double) draw_info->border_color.blue;
5625 }
5626 channel_mask=SetImageChannelMask(image,AlphaChannel);
5627 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5628 &target,x,y,primitive_info->method == FloodfillMethod ?
5629 MagickFalse : MagickTrue,exception);
5630 (void) SetImageChannelMask(image,channel_mask);
5631 break;
5632 }
5633 case ResetMethod:
5634 {
5635 PixelInfo
5636 pixel;
5637
5638 for (y=0; y < (ssize_t) image->rows; y++)
5639 {
5640 Quantum
5641 *magick_restrict q;
5642
5643 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5644 exception);
5645 if (q == (Quantum *) NULL)
5646 break;
5647 for (x=0; x < (ssize_t) image->columns; x++)
5648 {
5649 GetFillColor(draw_info,x,y,&pixel,exception);
5650 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5651 q+=(ptrdiff_t) GetPixelChannels(image);
5652 }
5653 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5654 exception);
5655 if (status == MagickFalse)
5656 break;
5657 }
5658 break;
5659 }
5660 }
5661 break;
5662 }
5663 case ColorPrimitive:
5664 {
5665 switch (primitive_info->method)
5666 {
5667 case PointMethod:
5668 default:
5669 {
5670 PixelInfo
5671 pixel;
5672
5673 Quantum
5674 *q;
5675
5676 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5677 if (q == (Quantum *) NULL)
5678 break;
5679 GetPixelInfo(image,&pixel);
5680 GetFillColor(draw_info,x,y,&pixel,exception);
5681 SetPixelViaPixelInfo(image,&pixel,q);
5682 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5683 exception);
5684 break;
5685 }
5686 case ReplaceMethod:
5687 {
5688 PixelInfo
5689 pixel,
5690 target;
5691
5692 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5693 x,y,&target,exception);
5694 for (y=0; y < (ssize_t) image->rows; y++)
5695 {
5696 Quantum
5697 *magick_restrict q;
5698
5699 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5700 exception);
5701 if (q == (Quantum *) NULL)
5702 break;
5703 for (x=0; x < (ssize_t) image->columns; x++)
5704 {
5705 GetPixelInfoPixel(image,q,&pixel);
5706 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5707 {
5708 q+=(ptrdiff_t) GetPixelChannels(image);
5709 continue;
5710 }
5711 GetFillColor(draw_info,x,y,&pixel,exception);
5712 SetPixelViaPixelInfo(image,&pixel,q);
5713 q+=(ptrdiff_t) GetPixelChannels(image);
5714 }
5715 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5716 exception);
5717 if (status == MagickFalse)
5718 break;
5719 }
5720 break;
5721 }
5722 case FloodfillMethod:
5723 case FillToBorderMethod:
5724 {
5725 PixelInfo
5726 target;
5727
5728 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5729 TileVirtualPixelMethod,x,y,&target,exception);
5730 if (primitive_info->method == FillToBorderMethod)
5731 {
5732 target.red=(double) draw_info->border_color.red;
5733 target.green=(double) draw_info->border_color.green;
5734 target.blue=(double) draw_info->border_color.blue;
5735 }
5736 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5737 &target,x,y,primitive_info->method == FloodfillMethod ?
5738 MagickFalse : MagickTrue,exception);
5739 break;
5740 }
5741 case ResetMethod:
5742 {
5743 PixelInfo
5744 pixel;
5745
5746 GetPixelInfo(image,&pixel);
5747 for (y=0; y < (ssize_t) image->rows; y++)
5748 {
5749 Quantum
5750 *magick_restrict q;
5751
5752 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5753 exception);
5754 if (q == (Quantum *) NULL)
5755 break;
5756 for (x=0; x < (ssize_t) image->columns; x++)
5757 {
5758 GetFillColor(draw_info,x,y,&pixel,exception);
5759 SetPixelViaPixelInfo(image,&pixel,q);
5760 q+=(ptrdiff_t) GetPixelChannels(image);
5761 }
5762 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5763 exception);
5764 if (status == MagickFalse)
5765 break;
5766 }
5767 break;
5768 }
5769 }
5770 break;
5771 }
5772 case ImagePrimitive:
5773 {
5775 affine;
5776
5777 char
5778 composite_geometry[MagickPathExtent];
5779
5780 Image
5781 *composite_image,
5782 *composite_images;
5783
5784 ImageInfo
5785 *clone_info;
5786
5788 geometry;
5789
5790 ssize_t
5791 x1,
5792 y1;
5793
5794 if (primitive_info->text == (char *) NULL)
5795 break;
5796 clone_info=AcquireImageInfo();
5797 composite_images=(Image *) NULL;
5798 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
5799 composite_images=ReadInlineImage(clone_info,primitive_info->text,
5800 exception);
5801 else
5802 if (*primitive_info->text != '\0')
5803 {
5804 /*
5805 Read composite image.
5806 */
5807 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5808 MagickPathExtent);
5809 (void) SetImageInfo(clone_info,1,exception);
5810 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5811 MagickPathExtent);
5812 if (clone_info->size != (char *) NULL)
5813 clone_info->size=DestroyString(clone_info->size);
5814 if (clone_info->extract != (char *) NULL)
5815 clone_info->extract=DestroyString(clone_info->extract);
5816 clone_info->coder_depth=draw_info->image_info->coder_depth;
5817 composite_images=StrictReadImage(clone_info,exception);
5818 }
5819 clone_info=DestroyImageInfo(clone_info);
5820 if (composite_images == (Image *) NULL)
5821 {
5822 status=MagickFalse;
5823 break;
5824 }
5825 composite_image=RemoveFirstImageFromList(&composite_images);
5826 composite_images=DestroyImageList(composite_images);
5827 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
5828 NULL,(void *) NULL);
5829 x1=CastDoubleToSsizeT(ceil(primitive_info[1].point.x-0.5));
5830 y1=CastDoubleToSsizeT(ceil(primitive_info[1].point.y-0.5));
5831 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
5832 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
5833 {
5834 /*
5835 Resize image.
5836 */
5837 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5838 "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
5839 composite_image->filter=image->filter;
5840 status&=(MagickStatusType) TransformImage(&composite_image,
5841 (char *) NULL,composite_geometry,exception);
5842 }
5843 if (composite_image->alpha_trait == UndefinedPixelTrait)
5844 status&=(MagickStatusType) SetImageAlphaChannel(composite_image,
5845 OpaqueAlphaChannel,exception);
5846 if (draw_info->alpha != OpaqueAlpha)
5847 status&=(MagickStatusType) SetImageAlpha(composite_image,
5848 draw_info->alpha,exception);
5849 SetGeometry(image,&geometry);
5850 image->gravity=draw_info->gravity;
5851 geometry.x=x;
5852 geometry.y=y;
5853 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5854 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
5855 composite_image->rows,(double) geometry.x,(double) geometry.y);
5856 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
5857 affine=draw_info->affine;
5858 affine.tx=(double) geometry.x;
5859 affine.ty=(double) geometry.y;
5860 composite_image->interpolate=image->interpolate;
5861 if ((draw_info->compose == OverCompositeOp) ||
5862 (draw_info->compose == SrcOverCompositeOp))
5863 status&=(MagickStatusType) DrawAffineImage(image,composite_image,
5864 &affine,exception);
5865 else
5866 status&=(MagickStatusType) CompositeImage(image,composite_image,
5867 draw_info->compose,MagickTrue,geometry.x,geometry.y,exception);
5868 composite_image=DestroyImage(composite_image);
5869 break;
5870 }
5871 case PointPrimitive:
5872 {
5873 PixelInfo
5874 fill_color;
5875
5876 Quantum
5877 *q;
5878
5879 if ((y < 0) || (y >= (ssize_t) image->rows))
5880 break;
5881 if ((x < 0) || (x >= (ssize_t) image->columns))
5882 break;
5883 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5884 if (q == (Quantum *) NULL)
5885 break;
5886 GetFillColor(draw_info,x,y,&fill_color,exception);
5887 CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,(double)
5888 GetPixelAlpha(image,q),q);
5889 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5890 exception);
5891 break;
5892 }
5893 case TextPrimitive:
5894 {
5895 char
5896 geometry[MagickPathExtent];
5897
5898 DrawInfo
5899 *clone_info;
5900
5901 if (primitive_info->text == (char *) NULL)
5902 break;
5903 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5904 (void) CloneString(&clone_info->text,primitive_info->text);
5905 (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
5906 primitive_info->point.x,primitive_info->point.y);
5907 (void) CloneString(&clone_info->geometry,geometry);
5908 status&=(MagickStatusType) AnnotateImage(image,clone_info,exception);
5909 clone_info=DestroyDrawInfo(clone_info);
5910 break;
5911 }
5912 default:
5913 {
5914 double
5915 mid,
5916 scale;
5917
5918 DrawInfo
5919 *clone_info;
5920
5921 if (IsEventLogging() != MagickFalse)
5922 LogPrimitiveInfo(primitive_info);
5923 scale=ExpandAffine(&draw_info->affine);
5924 if ((draw_info->dash_pattern != (double *) NULL) &&
5925 (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
5926 (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
5927 (draw_info->stroke.alpha != (double) TransparentAlpha))
5928 {
5929 /*
5930 Draw dash polygon.
5931 */
5932 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5933 clone_info->stroke_width=0.0;
5934 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5935 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5936 primitive_info,exception);
5937 clone_info=DestroyDrawInfo(clone_info);
5938 if (status != MagickFalse)
5939 status&=(MagickStatusType) DrawDashPolygon(draw_info,primitive_info,
5940 image,exception);
5941 break;
5942 }
5943 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5944 if ((mid > 1.0) &&
5945 ((draw_info->stroke.alpha != (double) TransparentAlpha) ||
5946 (draw_info->stroke_pattern != (Image *) NULL)))
5947 {
5948 double
5949 point_x,
5950 point_y;
5951
5952 MagickBooleanType
5953 closed_path;
5954
5955 /*
5956 Draw strokes while respecting line cap/join attributes.
5957 */
5958 closed_path=primitive_info[0].closed_subpath;
5959 i=(ssize_t) primitive_info[0].coordinates;
5960 point_x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
5961 point_y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
5962 if ((point_x < MagickEpsilon) && (point_y < MagickEpsilon))
5963 closed_path=MagickTrue;
5964 if ((((draw_info->linecap == RoundCap) ||
5965 (closed_path != MagickFalse)) &&
5966 (draw_info->linejoin == RoundJoin)) ||
5967 (primitive_info[i].primitive != UndefinedPrimitive))
5968 {
5969 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5970 primitive_info,exception);
5971 break;
5972 }
5973 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5974 clone_info->stroke_width=0.0;
5975 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5976 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5977 primitive_info,exception);
5978 clone_info=DestroyDrawInfo(clone_info);
5979 if (status != MagickFalse)
5980 status&=(MagickStatusType) DrawStrokePolygon(image,draw_info,
5981 primitive_info,exception);
5982 break;
5983 }
5984 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5985 primitive_info,exception);
5986 break;
5987 }
5988 }
5989 image_view=DestroyCacheView(image_view);
5990 if (draw_info->compliance == SVGCompliance)
5991 {
5992 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5993 (Image *) NULL,exception);
5994 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5995 (Image *) NULL,exception);
5996 }
5997 if (draw_info->debug != MagickFalse)
5998 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
5999 return(status != 0 ? MagickTrue : MagickFalse);
6000}
6001␌
6002/*
6003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6004% %
6005% %
6006% %
6007+ D r a w S t r o k e P o l y g o n %
6008% %
6009% %
6010% %
6011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6012%
6013% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
6014% the image while respecting the line cap and join attributes.
6015%
6016% The format of the DrawStrokePolygon method is:
6017%
6018% MagickBooleanType DrawStrokePolygon(Image *image,
6019% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
6020%
6021% A description of each parameter follows:
6022%
6023% o image: the image.
6024%
6025% o draw_info: the draw info.
6026%
6027% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
6028%
6029%
6030*/
6031
6032static MagickBooleanType DrawRoundLinecap(Image *image,
6033 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
6034 ExceptionInfo *exception)
6035{
6037 linecap[5];
6038
6039 ssize_t
6040 i;
6041
6042 if (primitive_info->coordinates < 1)
6043 return(MagickFalse);
6044 for (i=0; i < 4; i++)
6045 linecap[i]=(*primitive_info);
6046 linecap[0].coordinates=4;
6047 linecap[1].point.x+=2.0*MagickEpsilon;
6048 linecap[2].point.x+=2.0*MagickEpsilon;
6049 linecap[2].point.y+=2.0*MagickEpsilon;
6050 linecap[3].point.y+=2.0*MagickEpsilon;
6051 linecap[4].primitive=UndefinedPrimitive;
6052 return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
6053}
6054
6055static MagickBooleanType DrawStrokePolygon(Image *image,
6056 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
6057 ExceptionInfo *exception)
6058{
6059 DrawInfo
6060 *clone_info;
6061
6062 MagickBooleanType
6063 closed_path;
6064
6065 MagickStatusType
6066 status;
6067
6069 *stroke_polygon;
6070
6071 const PrimitiveInfo
6072 *p,
6073 *q;
6074
6075 /*
6076 Draw stroked polygon.
6077 */
6078 if (draw_info->debug != MagickFalse)
6079 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6080 " begin draw-stroke-polygon");
6081 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6082 clone_info->fill=draw_info->stroke;
6083 if (clone_info->fill_pattern != (Image *) NULL)
6084 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
6085 if (clone_info->stroke_pattern != (Image *) NULL)
6086 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
6087 MagickTrue,exception);
6088 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
6089 clone_info->stroke_width=0.0;
6090 clone_info->fill_rule=NonZeroRule;
6091 status=MagickTrue;
6092 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=(ptrdiff_t) p->coordinates)
6093 {
6094 if (p->coordinates == 1)
6095 continue;
6096 stroke_polygon=TraceStrokePolygon(draw_info,p,exception);
6097 if (stroke_polygon == (PrimitiveInfo *) NULL)
6098 {
6099 status=0;
6100 break;
6101 }
6102 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
6103 stroke_polygon,exception);
6104 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
6105 if (status == 0)
6106 break;
6107 q=p+p->coordinates-1;
6108 closed_path=p->closed_subpath;
6109 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
6110 {
6111 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,p,
6112 exception);
6113 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,q,
6114 exception);
6115 }
6116 }
6117 clone_info=DestroyDrawInfo(clone_info);
6118 if (draw_info->debug != MagickFalse)
6119 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6120 " end draw-stroke-polygon");
6121 return(status != 0 ? MagickTrue : MagickFalse);
6122}
6123␌
6124/*
6125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6126% %
6127% %
6128% %
6129% G e t A f f i n e M a t r i x %
6130% %
6131% %
6132% %
6133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6134%
6135% GetAffineMatrix() returns an AffineMatrix initialized to the identity
6136% matrix.
6137%
6138% The format of the GetAffineMatrix method is:
6139%
6140% void GetAffineMatrix(AffineMatrix *affine_matrix)
6141%
6142% A description of each parameter follows:
6143%
6144% o affine_matrix: the affine matrix.
6145%
6146*/
6147MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
6148{
6149 if (IsEventLogging() != MagickFalse)
6150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6151 assert(affine_matrix != (AffineMatrix *) NULL);
6152 (void) memset(affine_matrix,0,sizeof(*affine_matrix));
6153 affine_matrix->sx=1.0;
6154 affine_matrix->sy=1.0;
6155}
6156␌
6157/*
6158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6159% %
6160% %
6161% %
6162+ G e t D r a w I n f o %
6163% %
6164% %
6165% %
6166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6167%
6168% GetDrawInfo() initializes draw_info to default values from image_info.
6169%
6170% The format of the GetDrawInfo method is:
6171%
6172% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6173%
6174% A description of each parameter follows:
6175%
6176% o image_info: the image info..
6177%
6178% o draw_info: the draw info.
6179%
6180*/
6181MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6182{
6183 char
6184 *next_token;
6185
6186 const char
6187 *option;
6188
6190 *exception;
6191
6192 /*
6193 Initialize draw attributes.
6194 */
6195 assert(draw_info != (DrawInfo *) NULL);
6196 if (IsEventLogging() != MagickFalse)
6197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6198 (void) memset(draw_info,0,sizeof(*draw_info));
6199 draw_info->image_info=CloneImageInfo(image_info);
6200 GetAffineMatrix(&draw_info->affine);
6201 exception=AcquireExceptionInfo();
6202 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
6203 exception);
6204 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
6205 exception);
6206 draw_info->stroke_antialias=draw_info->image_info->antialias;
6207 draw_info->stroke_width=1.0;
6208 draw_info->fill_rule=EvenOddRule;
6209 draw_info->alpha=OpaqueAlpha;
6210 draw_info->fill_alpha=OpaqueAlpha;
6211 draw_info->stroke_alpha=OpaqueAlpha;
6212 draw_info->linecap=ButtCap;
6213 draw_info->linejoin=MiterJoin;
6214 draw_info->miterlimit=10;
6215 draw_info->decorate=NoDecoration;
6216 draw_info->pointsize=12.0;
6217 draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
6218 draw_info->compose=OverCompositeOp;
6219 draw_info->render=MagickTrue;
6220 draw_info->clip_path=MagickFalse;
6221 draw_info->debug=(GetLogEventMask() & (DrawEvent | AnnotateEvent)) != 0 ?
6222 MagickTrue : MagickFalse;
6223 draw_info->macro_expansion=(size_t *) NULL;
6224 if (draw_info->image_info->font != (char *) NULL)
6225 draw_info->font=AcquireString(draw_info->image_info->font);
6226 if (draw_info->image_info->density != (char *) NULL)
6227 draw_info->density=AcquireString(draw_info->image_info->density);
6228 draw_info->text_antialias=draw_info->image_info->antialias;
6229 if (fabs(draw_info->image_info->pointsize) >= MagickEpsilon)
6230 draw_info->pointsize=draw_info->image_info->pointsize;
6231 draw_info->border_color=draw_info->image_info->border_color;
6232 if (draw_info->image_info->server_name != (char *) NULL)
6233 draw_info->server_name=AcquireString(draw_info->image_info->server_name);
6234 option=GetImageOption(draw_info->image_info,"direction");
6235 if (option != (const char *) NULL)
6236 draw_info->direction=(DirectionType) ParseCommandOption(
6237 MagickDirectionOptions,MagickFalse,option);
6238 else
6239 draw_info->direction=UndefinedDirection;
6240 option=GetImageOption(draw_info->image_info,"encoding");
6241 if (option != (const char *) NULL)
6242 (void) CloneString(&draw_info->encoding,option);
6243 option=GetImageOption(draw_info->image_info,"family");
6244 if (option != (const char *) NULL)
6245 (void) CloneString(&draw_info->family,option);
6246 option=GetImageOption(draw_info->image_info,"fill");
6247 if (option != (const char *) NULL)
6248 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
6249 exception);
6250 option=GetImageOption(draw_info->image_info,"gravity");
6251 if (option != (const char *) NULL)
6252 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
6253 MagickFalse,option);
6254 option=GetImageOption(draw_info->image_info,"interline-spacing");
6255 if (option != (const char *) NULL)
6256 draw_info->interline_spacing=GetDrawValue(option,&next_token);
6257 option=GetImageOption(draw_info->image_info,"interword-spacing");
6258 if (option != (const char *) NULL)
6259 draw_info->interword_spacing=GetDrawValue(option,&next_token);
6260 option=GetImageOption(draw_info->image_info,"kerning");
6261 if (option != (const char *) NULL)
6262 draw_info->kerning=GetDrawValue(option,&next_token);
6263 option=GetImageOption(draw_info->image_info,"stroke");
6264 if (option != (const char *) NULL)
6265 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
6266 exception);
6267 option=GetImageOption(draw_info->image_info,"strokewidth");
6268 if (option != (const char *) NULL)
6269 draw_info->stroke_width=GetDrawValue(option,&next_token);
6270 option=GetImageOption(draw_info->image_info,"style");
6271 if (option != (const char *) NULL)
6272 draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
6273 MagickFalse,option);
6274 option=GetImageOption(draw_info->image_info,"undercolor");
6275 if (option != (const char *) NULL)
6276 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
6277 exception);
6278 option=GetImageOption(draw_info->image_info,"weight");
6279 if (option != (const char *) NULL)
6280 {
6281 ssize_t
6282 weight;
6283
6284 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
6285 if (weight == -1)
6286 weight=(ssize_t) StringToUnsignedLong(option);
6287 draw_info->weight=(size_t) weight;
6288 }
6289 option=GetImageOption(draw_info->image_info,"word-break");
6290 if (option != (const char *) NULL)
6291 draw_info->word_break=(WordBreakType) ParseCommandOption(
6292 MagickWordBreakOptions,MagickFalse,option);
6293 exception=DestroyExceptionInfo(exception);
6294 draw_info->signature=MagickCoreSignature;
6295}
6296␌
6297/*
6298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6299% %
6300% %
6301% %
6302+ P e r m u t a t e %
6303% %
6304% %
6305% %
6306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6307%
6308% Permutate() returns the permutation of the (n,k).
6309%
6310% The format of the Permutate method is:
6311%
6312% void Permutate(ssize_t n,ssize_t k)
6313%
6314% A description of each parameter follows:
6315%
6316% o n:
6317%
6318% o k:
6319%
6320%
6321*/
6322static inline double Permutate(const ssize_t n,const ssize_t k)
6323{
6324 double
6325 r;
6326
6327 ssize_t
6328 i;
6329
6330 r=1.0;
6331 for (i=k+1; i <= n; i++)
6332 r*=i;
6333 for (i=1; i <= (n-k); i++)
6334 r/=i;
6335 return(r);
6336}
6337␌
6338/*
6339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6340% %
6341% %
6342% %
6343+ T r a c e P r i m i t i v e %
6344% %
6345% %
6346% %
6347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6348%
6349% TracePrimitive is a collection of methods for generating graphic
6350% primitives such as arcs, ellipses, paths, etc.
6351%
6352*/
6353
6354static MagickBooleanType TraceArc(MVGInfo *mvg_info,const PointInfo start,
6355 const PointInfo end,const PointInfo degrees)
6356{
6357 PointInfo
6358 center,
6359 radius;
6360
6361 center.x=0.5*(end.x+start.x);
6362 center.y=0.5*(end.y+start.y);
6363 radius.x=fabs(center.x-start.x);
6364 radius.y=fabs(center.y-start.y);
6365 return(TraceEllipse(mvg_info,center,radius,degrees));
6366}
6367
6368static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,const PointInfo start,
6369 const PointInfo end,const PointInfo arc,const double angle,
6370 const MagickBooleanType large_arc,const MagickBooleanType sweep)
6371{
6372 double
6373 alpha,
6374 beta,
6375 delta,
6376 factor,
6377 gamma,
6378 theta;
6379
6380 MagickStatusType
6381 status;
6382
6383 PointInfo
6384 center,
6385 points[3],
6386 radii;
6387
6388 double
6389 cosine,
6390 sine;
6391
6393 *primitive_info;
6394
6396 *p;
6397
6398 ssize_t
6399 i;
6400
6401 size_t
6402 arc_segments;
6403
6404 ssize_t
6405 offset;
6406
6407 offset=mvg_info->offset;
6408 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6409 primitive_info->coordinates=0;
6410 if ((fabs(start.x-end.x) < MagickEpsilon) &&
6411 (fabs(start.y-end.y) < MagickEpsilon))
6412 return(TracePoint(primitive_info,end));
6413 radii.x=fabs(arc.x);
6414 radii.y=fabs(arc.y);
6415 if ((radii.x < MagickEpsilon) || (radii.y < MagickEpsilon))
6416 return(TraceLine(primitive_info,start,end));
6417 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
6418 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
6419 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
6420 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
6421 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
6422 (radii.y*radii.y);
6423 if (delta < MagickEpsilon)
6424 return(TraceLine(primitive_info,start,end));
6425 if (delta > 1.0)
6426 {
6427 radii.x*=sqrt((double) delta);
6428 radii.y*=sqrt((double) delta);
6429 }
6430 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
6431 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
6432 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
6433 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
6434 alpha=points[1].x-points[0].x;
6435 beta=points[1].y-points[0].y;
6436 if (fabs(alpha*alpha+beta*beta) < MagickEpsilon)
6437 return(TraceLine(primitive_info,start,end));
6438 factor=MagickSafeReciprocal(alpha*alpha+beta*beta)-0.25;
6439 if (factor <= 0.0)
6440 factor=0.0;
6441 else
6442 {
6443 factor=sqrt((double) factor);
6444 if (sweep == large_arc)
6445 factor=(-factor);
6446 }
6447 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
6448 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
6449 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
6450 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
6451 if ((theta < 0.0) && (sweep != MagickFalse))
6452 theta+=2.0*MagickPI;
6453 else
6454 if ((theta > 0.0) && (sweep == MagickFalse))
6455 theta-=2.0*MagickPI;
6456 arc_segments=(size_t) CastDoubleToSsizeT(ceil(fabs((double) (theta/(0.5*
6457 MagickPI+MagickEpsilon)))));
6458 status=MagickTrue;
6459 p=primitive_info;
6460 for (i=0; i < (ssize_t) arc_segments; i++)
6461 {
6462 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
6463 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
6464 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
6465 sin(fmod((double) beta,DegreesToRadians(360.0)));
6466 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
6467 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
6468 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6469 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
6470 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
6471 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6472 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
6473 theta/arc_segments),DegreesToRadians(360.0))));
6474 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
6475 theta/arc_segments),DegreesToRadians(360.0))));
6476 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
6477 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6478 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
6479 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6480 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
6481 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
6482 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
6483 points[0].y);
6484 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
6485 points[0].y);
6486 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
6487 points[1].y);
6488 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
6489 points[1].y);
6490 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
6491 points[2].y);
6492 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
6493 points[2].y);
6494 if (i == (ssize_t) (arc_segments-1))
6495 (p+3)->point=end;
6496 status&=(MagickStatusType) TraceBezier(mvg_info,4);
6497 if (status == 0)
6498 break;
6499 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
6500 mvg_info->offset+=(ssize_t) p->coordinates;
6501 p+=(ptrdiff_t) p->coordinates;
6502 }
6503 if (status == 0)
6504 return(MagickFalse);
6505 mvg_info->offset=offset;
6506 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6507 primitive_info->coordinates=(size_t) (p-primitive_info);
6508 primitive_info->closed_subpath=MagickFalse;
6509 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6510 {
6511 p->primitive=primitive_info->primitive;
6512 p--;
6513 }
6514 return(MagickTrue);
6515}
6516
6517static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
6518 const size_t number_coordinates)
6519{
6520 double
6521 alpha,
6522 *coefficients,
6523 weight;
6524
6525 PointInfo
6526 end,
6527 point,
6528 *points;
6529
6531 *primitive_info;
6532
6534 *p;
6535
6536 ssize_t
6537 i,
6538 j;
6539
6540 size_t
6541 control_points,
6542 quantum;
6543
6544 /*
6545 Allocate coefficients.
6546 */
6547 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6548 quantum=number_coordinates;
6549 for (i=0; i < (ssize_t) number_coordinates; i++)
6550 {
6551 for (j=i+1; j < (ssize_t) number_coordinates; j++)
6552 {
6553 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
6554 if (alpha > (double) GetMaxMemoryRequest())
6555 {
6556 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6557 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6558 return(MagickFalse);
6559 }
6560 if (alpha > (double) quantum)
6561 quantum=(size_t) alpha;
6562 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
6563 if (alpha > (double) quantum)
6564 quantum=(size_t) alpha;
6565 }
6566 }
6567 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6568 quantum=MagickMin(quantum/number_coordinates,BezierQuantum);
6569 if (quantum > (double) GetMaxMemoryRequest())
6570 {
6571 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6572 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6573 return(MagickFalse);
6574 }
6575 coefficients=(double *) AcquireQuantumMemory(number_coordinates,
6576 sizeof(*coefficients));
6577 points=(PointInfo *) AcquireQuantumMemory(quantum,number_coordinates*
6578 sizeof(*points));
6579 if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
6580 {
6581 if (points != (PointInfo *) NULL)
6582 points=(PointInfo *) RelinquishMagickMemory(points);
6583 if (coefficients != (double *) NULL)
6584 coefficients=(double *) RelinquishMagickMemory(coefficients);
6585 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6586 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6587 return(MagickFalse);
6588 }
6589 control_points=quantum*number_coordinates;
6590 if (CheckPrimitiveExtent(mvg_info,(double) control_points+1) == MagickFalse)
6591 {
6592 points=(PointInfo *) RelinquishMagickMemory(points);
6593 coefficients=(double *) RelinquishMagickMemory(coefficients);
6594 return(MagickFalse);
6595 }
6596 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6597 /*
6598 Compute bezier points.
6599 */
6600 end=primitive_info[number_coordinates-1].point;
6601 for (i=0; i < (ssize_t) number_coordinates; i++)
6602 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
6603 weight=0.0;
6604 for (i=0; i < (ssize_t) control_points; i++)
6605 {
6606 p=primitive_info;
6607 point.x=0.0;
6608 point.y=0.0;
6609 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
6610 for (j=0; j < (ssize_t) number_coordinates; j++)
6611 {
6612 point.x+=alpha*coefficients[j]*p->point.x;
6613 point.y+=alpha*coefficients[j]*p->point.y;
6614 alpha*=weight/(1.0-weight);
6615 p++;
6616 }
6617 points[i]=point;
6618 weight+=1.0/control_points;
6619 }
6620 /*
6621 Bezier curves are just short segmented polys.
6622 */
6623 p=primitive_info;
6624 for (i=0; i < (ssize_t) control_points; i++)
6625 {
6626 if (TracePoint(p,points[i]) == MagickFalse)
6627 {
6628 points=(PointInfo *) RelinquishMagickMemory(points);
6629 coefficients=(double *) RelinquishMagickMemory(coefficients);
6630 return(MagickFalse);
6631 }
6632 p+=(ptrdiff_t) p->coordinates;
6633 }
6634 if (TracePoint(p,end) == MagickFalse)
6635 {
6636 points=(PointInfo *) RelinquishMagickMemory(points);
6637 coefficients=(double *) RelinquishMagickMemory(coefficients);
6638 return(MagickFalse);
6639 }
6640 p+=(ptrdiff_t) p->coordinates;
6641 primitive_info->coordinates=(size_t) (p-primitive_info);
6642 primitive_info->closed_subpath=MagickFalse;
6643 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6644 {
6645 p->primitive=primitive_info->primitive;
6646 p--;
6647 }
6648 points=(PointInfo *) RelinquishMagickMemory(points);
6649 coefficients=(double *) RelinquishMagickMemory(coefficients);
6650 return(MagickTrue);
6651}
6652
6653static MagickBooleanType TraceCircle(MVGInfo *mvg_info,const PointInfo start,
6654 const PointInfo end)
6655{
6656 double
6657 alpha,
6658 beta,
6659 radius;
6660
6661 PointInfo
6662 offset,
6663 degrees;
6664
6665 alpha=end.x-start.x;
6666 beta=end.y-start.y;
6667 radius=hypot((double) alpha,(double) beta);
6668 offset.x=(double) radius;
6669 offset.y=(double) radius;
6670 degrees.x=0.0;
6671 degrees.y=360.0;
6672 return(TraceEllipse(mvg_info,start,offset,degrees));
6673}
6674
6675static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
6676 const PointInfo radii,const PointInfo arc)
6677{
6678 double
6679 coordinates,
6680 delta,
6681 step,
6682 x,
6683 y;
6684
6685 PointInfo
6686 angle,
6687 point;
6688
6690 *primitive_info;
6691
6693 *p;
6694
6695 ssize_t
6696 i;
6697
6698 /*
6699 Ellipses are just short segmented polys.
6700 */
6701 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6702 primitive_info->coordinates=0;
6703 if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
6704 return(MagickTrue);
6705 delta=MagickSafeReciprocal(MagickMax(radii.x,radii.y));
6706 step=MagickPI/(MagickPI*MagickSafeReciprocal(delta))/8.0;
6707 angle.x=DegreesToRadians(arc.x);
6708 y=arc.y;
6709 while (y < arc.x)
6710 y+=360.0;
6711 angle.y=DegreesToRadians(y);
6712 coordinates=ceil((angle.y-angle.x)/step+1.0);
6713 if (CheckPrimitiveExtent(mvg_info,coordinates+1) == MagickFalse)
6714 return(MagickFalse);
6715 i=0;
6716 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6717 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
6718 {
6719 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
6720 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
6721 if (i++ >= (ssize_t) coordinates)
6722 break;
6723 if (TracePoint(p,point) == MagickFalse)
6724 return(MagickFalse);
6725 p+=(ptrdiff_t) p->coordinates;
6726 }
6727 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
6728 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
6729 if (TracePoint(p,point) == MagickFalse)
6730 return(MagickFalse);
6731 p+=(ptrdiff_t) p->coordinates;
6732 primitive_info->coordinates=(size_t) (p-primitive_info);
6733 primitive_info->closed_subpath=MagickFalse;
6734 x=fabs(primitive_info[0].point.x-
6735 primitive_info[primitive_info->coordinates-1].point.x);
6736 y=fabs(primitive_info[0].point.y-
6737 primitive_info[primitive_info->coordinates-1].point.y);
6738 if ((x < MagickEpsilon) && (y < MagickEpsilon))
6739 primitive_info->closed_subpath=MagickTrue;
6740 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6741 {
6742 p->primitive=primitive_info->primitive;
6743 p--;
6744 }
6745 return(MagickTrue);
6746}
6747
6748static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
6749 const PointInfo start,const PointInfo end)
6750{
6751 if (TracePoint(primitive_info,start) == MagickFalse)
6752 return(MagickFalse);
6753 if (TracePoint(primitive_info+1,end) == MagickFalse)
6754 return(MagickFalse);
6755 (primitive_info+1)->primitive=primitive_info->primitive;
6756 primitive_info->coordinates=2;
6757 primitive_info->closed_subpath=MagickFalse;
6758 return(MagickTrue);
6759}
6760
6761static ssize_t TracePath(MVGInfo *mvg_info,const char *path,
6762 ExceptionInfo *exception)
6763{
6764 char
6765 *next_token,
6766 token[MagickPathExtent] = "";
6767
6768 const char
6769 *p;
6770
6771 double
6772 x,
6773 y;
6774
6775 int
6776 attribute,
6777 last_attribute;
6778
6779 MagickBooleanType
6780 status;
6781
6782 PointInfo
6783 end = {0.0, 0.0},
6784 points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
6785 point = {0.0, 0.0},
6786 start = {0.0, 0.0};
6787
6789 *primitive_info;
6790
6791 PrimitiveType
6792 primitive_type;
6793
6795 *q;
6796
6797 ssize_t
6798 i;
6799
6800 size_t
6801 number_coordinates,
6802 z_count;
6803
6804 ssize_t
6805 subpath_offset;
6806
6807 subpath_offset=mvg_info->offset;
6808 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6809 status=MagickTrue;
6810 attribute=0;
6811 number_coordinates=0;
6812 z_count=0;
6813 primitive_type=primitive_info->primitive;
6814 q=primitive_info;
6815 for (p=path; *p != '\0'; )
6816 {
6817 if (status == MagickFalse)
6818 break;
6819 while (isspace((int) ((unsigned char) *p)) != 0)
6820 p++;
6821 if (*p == '\0')
6822 break;
6823 last_attribute=attribute;
6824 attribute=(int) (*p++);
6825 switch (attribute)
6826 {
6827 case 'a':
6828 case 'A':
6829 {
6830 double
6831 angle = 0.0;
6832
6833 MagickBooleanType
6834 large_arc = MagickFalse,
6835 sweep = MagickFalse;
6836
6837 PointInfo
6838 arc = {0.0, 0.0};
6839
6840 /*
6841 Elliptical arc.
6842 */
6843 do
6844 {
6845 (void) GetNextToken(p,&p,MagickPathExtent,token);
6846 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6847 ThrowPointExpectedException(token,exception);
6848 if (*token == ',')
6849 (void) GetNextToken(p,&p,MagickPathExtent,token);
6850 arc.x=GetDrawValue(token,&next_token);
6851 if (token == next_token)
6852 ThrowPointExpectedException(token,exception);
6853 (void) GetNextToken(p,&p,MagickPathExtent,token);
6854 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6855 ThrowPointExpectedException(token,exception);
6856 if (*token == ',')
6857 (void) GetNextToken(p,&p,MagickPathExtent,token);
6858 arc.y=GetDrawValue(token,&next_token);
6859 if (token == next_token)
6860 ThrowPointExpectedException(token,exception);
6861 (void) GetNextToken(p,&p,MagickPathExtent,token);
6862 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6863 ThrowPointExpectedException(token,exception);
6864 if (*token == ',')
6865 (void) GetNextToken(p,&p,MagickPathExtent,token);
6866 angle=GetDrawValue(token,&next_token);
6867 if (token == next_token)
6868 ThrowPointExpectedException(token,exception);
6869 (void) GetNextToken(p,&p,MagickPathExtent,token);
6870 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6871 ThrowPointExpectedException(token,exception);
6872 if (*token == ',')
6873 (void) GetNextToken(p,&p,MagickPathExtent,token);
6874 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6875 (void) GetNextToken(p,&p,MagickPathExtent,token);
6876 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6877 ThrowPointExpectedException(token,exception);
6878 if (*token == ',')
6879 (void) GetNextToken(p,&p,MagickPathExtent,token);
6880 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6881 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6882 ThrowPointExpectedException(token,exception);
6883 if (*token == ',')
6884 (void) GetNextToken(p,&p,MagickPathExtent,token);
6885 (void) GetNextToken(p,&p,MagickPathExtent,token);
6886 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6887 ThrowPointExpectedException(token,exception);
6888 if (*token == ',')
6889 (void) GetNextToken(p,&p,MagickPathExtent,token);
6890 x=GetDrawValue(token,&next_token);
6891 if (token == next_token)
6892 ThrowPointExpectedException(token,exception);
6893 (void) GetNextToken(p,&p,MagickPathExtent,token);
6894 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6895 ThrowPointExpectedException(token,exception);
6896 if (*token == ',')
6897 (void) GetNextToken(p,&p,MagickPathExtent,token);
6898 y=GetDrawValue(token,&next_token);
6899 if (token == next_token)
6900 ThrowPointExpectedException(token,exception);
6901 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
6902 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
6903 if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
6904 return(-1);
6905 q=(*mvg_info->primitive_info)+mvg_info->offset;
6906 mvg_info->offset+=(ssize_t) q->coordinates;
6907 q+=(ptrdiff_t) q->coordinates;
6908 point=end;
6909 while (isspace((int) ((unsigned char) *p)) != 0)
6910 p++;
6911 if (*p == ',')
6912 p++;
6913 } while (IsValidPoint(p) != MagickFalse);
6914 break;
6915 }
6916 case 'c':
6917 case 'C':
6918 {
6919 /*
6920 Cubic Bézier curve.
6921 */
6922 do
6923 {
6924 points[0]=point;
6925 for (i=1; i < 4; i++)
6926 {
6927 (void) GetNextToken(p,&p,MagickPathExtent,token);
6928 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6929 ThrowPointExpectedException(token,exception);
6930 if (*token == ',')
6931 (void) GetNextToken(p,&p,MagickPathExtent,token);
6932 x=GetDrawValue(token,&next_token);
6933 if (token == next_token)
6934 ThrowPointExpectedException(token,exception);
6935 (void) GetNextToken(p,&p,MagickPathExtent,token);
6936 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6937 ThrowPointExpectedException(token,exception);
6938 if (*token == ',')
6939 (void) GetNextToken(p,&p,MagickPathExtent,token);
6940 y=GetDrawValue(token,&next_token);
6941 if (token == next_token)
6942 ThrowPointExpectedException(token,exception);
6943 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
6944 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
6945 points[i]=end;
6946 }
6947 for (i=0; i < 4; i++)
6948 (q+i)->point=points[i];
6949 if (TraceBezier(mvg_info,4) == MagickFalse)
6950 return(-1);
6951 q=(*mvg_info->primitive_info)+mvg_info->offset;
6952 mvg_info->offset+=(ssize_t) q->coordinates;
6953 q+=(ptrdiff_t) q->coordinates;
6954 point=end;
6955 while (isspace((int) ((unsigned char) *p)) != 0)
6956 p++;
6957 if (*p == ',')
6958 p++;
6959 } while (IsValidPoint(p) != MagickFalse);
6960 break;
6961 }
6962 case 'H':
6963 case 'h':
6964 {
6965 do
6966 {
6967 (void) GetNextToken(p,&p,MagickPathExtent,token);
6968 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6969 ThrowPointExpectedException(token,exception);
6970 if (*token == ',')
6971 (void) GetNextToken(p,&p,MagickPathExtent,token);
6972 x=GetDrawValue(token,&next_token);
6973 if (token == next_token)
6974 ThrowPointExpectedException(token,exception);
6975 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
6976 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6977 return(-1);
6978 q=(*mvg_info->primitive_info)+mvg_info->offset;
6979 if (TracePoint(q,point) == MagickFalse)
6980 return(-1);
6981 mvg_info->offset+=(ssize_t) q->coordinates;
6982 q+=(ptrdiff_t) q->coordinates;
6983 while (isspace((int) ((unsigned char) *p)) != 0)
6984 p++;
6985 if (*p == ',')
6986 p++;
6987 } while (IsValidPoint(p) != MagickFalse);
6988 break;
6989 }
6990 case 'l':
6991 case 'L':
6992 {
6993 /*
6994 Line to.
6995 */
6996 do
6997 {
6998 (void) GetNextToken(p,&p,MagickPathExtent,token);
6999 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7000 ThrowPointExpectedException(token,exception);
7001 if (*token == ',')
7002 (void) GetNextToken(p,&p,MagickPathExtent,token);
7003 x=GetDrawValue(token,&next_token);
7004 if (token == next_token)
7005 ThrowPointExpectedException(token,exception);
7006 (void) GetNextToken(p,&p,MagickPathExtent,token);
7007 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7008 ThrowPointExpectedException(token,exception);
7009 if (*token == ',')
7010 (void) GetNextToken(p,&p,MagickPathExtent,token);
7011 y=GetDrawValue(token,&next_token);
7012 if (token == next_token)
7013 ThrowPointExpectedException(token,exception);
7014 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
7015 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
7016 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7017 return(-1);
7018 q=(*mvg_info->primitive_info)+mvg_info->offset;
7019 if (TracePoint(q,point) == MagickFalse)
7020 return(-1);
7021 mvg_info->offset+=(ssize_t) q->coordinates;
7022 q+=(ptrdiff_t) q->coordinates;
7023 while (isspace((int) ((unsigned char) *p)) != 0)
7024 p++;
7025 if (*p == ',')
7026 p++;
7027 } while (IsValidPoint(p) != MagickFalse);
7028 break;
7029 }
7030 case 'M':
7031 case 'm':
7032 {
7033 /*
7034 Move to.
7035 */
7036 if (mvg_info->offset != subpath_offset)
7037 {
7038 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7039 primitive_info->coordinates=(size_t) (q-primitive_info);
7040 number_coordinates+=primitive_info->coordinates;
7041 primitive_info=q;
7042 subpath_offset=mvg_info->offset;
7043 }
7044 i=0;
7045 do
7046 {
7047 (void) GetNextToken(p,&p,MagickPathExtent,token);
7048 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7049 ThrowPointExpectedException(token,exception);
7050 if (*token == ',')
7051 (void) GetNextToken(p,&p,MagickPathExtent,token);
7052 x=GetDrawValue(token,&next_token);
7053 if (token == next_token)
7054 ThrowPointExpectedException(token,exception);
7055 (void) GetNextToken(p,&p,MagickPathExtent,token);
7056 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7057 ThrowPointExpectedException(token,exception);
7058 if (*token == ',')
7059 (void) GetNextToken(p,&p,MagickPathExtent,token);
7060 y=GetDrawValue(token,&next_token);
7061 if (token == next_token)
7062 ThrowPointExpectedException(token,exception);
7063 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
7064 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
7065 if (i == 0)
7066 start=point;
7067 i++;
7068 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7069 return(-1);
7070 q=(*mvg_info->primitive_info)+mvg_info->offset;
7071 if (TracePoint(q,point) == MagickFalse)
7072 return(-1);
7073 mvg_info->offset+=(ssize_t) q->coordinates;
7074 q+=(ptrdiff_t) q->coordinates;
7075 while (isspace((int) ((unsigned char) *p)) != 0)
7076 p++;
7077 if (*p == ',')
7078 p++;
7079 } while (IsValidPoint(p) != MagickFalse);
7080 break;
7081 }
7082 case 'q':
7083 case 'Q':
7084 {
7085 /*
7086 Quadratic Bézier curve.
7087 */
7088 do
7089 {
7090 points[0]=point;
7091 for (i=1; i < 3; i++)
7092 {
7093 (void) GetNextToken(p,&p,MagickPathExtent,token);
7094 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7095 ThrowPointExpectedException(token,exception);
7096 if (*token == ',')
7097 (void) GetNextToken(p,&p,MagickPathExtent,token);
7098 x=GetDrawValue(token,&next_token);
7099 if (token == next_token)
7100 ThrowPointExpectedException(token,exception);
7101 (void) GetNextToken(p,&p,MagickPathExtent,token);
7102 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7103 ThrowPointExpectedException(token,exception);
7104 if (*token == ',')
7105 (void) GetNextToken(p,&p,MagickPathExtent,token);
7106 y=GetDrawValue(token,&next_token);
7107 if (token == next_token)
7108 ThrowPointExpectedException(token,exception);
7109 if (*p == ',')
7110 p++;
7111 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
7112 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
7113 points[i]=end;
7114 }
7115 for (i=0; i < 3; i++)
7116 (q+i)->point=points[i];
7117 if (TraceBezier(mvg_info,3) == MagickFalse)
7118 return(-1);
7119 q=(*mvg_info->primitive_info)+mvg_info->offset;
7120 mvg_info->offset+=(ssize_t) q->coordinates;
7121 q+=(ptrdiff_t) q->coordinates;
7122 point=end;
7123 while (isspace((int) ((unsigned char) *p)) != 0)
7124 p++;
7125 if (*p == ',')
7126 p++;
7127 } while (IsValidPoint(p) != MagickFalse);
7128 break;
7129 }
7130 case 's':
7131 case 'S':
7132 {
7133 /*
7134 Cubic Bézier curve.
7135 */
7136 do
7137 {
7138 points[0]=points[3];
7139 points[1].x=2.0*points[3].x-points[2].x;
7140 points[1].y=2.0*points[3].y-points[2].y;
7141 for (i=2; i < 4; i++)
7142 {
7143 (void) GetNextToken(p,&p,MagickPathExtent,token);
7144 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7145 ThrowPointExpectedException(token,exception);
7146 if (*token == ',')
7147 (void) GetNextToken(p,&p,MagickPathExtent,token);
7148 x=GetDrawValue(token,&next_token);
7149 if (token == next_token)
7150 ThrowPointExpectedException(token,exception);
7151 (void) GetNextToken(p,&p,MagickPathExtent,token);
7152 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7153 ThrowPointExpectedException(token,exception);
7154 if (*token == ',')
7155 (void) GetNextToken(p,&p,MagickPathExtent,token);
7156 y=GetDrawValue(token,&next_token);
7157 if (token == next_token)
7158 ThrowPointExpectedException(token,exception);
7159 if (*p == ',')
7160 p++;
7161 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
7162 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
7163 points[i]=end;
7164 }
7165 if (strchr("CcSs",last_attribute) == (char *) NULL)
7166 {
7167 points[0]=point;
7168 points[1]=point;
7169 }
7170 for (i=0; i < 4; i++)
7171 (q+i)->point=points[i];
7172 if (TraceBezier(mvg_info,4) == MagickFalse)
7173 return(-1);
7174 q=(*mvg_info->primitive_info)+mvg_info->offset;
7175 mvg_info->offset+=(ssize_t) q->coordinates;
7176 q+=(ptrdiff_t) q->coordinates;
7177 point=end;
7178 last_attribute=attribute;
7179 while (isspace((int) ((unsigned char) *p)) != 0)
7180 p++;
7181 if (*p == ',')
7182 p++;
7183 } while (IsValidPoint(p) != MagickFalse);
7184 break;
7185 }
7186 case 't':
7187 case 'T':
7188 {
7189 /*
7190 Quadratic Bézier curve.
7191 */
7192 do
7193 {
7194 points[0]=points[2];
7195 points[1].x=2.0*points[2].x-points[1].x;
7196 points[1].y=2.0*points[2].y-points[1].y;
7197 for (i=2; i < 3; i++)
7198 {
7199 (void) GetNextToken(p,&p,MagickPathExtent,token);
7200 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7201 ThrowPointExpectedException(token,exception);
7202 if (*token == ',')
7203 (void) GetNextToken(p,&p,MagickPathExtent,token);
7204 x=GetDrawValue(token,&next_token);
7205 if (token == next_token)
7206 ThrowPointExpectedException(token,exception);
7207 (void) GetNextToken(p,&p,MagickPathExtent,token);
7208 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7209 ThrowPointExpectedException(token,exception);
7210 if (*token == ',')
7211 (void) GetNextToken(p,&p,MagickPathExtent,token);
7212 y=GetDrawValue(token,&next_token);
7213 if (token == next_token)
7214 ThrowPointExpectedException(token,exception);
7215 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
7216 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
7217 points[i]=end;
7218 }
7219 if (status == MagickFalse)
7220 break;
7221 if (strchr("QqTt",last_attribute) == (char *) NULL)
7222 {
7223 points[0]=point;
7224 points[1]=point;
7225 }
7226 for (i=0; i < 3; i++)
7227 (q+i)->point=points[i];
7228 if (TraceBezier(mvg_info,3) == MagickFalse)
7229 return(-1);
7230 q=(*mvg_info->primitive_info)+mvg_info->offset;
7231 mvg_info->offset+=(ssize_t) q->coordinates;
7232 q+=(ptrdiff_t) q->coordinates;
7233 point=end;
7234 last_attribute=attribute;
7235 while (isspace((int) ((unsigned char) *p)) != 0)
7236 p++;
7237 if (*p == ',')
7238 p++;
7239 } while (IsValidPoint(p) != MagickFalse);
7240 break;
7241 }
7242 case 'v':
7243 case 'V':
7244 {
7245 /*
7246 Line to.
7247 */
7248 do
7249 {
7250 (void) GetNextToken(p,&p,MagickPathExtent,token);
7251 if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7252 ThrowPointExpectedException(token,exception);
7253 if (*token == ',')
7254 (void) GetNextToken(p,&p,MagickPathExtent,token);
7255 y=GetDrawValue(token,&next_token);
7256 if (token == next_token)
7257 ThrowPointExpectedException(token,exception);
7258 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
7259 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7260 return(-1);
7261 q=(*mvg_info->primitive_info)+mvg_info->offset;
7262 if (TracePoint(q,point) == MagickFalse)
7263 return(-1);
7264 mvg_info->offset+=(ssize_t) q->coordinates;
7265 q+=(ptrdiff_t) q->coordinates;
7266 while (isspace((int) ((unsigned char) *p)) != 0)
7267 p++;
7268 if (*p == ',')
7269 p++;
7270 } while (IsValidPoint(p) != MagickFalse);
7271 break;
7272 }
7273 case 'z':
7274 case 'Z':
7275 {
7276 /*
7277 Close path.
7278 */
7279 point=start;
7280 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7281 return(-1);
7282 q=(*mvg_info->primitive_info)+mvg_info->offset;
7283 if (TracePoint(q,point) == MagickFalse)
7284 return(-1);
7285 mvg_info->offset+=(ssize_t) q->coordinates;
7286 q+=(ptrdiff_t) q->coordinates;
7287 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7288 primitive_info->coordinates=(size_t) (q-primitive_info);
7289 primitive_info->closed_subpath=MagickTrue;
7290 number_coordinates+=primitive_info->coordinates;
7291 primitive_info=q;
7292 subpath_offset=mvg_info->offset;
7293 z_count++;
7294 break;
7295 }
7296 default:
7297 {
7298 ThrowPointExpectedException(token,exception);
7299 break;
7300 }
7301 }
7302 }
7303 if (status == MagickFalse)
7304 return(-1);
7305 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7306 primitive_info->coordinates=(size_t) (q-primitive_info);
7307 number_coordinates+=primitive_info->coordinates;
7308 for (i=0; i < (ssize_t) number_coordinates; i++)
7309 {
7310 q--;
7311 q->primitive=primitive_type;
7312 if (z_count > 1)
7313 q->method=FillToBorderMethod;
7314 }
7315 q=primitive_info;
7316 return((ssize_t) number_coordinates);
7317}
7318
7319static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
7320 const PointInfo start,const PointInfo end)
7321{
7322 PointInfo
7323 point;
7324
7326 *p;
7327
7328 ssize_t
7329 i;
7330
7331 p=primitive_info;
7332 if (TracePoint(p,start) == MagickFalse)
7333 return(MagickFalse);
7334 p+=(ptrdiff_t) p->coordinates;
7335 point.x=start.x;
7336 point.y=end.y;
7337 if (TracePoint(p,point) == MagickFalse)
7338 return(MagickFalse);
7339 p+=(ptrdiff_t) p->coordinates;
7340 if (TracePoint(p,end) == MagickFalse)
7341 return(MagickFalse);
7342 p+=(ptrdiff_t) p->coordinates;
7343 point.x=end.x;
7344 point.y=start.y;
7345 if (TracePoint(p,point) == MagickFalse)
7346 return(MagickFalse);
7347 p+=(ptrdiff_t) p->coordinates;
7348 if (TracePoint(p,start) == MagickFalse)
7349 return(MagickFalse);
7350 p+=(ptrdiff_t) p->coordinates;
7351 primitive_info->coordinates=(size_t) (p-primitive_info);
7352 primitive_info->closed_subpath=MagickTrue;
7353 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7354 {
7355 p->primitive=primitive_info->primitive;
7356 p--;
7357 }
7358 return(MagickTrue);
7359}
7360
7361static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
7362 const PointInfo start,const PointInfo end,PointInfo arc)
7363{
7364 PointInfo
7365 degrees,
7366 point,
7367 segment;
7368
7370 *primitive_info;
7371
7373 *p;
7374
7375 ssize_t
7376 i;
7377
7378 ssize_t
7379 offset;
7380
7381 offset=mvg_info->offset;
7382 segment.x=fabs(end.x-start.x);
7383 segment.y=fabs(end.y-start.y);
7384 if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
7385 {
7386 (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
7387 return(MagickTrue);
7388 }
7389 if (arc.x > (0.5*segment.x))
7390 arc.x=0.5*segment.x;
7391 if (arc.y > (0.5*segment.y))
7392 arc.y=0.5*segment.y;
7393 point.x=start.x+segment.x-arc.x;
7394 point.y=start.y+arc.y;
7395 degrees.x=270.0;
7396 degrees.y=360.0;
7397 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7398 return(MagickFalse);
7399 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7400 mvg_info->offset+=(ssize_t) p->coordinates;
7401 point.x=start.x+segment.x-arc.x;
7402 point.y=start.y+segment.y-arc.y;
7403 degrees.x=0.0;
7404 degrees.y=90.0;
7405 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7406 return(MagickFalse);
7407 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7408 mvg_info->offset+=(ssize_t) p->coordinates;
7409 point.x=start.x+arc.x;
7410 point.y=start.y+segment.y-arc.y;
7411 degrees.x=90.0;
7412 degrees.y=180.0;
7413 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7414 return(MagickFalse);
7415 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7416 mvg_info->offset+=(ssize_t) p->coordinates;
7417 point.x=start.x+arc.x;
7418 point.y=start.y+arc.y;
7419 degrees.x=180.0;
7420 degrees.y=270.0;
7421 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7422 return(MagickFalse);
7423 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7424 mvg_info->offset+=(ssize_t) p->coordinates;
7425 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7426 return(MagickFalse);
7427 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7428 if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
7429 return(MagickFalse);
7430 p+=(ptrdiff_t) p->coordinates;
7431 mvg_info->offset=offset;
7432 primitive_info=(*mvg_info->primitive_info)+offset;
7433 primitive_info->coordinates=(size_t) (p-primitive_info);
7434 primitive_info->closed_subpath=MagickTrue;
7435 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7436 {
7437 p->primitive=primitive_info->primitive;
7438 p--;
7439 }
7440 return(MagickTrue);
7441}
7442
7443static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
7444 const size_t number_vertices,const double offset)
7445{
7446 double
7447 distance;
7448
7449 double
7450 dx,
7451 dy;
7452
7453 ssize_t
7454 i;
7455
7456 ssize_t
7457 j;
7458
7459 dx=0.0;
7460 dy=0.0;
7461 for (i=1; i < (ssize_t) number_vertices; i++)
7462 {
7463 dx=primitive_info[0].point.x-primitive_info[i].point.x;
7464 dy=primitive_info[0].point.y-primitive_info[i].point.y;
7465 if ((fabs((double) dx) >= MagickEpsilon) ||
7466 (fabs((double) dy) >= MagickEpsilon))
7467 break;
7468 }
7469 if (i == (ssize_t) number_vertices)
7470 i=(ssize_t) number_vertices-1L;
7471 distance=hypot((double) dx,(double) dy);
7472 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
7473 dx*(distance+offset)/distance);
7474 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
7475 dy*(distance+offset)/distance);
7476 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
7477 {
7478 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
7479 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
7480 if ((fabs((double) dx) >= MagickEpsilon) ||
7481 (fabs((double) dy) >= MagickEpsilon))
7482 break;
7483 }
7484 distance=hypot((double) dx,(double) dy);
7485 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
7486 dx*(distance+offset)/distance);
7487 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
7488 dy*(distance+offset)/distance);
7489 return(MagickTrue);
7490}
7491
7492static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
7493 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
7494{
7495#define MaxStrokePad (6*BezierQuantum+360)
7496#define CheckPathExtent(pad_p,pad_q) \
7497{ \
7498 if ((pad_p) > MaxBezierCoordinates) \
7499 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7500 else \
7501 if ((p+(ptrdiff_t) (pad_p)) >= (ssize_t) extent_p) \
7502 { \
7503 if (~extent_p < (pad_p)) \
7504 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7505 else \
7506 { \
7507 extent_p+=(pad_p); \
7508 stroke_p=(PointInfo *) ResizeQuantumMemory(stroke_p,extent_p+ \
7509 MaxStrokePad,sizeof(*stroke_p)); \
7510 } \
7511 } \
7512 if ((pad_q) > MaxBezierCoordinates) \
7513 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7514 else \
7515 if ((q+(ptrdiff_t) (pad_q)) >= (ssize_t) extent_q) \
7516 { \
7517 if (~extent_q < (pad_q)) \
7518 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7519 else \
7520 { \
7521 extent_q+=(pad_q); \
7522 stroke_q=(PointInfo *) ResizeQuantumMemory(stroke_q,extent_q+ \
7523 MaxStrokePad,sizeof(*stroke_q)); \
7524 } \
7525 } \
7526 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL)) \
7527 { \
7528 if (stroke_p != (PointInfo *) NULL) \
7529 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7530 if (stroke_q != (PointInfo *) NULL) \
7531 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7532 polygon_primitive=(PrimitiveInfo *) \
7533 RelinquishMagickMemory(polygon_primitive); \
7534 (void) ThrowMagickException(exception,GetMagickModule(), \
7535 ResourceLimitError,"MemoryAllocationFailed","`%s'",""); \
7536 return((PrimitiveInfo *) NULL); \
7537 } \
7538}
7539
7540 typedef struct _StrokeSegment
7541 {
7542 double
7543 p,
7544 q;
7545 } StrokeSegment;
7546
7547 double
7548 delta_theta,
7549 dot_product,
7550 mid,
7551 miterlimit;
7552
7553 MagickBooleanType
7554 closed_path;
7555
7556 PointInfo
7557 box_p[5],
7558 box_q[5],
7559 center,
7560 offset,
7561 *stroke_p,
7562 *stroke_q;
7563
7565 *polygon_primitive,
7566 *stroke_polygon;
7567
7568 ssize_t
7569 i;
7570
7571 size_t
7572 arc_segments,
7573 extent_p,
7574 extent_q,
7575 number_vertices;
7576
7577 ssize_t
7578 j,
7579 n,
7580 p,
7581 q;
7582
7583 StrokeSegment
7584 dx = {0.0, 0.0},
7585 dy = {0.0, 0.0},
7586 inverse_slope = {0.0, 0.0},
7587 slope = {0.0, 0.0},
7588 theta = {0.0, 0.0};
7589
7590 /*
7591 Allocate paths.
7592 */
7593 number_vertices=primitive_info->coordinates;
7594 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7595 number_vertices+2UL,sizeof(*polygon_primitive));
7596 if (polygon_primitive == (PrimitiveInfo *) NULL)
7597 {
7598 (void) ThrowMagickException(exception,GetMagickModule(),
7599 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7600 return((PrimitiveInfo *) NULL);
7601 }
7602 (void) memcpy(polygon_primitive,primitive_info,(size_t) number_vertices*
7603 sizeof(*polygon_primitive));
7604 offset.x=primitive_info[number_vertices-1].point.x-primitive_info[0].point.x;
7605 offset.y=primitive_info[number_vertices-1].point.y-primitive_info[0].point.y;
7606 closed_path=(fabs(offset.x) < MagickEpsilon) &&
7607 (fabs(offset.y) < MagickEpsilon) ? MagickTrue : MagickFalse;
7608 if ((draw_info->linejoin == RoundJoin) ||
7609 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
7610 {
7611 polygon_primitive[number_vertices]=primitive_info[1];
7612 number_vertices++;
7613 }
7614 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
7615 /*
7616 Compute the slope for the first line segment, p.
7617 */
7618 closed_path=primitive_info[0].closed_subpath;
7619 dx.p=0.0;
7620 dy.p=0.0;
7621 for (n=1; n < (ssize_t) number_vertices; n++)
7622 {
7623 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
7624 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
7625 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
7626 break;
7627 }
7628 if (n == (ssize_t) number_vertices)
7629 {
7630 if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
7631 {
7632 /*
7633 Zero length subpath.
7634 */
7635 stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
7636 sizeof(*stroke_polygon));
7637 stroke_polygon[0]=polygon_primitive[0];
7638 stroke_polygon[0].coordinates=0;
7639 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7640 polygon_primitive);
7641 return(stroke_polygon);
7642 }
7643 n=(ssize_t) number_vertices-1L;
7644 }
7645 extent_p=2*number_vertices;
7646 extent_q=2*number_vertices;
7647 stroke_p=(PointInfo *) AcquireQuantumMemory((size_t) extent_p+MaxStrokePad,
7648 sizeof(*stroke_p));
7649 stroke_q=(PointInfo *) AcquireQuantumMemory((size_t) extent_q+MaxStrokePad,
7650 sizeof(*stroke_q));
7651 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL))
7652 {
7653 if (stroke_p != (PointInfo *) NULL)
7654 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7655 if (stroke_q != (PointInfo *) NULL)
7656 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7657 polygon_primitive=(PrimitiveInfo *)
7658 RelinquishMagickMemory(polygon_primitive);
7659 (void) ThrowMagickException(exception,GetMagickModule(),
7660 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7661 return((PrimitiveInfo *) NULL);
7662 }
7663 slope.p=0.0;
7664 inverse_slope.p=0.0;
7665 if (fabs(dx.p) < MagickEpsilon)
7666 {
7667 if (dx.p >= 0.0)
7668 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7669 else
7670 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7671 }
7672 else
7673 if (fabs(dy.p) < MagickEpsilon)
7674 {
7675 if (dy.p >= 0.0)
7676 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7677 else
7678 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7679 }
7680 else
7681 {
7682 slope.p=dy.p/dx.p;
7683 inverse_slope.p=(-1.0*MagickSafeReciprocal(slope.p));
7684 }
7685 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
7686 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
7687 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
7688 (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
7689 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
7690 offset.y=(double) (offset.x*inverse_slope.p);
7691 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
7692 {
7693 box_p[0].x=polygon_primitive[0].point.x-offset.x;
7694 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
7695 box_p[1].x=polygon_primitive[n].point.x-offset.x;
7696 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
7697 box_q[0].x=polygon_primitive[0].point.x+offset.x;
7698 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
7699 box_q[1].x=polygon_primitive[n].point.x+offset.x;
7700 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
7701 }
7702 else
7703 {
7704 box_p[0].x=polygon_primitive[0].point.x+offset.x;
7705 box_p[0].y=polygon_primitive[0].point.y+offset.y;
7706 box_p[1].x=polygon_primitive[n].point.x+offset.x;
7707 box_p[1].y=polygon_primitive[n].point.y+offset.y;
7708 box_q[0].x=polygon_primitive[0].point.x-offset.x;
7709 box_q[0].y=polygon_primitive[0].point.y-offset.y;
7710 box_q[1].x=polygon_primitive[n].point.x-offset.x;
7711 box_q[1].y=polygon_primitive[n].point.y-offset.y;
7712 }
7713 /*
7714 Create strokes for the line join attribute: bevel, miter, round.
7715 */
7716 p=0;
7717 q=0;
7718 stroke_q[p++]=box_q[0];
7719 stroke_p[q++]=box_p[0];
7720 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
7721 {
7722 /*
7723 Compute the slope for this line segment, q.
7724 */
7725 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
7726 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
7727 dot_product=dx.q*dx.q+dy.q*dy.q;
7728 if (dot_product < 0.25)
7729 continue;
7730 slope.q=0.0;
7731 inverse_slope.q=0.0;
7732 if (fabs(dx.q) < MagickEpsilon)
7733 {
7734 if (dx.q >= 0.0)
7735 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7736 else
7737 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7738 }
7739 else
7740 if (fabs(dy.q) < MagickEpsilon)
7741 {
7742 if (dy.q >= 0.0)
7743 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7744 else
7745 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7746 }
7747 else
7748 {
7749 slope.q=dy.q/dx.q;
7750 inverse_slope.q=(-1.0*MagickSafeReciprocal(slope.q));
7751 }
7752 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
7753 offset.y=(double) (offset.x*inverse_slope.q);
7754 dot_product=dy.q*offset.x-dx.q*offset.y;
7755 if (dot_product > 0.0)
7756 {
7757 box_p[2].x=polygon_primitive[n].point.x-offset.x;
7758 box_p[2].y=polygon_primitive[n].point.y-offset.y;
7759 box_p[3].x=polygon_primitive[i].point.x-offset.x;
7760 box_p[3].y=polygon_primitive[i].point.y-offset.y;
7761 box_q[2].x=polygon_primitive[n].point.x+offset.x;
7762 box_q[2].y=polygon_primitive[n].point.y+offset.y;
7763 box_q[3].x=polygon_primitive[i].point.x+offset.x;
7764 box_q[3].y=polygon_primitive[i].point.y+offset.y;
7765 }
7766 else
7767 {
7768 box_p[2].x=polygon_primitive[n].point.x+offset.x;
7769 box_p[2].y=polygon_primitive[n].point.y+offset.y;
7770 box_p[3].x=polygon_primitive[i].point.x+offset.x;
7771 box_p[3].y=polygon_primitive[i].point.y+offset.y;
7772 box_q[2].x=polygon_primitive[n].point.x-offset.x;
7773 box_q[2].y=polygon_primitive[n].point.y-offset.y;
7774 box_q[3].x=polygon_primitive[i].point.x-offset.x;
7775 box_q[3].y=polygon_primitive[i].point.y-offset.y;
7776 }
7777 if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
7778 {
7779 box_p[4]=box_p[1];
7780 box_q[4]=box_q[1];
7781 }
7782 else
7783 {
7784 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
7785 box_p[3].y)/(slope.p-slope.q));
7786 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
7787 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
7788 box_q[3].y)/(slope.p-slope.q));
7789 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
7790 }
7791 DisableMSCWarning(4127)
7792 CheckPathExtent(MaxStrokePad,MaxStrokePad);
7793 RestoreMSCWarning
7794 dot_product=dx.q*dy.p-dx.p*dy.q;
7795 if (dot_product <= 0.0)
7796 switch (draw_info->linejoin)
7797 {
7798 case BevelJoin:
7799 {
7800 stroke_q[q++]=box_q[1];
7801 stroke_q[q++]=box_q[2];
7802 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7803 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7804 if (dot_product <= miterlimit)
7805 stroke_p[p++]=box_p[4];
7806 else
7807 {
7808 stroke_p[p++]=box_p[1];
7809 stroke_p[p++]=box_p[2];
7810 }
7811 break;
7812 }
7813 case MiterJoin:
7814 {
7815 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7816 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7817 if (dot_product <= miterlimit)
7818 {
7819 stroke_q[q++]=box_q[4];
7820 stroke_p[p++]=box_p[4];
7821 }
7822 else
7823 {
7824 stroke_q[q++]=box_q[1];
7825 stroke_q[q++]=box_q[2];
7826 stroke_p[p++]=box_p[1];
7827 stroke_p[p++]=box_p[2];
7828 }
7829 break;
7830 }
7831 case RoundJoin:
7832 {
7833 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7834 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7835 if (dot_product <= miterlimit)
7836 stroke_p[p++]=box_p[4];
7837 else
7838 {
7839 stroke_p[p++]=box_p[1];
7840 stroke_p[p++]=box_p[2];
7841 }
7842 center=polygon_primitive[n].point;
7843 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
7844 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
7845 if (theta.q < theta.p)
7846 theta.q+=2.0*MagickPI;
7847 arc_segments=(size_t) CastDoubleToSsizeT(ceil((double) ((theta.q-
7848 theta.p)/(2.0*sqrt(MagickSafeReciprocal(mid))))));
7849 DisableMSCWarning(4127)
7850 CheckPathExtent(MaxStrokePad,arc_segments+MaxStrokePad);
7851 RestoreMSCWarning
7852 stroke_q[q].x=box_q[1].x;
7853 stroke_q[q].y=box_q[1].y;
7854 q++;
7855 for (j=1; j < (ssize_t) arc_segments; j++)
7856 {
7857 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7858 stroke_q[q].x=(double) (center.x+mid*cos(fmod((double)
7859 (theta.p+delta_theta),DegreesToRadians(360.0))));
7860 stroke_q[q].y=(double) (center.y+mid*sin(fmod((double)
7861 (theta.p+delta_theta),DegreesToRadians(360.0))));
7862 q++;
7863 }
7864 stroke_q[q++]=box_q[2];
7865 break;
7866 }
7867 default:
7868 break;
7869 }
7870 else
7871 switch (draw_info->linejoin)
7872 {
7873 case BevelJoin:
7874 {
7875 stroke_p[p++]=box_p[1];
7876 stroke_p[p++]=box_p[2];
7877 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7878 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7879 if (dot_product <= miterlimit)
7880 stroke_q[q++]=box_q[4];
7881 else
7882 {
7883 stroke_q[q++]=box_q[1];
7884 stroke_q[q++]=box_q[2];
7885 }
7886 break;
7887 }
7888 case MiterJoin:
7889 {
7890 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7891 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7892 if (dot_product <= miterlimit)
7893 {
7894 stroke_q[q++]=box_q[4];
7895 stroke_p[p++]=box_p[4];
7896 }
7897 else
7898 {
7899 stroke_q[q++]=box_q[1];
7900 stroke_q[q++]=box_q[2];
7901 stroke_p[p++]=box_p[1];
7902 stroke_p[p++]=box_p[2];
7903 }
7904 break;
7905 }
7906 case RoundJoin:
7907 {
7908 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7909 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7910 if (dot_product <= miterlimit)
7911 stroke_q[q++]=box_q[4];
7912 else
7913 {
7914 stroke_q[q++]=box_q[1];
7915 stroke_q[q++]=box_q[2];
7916 }
7917 center=polygon_primitive[n].point;
7918 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
7919 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
7920 if (theta.p < theta.q)
7921 theta.p+=2.0*MagickPI;
7922 arc_segments=(size_t) CastDoubleToSsizeT(ceil((double) ((theta.p-
7923 theta.q)/(2.0*sqrt((double) (MagickSafeReciprocal(mid)))))));
7924 DisableMSCWarning(4127)
7925 CheckPathExtent(arc_segments+MaxStrokePad,MaxStrokePad);
7926 RestoreMSCWarning
7927 stroke_p[p++]=box_p[1];
7928 for (j=1; j < (ssize_t) arc_segments; j++)
7929 {
7930 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7931 stroke_p[p].x=(double) (center.x+mid*cos(fmod((double)
7932 (theta.p+delta_theta),DegreesToRadians(360.0))));
7933 stroke_p[p].y=(double) (center.y+mid*sin(fmod((double)
7934 (theta.p+delta_theta),DegreesToRadians(360.0))));
7935 p++;
7936 }
7937 stroke_p[p++]=box_p[2];
7938 break;
7939 }
7940 default:
7941 break;
7942 }
7943 slope.p=slope.q;
7944 inverse_slope.p=inverse_slope.q;
7945 box_p[0]=box_p[2];
7946 box_p[1]=box_p[3];
7947 box_q[0]=box_q[2];
7948 box_q[1]=box_q[3];
7949 dx.p=dx.q;
7950 dy.p=dy.q;
7951 n=i;
7952 }
7953 stroke_p[p++]=box_p[1];
7954 stroke_q[q++]=box_q[1];
7955 /*
7956 Trace stroked polygon.
7957 */
7958 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7959 (p+q+2L),(size_t) (closed_path+2L)*sizeof(*stroke_polygon));
7960 if (stroke_polygon == (PrimitiveInfo *) NULL)
7961 {
7962 (void) ThrowMagickException(exception,GetMagickModule(),
7963 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7964 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7965 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7966 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7967 polygon_primitive);
7968 return(stroke_polygon);
7969 }
7970 for (i=0; i < (ssize_t) p; i++)
7971 {
7972 stroke_polygon[i]=polygon_primitive[0];
7973 stroke_polygon[i].point=stroke_p[i];
7974 }
7975 if (closed_path != MagickFalse)
7976 {
7977 stroke_polygon[i]=polygon_primitive[0];
7978 stroke_polygon[i].point=stroke_polygon[0].point;
7979 i++;
7980 }
7981 for ( ; i < (ssize_t) (p+q+closed_path); i++)
7982 {
7983 stroke_polygon[i]=polygon_primitive[0];
7984 stroke_polygon[i].point=stroke_q[p+q+closed_path-(i+1)];
7985 }
7986 if (closed_path != MagickFalse)
7987 {
7988 stroke_polygon[i]=polygon_primitive[0];
7989 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
7990 i++;
7991 }
7992 stroke_polygon[i]=polygon_primitive[0];
7993 stroke_polygon[i].point=stroke_polygon[0].point;
7994 i++;
7995 stroke_polygon[i].primitive=UndefinedPrimitive;
7996 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
7997 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7998 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7999 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
8000 return(stroke_polygon);
8001}