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