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