MagickCore 7.1.1-43
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
draw.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD RRRR AAA W W %
7% D D R R A A W W %
8% D D RRRR AAAAA W W W %
9% D D R RN A A WW WW %
10% DDDD R R A A W W %
11% %
12% %
13% MagickCore Image Drawing Methods %
14% %
15% %
16% Software Design %
17% Cristy %
18% July 1998 %
19% %
20% %
21% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
22% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% https://imagemagick.org/script/license.php %
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{
111 bounds;
112
113 double
114 scanline;
115
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;
141
142typedef struct _MVGInfo
143{
145 **primitive_info;
146
147 size_t
148 *extent;
149
150 ssize_t
151 offset;
152
154 point;
155
157 *exception;
158} MVGInfo;
159
160typedef struct _PolygonInfo
161{
163 *edges;
164
165 size_t
166 number_edges;
168
169typedef enum
170{
171 MoveToCode,
172 OpenCode,
173 GhostlineCode,
174 LineToCode,
175 EndCode
176} PathInfoCode;
177
178typedef struct _PathInfo
179{
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{
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{
274 *clone_info;
275
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) (2*x+2),
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) (2*x+2)*
350 sizeof(*clone_info->dash_pattern));
351 (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(size_t)
352 (x+1)*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{
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{
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
526 point,
527 *points;
528
530 *polygon_info;
531
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 {
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
843 *path_info;
844
845 PathInfoCode
846 code;
847
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
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{
1138 inverse_affine;
1139
1140 double
1141 determinant;
1142
1143 determinant=PerceptibleReciprocal(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{
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
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 > (image->rows-1.0))
1242 edge.y2=image->rows-1.0;
1243 GetPixelInfo(image,&zero);
1244 start=CastDoubleToLong(ceil(edge.y1-0.5));
1245 stop=CastDoubleToLong(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
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 > image->columns-1.0)
1278 inverse_edge.x2=image->columns-1.0;
1279 q=GetCacheViewAuthenticPixels(image_view,CastDoubleToLong(
1280 ceil(inverse_edge.x1-0.5)),y,(size_t) CastDoubleToLong(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=CastDoubleToLong(ceil(inverse_edge.x1-0.5));
1287 x <= CastDoubleToLong(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
1362 primitive_info[6];
1363
1364 ssize_t
1365 i;
1366
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 {
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
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 scale=ExpandAffine(&draw_info->affine);
1802 length=scale*draw_info->dash_pattern[0];
1803 offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
1804 scale*draw_info->dash_offset : 0.0;
1805 j=1;
1806 for (n=0; offset > 0.0; j=0)
1807 {
1808 if (draw_info->dash_pattern[n] <= 0.0)
1809 break;
1810 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1811 if (offset > length)
1812 {
1813 offset-=length;
1814 n++;
1815 length=scale*draw_info->dash_pattern[n];
1816 continue;
1817 }
1818 if (offset < length)
1819 {
1820 length-=offset;
1821 offset=0.0;
1822 break;
1823 }
1824 offset=0.0;
1825 n++;
1826 }
1827 status=MagickTrue;
1828 maximum_length=0.0;
1829 total_length=0.0;
1830 for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
1831 {
1832 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1833 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1834 maximum_length=hypot(dx,dy);
1835 if (maximum_length > (double) (MaxBezierCoordinates >> 2))
1836 continue;
1837 if (fabs(length) < MagickEpsilon)
1838 {
1839 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1840 n++;
1841 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1842 n=0;
1843 length=scale*draw_info->dash_pattern[n];
1844 }
1845 for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
1846 {
1847 total_length+=length;
1848 if ((n & 0x01) != 0)
1849 {
1850 dash_polygon[0]=primitive_info[0];
1851 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1852 total_length*PerceptibleReciprocal(maximum_length));
1853 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1854 total_length*PerceptibleReciprocal(maximum_length));
1855 j=1;
1856 }
1857 else
1858 {
1859 if ((j+1) > (ssize_t) number_vertices)
1860 break;
1861 dash_polygon[j]=primitive_info[i-1];
1862 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1863 total_length*PerceptibleReciprocal(maximum_length));
1864 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1865 total_length*PerceptibleReciprocal(maximum_length));
1866 dash_polygon[j].coordinates=1;
1867 j++;
1868 dash_polygon[0].coordinates=(size_t) j;
1869 dash_polygon[j].primitive=UndefinedPrimitive;
1870 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1871 dash_polygon,exception);
1872 if (status == MagickFalse)
1873 break;
1874 }
1875 if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1876 n++;
1877 if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1878 n=0;
1879 length=scale*draw_info->dash_pattern[n];
1880 }
1881 length-=(maximum_length-total_length);
1882 if ((n & 0x01) != 0)
1883 continue;
1884 dash_polygon[j]=primitive_info[i];
1885 dash_polygon[j].coordinates=1;
1886 j++;
1887 }
1888 if ((status != MagickFalse) && (total_length < maximum_length) &&
1889 ((n & 0x01) == 0) && (j > 1))
1890 {
1891 dash_polygon[j]=primitive_info[i-1];
1892 dash_polygon[j].point.x+=MagickEpsilon;
1893 dash_polygon[j].point.y+=MagickEpsilon;
1894 dash_polygon[j].coordinates=1;
1895 j++;
1896 dash_polygon[0].coordinates=(size_t) j;
1897 dash_polygon[j].primitive=UndefinedPrimitive;
1898 status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1899 dash_polygon,exception);
1900 }
1901 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1902 clone_info=DestroyDrawInfo(clone_info);
1903 if (draw_info->debug != MagickFalse)
1904 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1905 return(status != 0 ? MagickTrue : MagickFalse);
1906}
1907
1908/*
1909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1910% %
1911% %
1912% %
1913% D r a w G r a d i e n t I m a g e %
1914% %
1915% %
1916% %
1917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918%
1919% DrawGradientImage() draws a linear gradient on the image.
1920%
1921% The format of the DrawGradientImage method is:
1922%
1923% MagickBooleanType DrawGradientImage(Image *image,
1924% const DrawInfo *draw_info,ExceptionInfo *exception)
1925%
1926% A description of each parameter follows:
1927%
1928% o image: the image.
1929%
1930% o draw_info: the draw info.
1931%
1932% o exception: return any errors or warnings in this structure.
1933%
1934*/
1935
1936static inline double GetStopColorOffset(const GradientInfo *gradient,
1937 const ssize_t x,const ssize_t y)
1938{
1939 switch (gradient->type)
1940 {
1941 case UndefinedGradient:
1942 case LinearGradient:
1943 {
1944 double
1945 gamma,
1946 length,
1947 offset,
1948 scale;
1949
1950 PointInfo
1951 p,
1952 q;
1953
1954 const SegmentInfo
1955 *gradient_vector;
1956
1957 gradient_vector=(&gradient->gradient_vector);
1958 p.x=gradient_vector->x2-gradient_vector->x1;
1959 p.y=gradient_vector->y2-gradient_vector->y1;
1960 q.x=(double) x-gradient_vector->x1;
1961 q.y=(double) y-gradient_vector->y1;
1962 length=sqrt(q.x*q.x+q.y*q.y);
1963 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
1964 gamma=PerceptibleReciprocal(gamma);
1965 scale=p.x*q.x+p.y*q.y;
1966 offset=gamma*scale*length;
1967 return(offset);
1968 }
1969 case RadialGradient:
1970 {
1971 PointInfo
1972 v;
1973
1974 if (gradient->spread == RepeatSpread)
1975 {
1976 v.x=(double) x-gradient->center.x;
1977 v.y=(double) y-gradient->center.y;
1978 return(sqrt(v.x*v.x+v.y*v.y));
1979 }
1980 v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
1981 gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
1982 gradient->angle))))*PerceptibleReciprocal(gradient->radii.x);
1983 v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
1984 gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
1985 gradient->angle))))*PerceptibleReciprocal(gradient->radii.y);
1986 return(sqrt(v.x*v.x+v.y*v.y));
1987 }
1988 }
1989 return(0.0);
1990}
1991
1992static int StopInfoCompare(const void *x,const void *y)
1993{
1994 StopInfo
1995 *stop_1,
1996 *stop_2;
1997
1998 stop_1=(StopInfo *) x;
1999 stop_2=(StopInfo *) y;
2000 if (stop_1->offset > stop_2->offset)
2001 return(1);
2002 if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
2003 return(0);
2004 return(-1);
2005}
2006
2007MagickExport MagickBooleanType DrawGradientImage(Image *image,
2008 const DrawInfo *draw_info,ExceptionInfo *exception)
2009{
2010 CacheView
2011 *image_view;
2012
2013 const GradientInfo
2014 *gradient;
2015
2016 const SegmentInfo
2017 *gradient_vector;
2018
2019 double
2020 length;
2021
2022 MagickBooleanType
2023 status;
2024
2025 PixelInfo
2026 zero;
2027
2028 PointInfo
2029 point;
2030
2032 bounding_box;
2033
2034 size_t
2035 height;
2036
2037 ssize_t
2038 y;
2039
2040 /*
2041 Draw linear or radial gradient on image.
2042 */
2043 assert(image != (Image *) NULL);
2044 assert(image->signature == MagickCoreSignature);
2045 assert(draw_info != (const DrawInfo *) NULL);
2046 if (IsEventLogging() != MagickFalse)
2047 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2048 gradient=(&draw_info->gradient);
2049 qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
2050 StopInfoCompare);
2051 gradient_vector=(&gradient->gradient_vector);
2052 point.x=gradient_vector->x2-gradient_vector->x1;
2053 point.y=gradient_vector->y2-gradient_vector->y1;
2054 length=sqrt(point.x*point.x+point.y*point.y);
2055 bounding_box=gradient->bounding_box;
2056 status=MagickTrue;
2057 GetPixelInfo(image,&zero);
2058 image_view=AcquireAuthenticCacheView(image,exception);
2059 height=(size_t) (bounding_box.y+(ssize_t) bounding_box.height);
2060#if defined(MAGICKCORE_OPENMP_SUPPORT)
2061 #pragma omp parallel for schedule(static) shared(status) \
2062 magick_number_threads(image,image,height,1)
2063#endif
2064 for (y=bounding_box.y; y < (ssize_t) height; y++)
2065 {
2066 double
2067 alpha,
2068 offset;
2069
2070 PixelInfo
2071 composite,
2072 pixel;
2073
2074 Quantum
2075 *magick_restrict q;
2076
2077 size_t
2078 width;
2079
2080 ssize_t
2081 i,
2082 j,
2083 x;
2084
2085 if (status == MagickFalse)
2086 continue;
2087 q=GetCacheViewAuthenticPixels(image_view,bounding_box.x,y,(size_t)
2088 bounding_box.width,1,exception);
2089 if (q == (Quantum *) NULL)
2090 {
2091 status=MagickFalse;
2092 continue;
2093 }
2094 pixel=zero;
2095 composite=zero;
2096 offset=GetStopColorOffset(gradient,0,y);
2097 if (gradient->type != RadialGradient)
2098 offset*=PerceptibleReciprocal(length);
2099 width=(size_t) (bounding_box.x+(ssize_t) bounding_box.width);
2100 for (x=bounding_box.x; x < (ssize_t) width; x++)
2101 {
2102 GetPixelInfoPixel(image,q,&pixel);
2103 switch (gradient->spread)
2104 {
2105 case UndefinedSpread:
2106 case PadSpread:
2107 {
2108 if ((x != CastDoubleToLong(ceil(gradient_vector->x1-0.5))) ||
2109 (y != CastDoubleToLong(ceil(gradient_vector->y1-0.5))))
2110 {
2111 offset=GetStopColorOffset(gradient,x,y);
2112 if (gradient->type != RadialGradient)
2113 offset*=PerceptibleReciprocal(length);
2114 }
2115 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2116 if (offset < gradient->stops[i].offset)
2117 break;
2118 if ((offset < 0.0) || (i == 0))
2119 composite=gradient->stops[0].color;
2120 else
2121 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
2122 composite=gradient->stops[gradient->number_stops-1].color;
2123 else
2124 {
2125 j=i;
2126 i--;
2127 alpha=(offset-gradient->stops[i].offset)/
2128 (gradient->stops[j].offset-gradient->stops[i].offset);
2129 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2130 &gradient->stops[j].color,alpha,&composite);
2131 }
2132 break;
2133 }
2134 case ReflectSpread:
2135 {
2136 if ((x != CastDoubleToLong(ceil(gradient_vector->x1-0.5))) ||
2137 (y != CastDoubleToLong(ceil(gradient_vector->y1-0.5))))
2138 {
2139 offset=GetStopColorOffset(gradient,x,y);
2140 if (gradient->type != RadialGradient)
2141 offset*=PerceptibleReciprocal(length);
2142 }
2143 if (offset < 0.0)
2144 offset=(-offset);
2145 if ((ssize_t) fmod(offset,2.0) == 0)
2146 offset=fmod(offset,1.0);
2147 else
2148 offset=1.0-fmod(offset,1.0);
2149 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2150 if (offset < gradient->stops[i].offset)
2151 break;
2152 if (i == 0)
2153 composite=gradient->stops[0].color;
2154 else
2155 if (i == (ssize_t) gradient->number_stops)
2156 composite=gradient->stops[gradient->number_stops-1].color;
2157 else
2158 {
2159 j=i;
2160 i--;
2161 alpha=(offset-gradient->stops[i].offset)/
2162 (gradient->stops[j].offset-gradient->stops[i].offset);
2163 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2164 &gradient->stops[j].color,alpha,&composite);
2165 }
2166 break;
2167 }
2168 case RepeatSpread:
2169 {
2170 double
2171 repeat;
2172
2173 MagickBooleanType
2174 antialias;
2175
2176 antialias=MagickFalse;
2177 repeat=0.0;
2178 if ((x != CastDoubleToLong(ceil(gradient_vector->x1-0.5))) ||
2179 (y != CastDoubleToLong(ceil(gradient_vector->y1-0.5))))
2180 {
2181 offset=GetStopColorOffset(gradient,x,y);
2182 if (gradient->type == LinearGradient)
2183 {
2184 repeat=fmod(offset,length);
2185 if (repeat < 0.0)
2186 repeat=length-fmod(-repeat,length);
2187 else
2188 repeat=fmod(offset,length);
2189 antialias=(repeat < length) && ((repeat+1.0) > length) ?
2190 MagickTrue : MagickFalse;
2191 offset=PerceptibleReciprocal(length)*repeat;
2192 }
2193 else
2194 {
2195 repeat=fmod(offset,gradient->radius);
2196 if (repeat < 0.0)
2197 repeat=gradient->radius-fmod(-repeat,gradient->radius);
2198 else
2199 repeat=fmod(offset,gradient->radius);
2200 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
2201 MagickFalse;
2202 offset=repeat*PerceptibleReciprocal(gradient->radius);
2203 }
2204 }
2205 for (i=0; i < (ssize_t) gradient->number_stops; i++)
2206 if (offset < gradient->stops[i].offset)
2207 break;
2208 if (i == 0)
2209 composite=gradient->stops[0].color;
2210 else
2211 if (i == (ssize_t) gradient->number_stops)
2212 composite=gradient->stops[gradient->number_stops-1].color;
2213 else
2214 {
2215 j=i;
2216 i--;
2217 alpha=(offset-gradient->stops[i].offset)/
2218 (gradient->stops[j].offset-gradient->stops[i].offset);
2219 if (antialias != MagickFalse)
2220 {
2221 if (gradient->type == LinearGradient)
2222 alpha=length-repeat;
2223 else
2224 alpha=gradient->radius-repeat;
2225 i=0;
2226 j=(ssize_t) gradient->number_stops-1L;
2227 }
2228 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2229 &gradient->stops[j].color,alpha,&composite);
2230 }
2231 break;
2232 }
2233 }
2234 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
2235 &pixel);
2236 SetPixelViaPixelInfo(image,&pixel,q);
2237 q+=(ptrdiff_t) GetPixelChannels(image);
2238 }
2239 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2240 status=MagickFalse;
2241 }
2242 image_view=DestroyCacheView(image_view);
2243 return(status);
2244}
2245
2246/*
2247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2248% %
2249% %
2250% %
2251% D r a w I m a g e %
2252% %
2253% %
2254% %
2255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2256%
2257% DrawImage() draws a graphic primitive on your image. The primitive
2258% may be represented as a string or filename. Precede the filename with an
2259% "at" sign (@) and the contents of the file are drawn on the image. You
2260% can affect how text is drawn by setting one or more members of the draw
2261% info structure.
2262%
2263% The format of the DrawImage method is:
2264%
2265% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
2266% ExceptionInfo *exception)
2267%
2268% A description of each parameter follows:
2269%
2270% o image: the image.
2271%
2272% o draw_info: the draw info.
2273%
2274% o exception: return any errors or warnings in this structure.
2275%
2276*/
2277
2278static MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
2279 const double pad)
2280{
2281 char
2282 **text = (char **) NULL;
2283
2284 double
2285 extent;
2286
2287 size_t
2288 quantum;
2289
2290 ssize_t
2291 i;
2292
2293 /*
2294 Check if there is enough storage for drawing primitives.
2295 */
2296 quantum=sizeof(**mvg_info->primitive_info);
2297 extent=(double) mvg_info->offset+pad+(PrimitiveExtentPad+1)*(double) quantum;
2298 if (extent <= (double) *mvg_info->extent)
2299 return(MagickTrue);
2300 if ((extent >= (double) GetMaxMemoryRequest()) || (IsNaN(extent) != 0))
2301 return(MagickFalse);
2302 if (mvg_info->offset > 0)
2303 {
2304 text=(char **) AcquireQuantumMemory((size_t) mvg_info->offset,
2305 sizeof(*text));
2306 if (text == (char **) NULL)
2307 return(MagickFalse);
2308 for (i=0; i < mvg_info->offset; i++)
2309 text[i]=(*mvg_info->primitive_info)[i].text;
2310 }
2311 *mvg_info->primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
2312 *mvg_info->primitive_info,(size_t) (extent+1),quantum);
2313 if (*mvg_info->primitive_info != (PrimitiveInfo *) NULL)
2314 {
2315 if (text != (char **) NULL)
2316 text=(char **) RelinquishMagickMemory(text);
2317 *mvg_info->extent=(size_t) extent;
2318 for (i=mvg_info->offset+1; i <= (ssize_t) extent; i++)
2319 {
2320 (*mvg_info->primitive_info)[i].primitive=UndefinedPrimitive;
2321 (*mvg_info->primitive_info)[i].text=(char *) NULL;
2322 }
2323 return(MagickTrue);
2324 }
2325 /*
2326 Reallocation failed, allocate a primitive to facilitate unwinding.
2327 */
2328 if (text != (char **) NULL)
2329 {
2330 for (i=0; i < mvg_info->offset; i++)
2331 if (text[i] != (char *) NULL)
2332 text[i]=DestroyString(text[i]);
2333 text=(char **) RelinquishMagickMemory(text);
2334 }
2335 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
2336 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2337 *mvg_info->primitive_info=(PrimitiveInfo *) AcquireCriticalMemory((size_t)
2338 (PrimitiveExtentPad+1)*quantum);
2339 (void) memset(*mvg_info->primitive_info,0,(size_t) ((PrimitiveExtentPad+1)*
2340 quantum));
2341 *mvg_info->extent=1;
2342 mvg_info->offset=0;
2343 return(MagickFalse);
2344}
2345
2346static inline double GetDrawValue(const char *magick_restrict string,
2347 char **magick_restrict sentinel)
2348{
2349 char
2350 **magick_restrict q;
2351
2352 double
2353 value;
2354
2355 q=sentinel;
2356 value=InterpretLocaleValue(string,q);
2357 sentinel=q;
2358 return(value);
2359}
2360
2361static int MVGMacroCompare(const void *target,const void *source)
2362{
2363 const char
2364 *p,
2365 *q;
2366
2367 p=(const char *) target;
2368 q=(const char *) source;
2369 return(strcmp(p,q));
2370}
2371
2372static SplayTreeInfo *GetMVGMacros(const char *primitive)
2373{
2374 char
2375 *macro,
2376 *token;
2377
2378 const char
2379 *q;
2380
2381 size_t
2382 extent;
2383
2385 *macros;
2386
2387 /*
2388 Scan graphic primitives for definitions and classes.
2389 */
2390 if (primitive == (const char *) NULL)
2391 return((SplayTreeInfo *) NULL);
2392 macros=NewSplayTree(MVGMacroCompare,RelinquishMagickMemory,
2393 RelinquishMagickMemory);
2394 macro=AcquireString(primitive);
2395 token=AcquireString(primitive);
2396 extent=strlen(token)+MagickPathExtent;
2397 for (q=primitive; *q != '\0'; )
2398 {
2399 if (GetNextToken(q,&q,extent,token) < 1)
2400 break;
2401 if (*token == '\0')
2402 break;
2403 if (LocaleCompare("push",token) == 0)
2404 {
2405 const char
2406 *end,
2407 *start;
2408
2409 (void) GetNextToken(q,&q,extent,token);
2410 if (*q == '"')
2411 {
2412 char
2413 name[MagickPathExtent];
2414
2415 const char
2416 *p;
2417
2418 ssize_t
2419 n;
2420
2421 /*
2422 Named macro (e.g. push graphic-context "wheel").
2423 */
2424 (void) GetNextToken(q,&q,extent,token);
2425 start=q;
2426 end=q;
2427 (void) CopyMagickString(name,token,MagickPathExtent);
2428 n=1;
2429 for (p=q; *p != '\0'; )
2430 {
2431 if (GetNextToken(p,&p,extent,token) < 1)
2432 break;
2433 if (*token == '\0')
2434 break;
2435 if (LocaleCompare(token,"pop") == 0)
2436 {
2437 end=p-strlen(token)-1;
2438 n--;
2439 }
2440 if (LocaleCompare(token,"push") == 0)
2441 n++;
2442 if ((n == 0) && (end >= start))
2443 {
2444 size_t
2445 length=(size_t) (end-start);
2446
2447 /*
2448 Extract macro.
2449 */
2450 (void) GetNextToken(p,&p,extent,token);
2451 if (length > 0)
2452 {
2453 (void) CopyMagickString(macro,start,length);
2454 (void) AddValueToSplayTree(macros,ConstantString(name),
2455 ConstantString(macro));
2456 }
2457 break;
2458 }
2459 }
2460 }
2461 }
2462 }
2463 token=DestroyString(token);
2464 macro=DestroyString(macro);
2465 return(macros);
2466}
2467
2468static inline MagickBooleanType IsPoint(const char *point)
2469{
2470 char
2471 *p;
2472
2473 double
2474 value;
2475
2476 value=GetDrawValue(point,&p);
2477 return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
2478 MagickTrue);
2479}
2480
2481static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
2482 const PointInfo point)
2483{
2484 primitive_info->coordinates=1;
2485 primitive_info->closed_subpath=MagickFalse;
2486 primitive_info->point=point;
2487 return(MagickTrue);
2488}
2489
2490static MagickBooleanType RenderMVGContent(Image *image,
2491 const DrawInfo *draw_info,const size_t depth,ExceptionInfo *exception)
2492{
2493#define RenderImageTag "Render/Image"
2494
2496 affine,
2497 current;
2498
2499 char
2500 keyword[MagickPathExtent],
2501 geometry[MagickPathExtent],
2502 *next_token,
2503 pattern[MagickPathExtent],
2504 *primitive,
2505 *token;
2506
2507 const char
2508 *p,
2509 *q;
2510
2511 double
2512 angle,
2513 coordinates,
2514 cursor,
2515 factor,
2516 primitive_extent;
2517
2518 DrawInfo
2519 *clone_info,
2520 **graphic_context;
2521
2522 MagickBooleanType
2523 proceed;
2524
2525 MagickStatusType
2526 status;
2527
2528 MVGInfo
2529 mvg_info;
2530
2531 PointInfo
2532 point;
2533
2535 *primitive_info;
2536
2537 PrimitiveType
2538 primitive_type;
2539
2541 bounds;
2542
2543 size_t
2544 extent,
2545 number_points,
2546 number_stops;
2547
2549 *macros;
2550
2551 ssize_t
2552 defsDepth,
2553 i,
2554 j,
2555 k,
2556 n,
2557 symbolDepth,
2558 x;
2559
2560 StopInfo
2561 *stops;
2562
2564 metrics;
2565
2566 assert(image != (Image *) NULL);
2567 assert(image->signature == MagickCoreSignature);
2568 assert(draw_info != (DrawInfo *) NULL);
2569 assert(draw_info->signature == MagickCoreSignature);
2570 if (IsEventLogging() != MagickFalse)
2571 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2572 if (depth > MagickMaxRecursionDepth)
2573 ThrowBinaryException(DrawError,"VectorGraphicsNestedTooDeeply",
2574 image->filename);
2575 if ((draw_info->primitive == (char *) NULL) ||
2576 (*draw_info->primitive == '\0'))
2577 return(MagickFalse);
2578 if (draw_info->debug != MagickFalse)
2579 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
2580 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2581 return(MagickFalse);
2582 if ((image->alpha_trait & BlendPixelTrait) == 0)
2583 {
2584 status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2585 if (status == MagickFalse)
2586 return(MagickFalse);
2587 }
2588 if ((*draw_info->primitive == '@') && (strlen(draw_info->primitive) > 1) &&
2589 (*(draw_info->primitive+1) != '-') && (depth == 0))
2590 primitive=FileToString(draw_info->primitive,~0UL,exception);
2591 else
2592 primitive=AcquireString(draw_info->primitive);
2593 if (primitive == (char *) NULL)
2594 return(MagickFalse);
2595 primitive_extent=(double) strlen(primitive);
2596 (void) SetImageArtifact(image,"mvg:vector-graphics",primitive);
2597 n=0;
2598 number_stops=0;
2599 stops=(StopInfo *) NULL;
2600 /*
2601 Allocate primitive info memory.
2602 */
2603 graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
2604 if (graphic_context == (DrawInfo **) NULL)
2605 {
2606 primitive=DestroyString(primitive);
2607 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2608 image->filename);
2609 }
2610 number_points=(size_t) PrimitiveExtentPad;
2611 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
2612 (number_points+1),sizeof(*primitive_info));
2613 if (primitive_info == (PrimitiveInfo *) NULL)
2614 {
2615 primitive=DestroyString(primitive);
2616 for ( ; n >= 0; n--)
2617 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2618 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
2619 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2620 image->filename);
2621 }
2622 (void) memset(primitive_info,0,(size_t) (number_points+1)*
2623 sizeof(*primitive_info));
2624 (void) memset(&mvg_info,0,sizeof(mvg_info));
2625 mvg_info.primitive_info=(&primitive_info);
2626 mvg_info.extent=(&number_points);
2627 mvg_info.exception=exception;
2628 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2629 graphic_context[n]->viewbox=image->page;
2630 if ((image->page.width == 0) || (image->page.height == 0))
2631 {
2632 graphic_context[n]->viewbox.width=image->columns;
2633 graphic_context[n]->viewbox.height=image->rows;
2634 }
2635 token=AcquireString(primitive);
2636 extent=strlen(token)+MagickPathExtent;
2637 defsDepth=0;
2638 symbolDepth=0;
2639 cursor=0.0;
2640 macros=GetMVGMacros(primitive);
2641 status=MagickTrue;
2642 for (q=primitive; *q != '\0'; )
2643 {
2644 /*
2645 Interpret graphic primitive.
2646 */
2647 if (GetNextToken(q,&q,MagickPathExtent,keyword) < 1)
2648 break;
2649 if (*keyword == '\0')
2650 break;
2651 if (*keyword == '#')
2652 {
2653 /*
2654 Comment.
2655 */
2656 while ((*q != '\n') && (*q != '\0'))
2657 q++;
2658 continue;
2659 }
2660 p=q-strlen(keyword)-1;
2661 primitive_type=UndefinedPrimitive;
2662 current=graphic_context[n]->affine;
2663 GetAffineMatrix(&affine);
2664 *token='\0';
2665 switch (*keyword)
2666 {
2667 case ';':
2668 break;
2669 case 'a':
2670 case 'A':
2671 {
2672 if (LocaleCompare("affine",keyword) == 0)
2673 {
2674 (void) GetNextToken(q,&q,extent,token);
2675 affine.sx=GetDrawValue(token,&next_token);
2676 if (token == next_token)
2677 ThrowPointExpectedException(token,exception);
2678 (void) GetNextToken(q,&q,extent,token);
2679 if (*token == ',')
2680 (void) GetNextToken(q,&q,extent,token);
2681 affine.rx=GetDrawValue(token,&next_token);
2682 if (token == next_token)
2683 ThrowPointExpectedException(token,exception);
2684 (void) GetNextToken(q,&q,extent,token);
2685 if (*token == ',')
2686 (void) GetNextToken(q,&q,extent,token);
2687 affine.ry=GetDrawValue(token,&next_token);
2688 if (token == next_token)
2689 ThrowPointExpectedException(token,exception);
2690 (void) GetNextToken(q,&q,extent,token);
2691 if (*token == ',')
2692 (void) GetNextToken(q,&q,extent,token);
2693 affine.sy=GetDrawValue(token,&next_token);
2694 if (token == next_token)
2695 ThrowPointExpectedException(token,exception);
2696 (void) GetNextToken(q,&q,extent,token);
2697 if (*token == ',')
2698 (void) GetNextToken(q,&q,extent,token);
2699 affine.tx=GetDrawValue(token,&next_token);
2700 if (token == next_token)
2701 ThrowPointExpectedException(token,exception);
2702 (void) GetNextToken(q,&q,extent,token);
2703 if (*token == ',')
2704 (void) GetNextToken(q,&q,extent,token);
2705 affine.ty=GetDrawValue(token,&next_token);
2706 if (token == next_token)
2707 ThrowPointExpectedException(token,exception);
2708 break;
2709 }
2710 if (LocaleCompare("alpha",keyword) == 0)
2711 {
2712 primitive_type=AlphaPrimitive;
2713 break;
2714 }
2715 if (LocaleCompare("arc",keyword) == 0)
2716 {
2717 primitive_type=ArcPrimitive;
2718 break;
2719 }
2720 status=MagickFalse;
2721 break;
2722 }
2723 case 'b':
2724 case 'B':
2725 {
2726 if (LocaleCompare("bezier",keyword) == 0)
2727 {
2728 primitive_type=BezierPrimitive;
2729 break;
2730 }
2731 if (LocaleCompare("border-color",keyword) == 0)
2732 {
2733 (void) GetNextToken(q,&q,extent,token);
2734 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
2735 &graphic_context[n]->border_color,exception);
2736 break;
2737 }
2738 status=MagickFalse;
2739 break;
2740 }
2741 case 'c':
2742 case 'C':
2743 {
2744 if (LocaleCompare("class",keyword) == 0)
2745 {
2746 const char
2747 *mvg_class;
2748
2749 (void) GetNextToken(q,&q,extent,token);
2750 if ((*token == '\0') || (*token == ';'))
2751 {
2752 status=MagickFalse;
2753 break;
2754 }
2755 /*
2756 Identify recursion.
2757 */
2758 for (i=0; i < n; i++)
2759 if (LocaleCompare(token,graphic_context[i]->id) == 0)
2760 break;
2761 if (i < n)
2762 break;
2763 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
2764 if ((graphic_context[n]->render != MagickFalse) &&
2765 (mvg_class != (const char *) NULL) && (p > primitive))
2766 {
2767 char
2768 *elements;
2769
2770 ssize_t
2771 offset;
2772
2773 /*
2774 Inject class elements in stream.
2775 */
2776 offset=(ssize_t) (p-primitive);
2777 elements=AcquireString(primitive);
2778 elements[offset]='\0';
2779 (void) ConcatenateString(&elements,mvg_class);
2780 (void) ConcatenateString(&elements,"\n");
2781 (void) ConcatenateString(&elements,q);
2782 primitive=DestroyString(primitive);
2783 primitive=elements;
2784 q=primitive+offset;
2785 }
2786 break;
2787 }
2788 if (LocaleCompare("clip-path",keyword) == 0)
2789 {
2790 const char
2791 *clip_path;
2792
2793 /*
2794 Take a node from within the MVG document, and duplicate it here.
2795 */
2796 (void) GetNextToken(q,&q,extent,token);
2797 if (*token == '\0')
2798 {
2799 status=MagickFalse;
2800 break;
2801 }
2802 (void) CloneString(&graphic_context[n]->clip_mask,token);
2803 clip_path=(const char *) GetValueFromSplayTree(macros,token);
2804 if (clip_path != (const char *) NULL)
2805 {
2806 if (graphic_context[n]->clipping_mask != (Image *) NULL)
2807 graphic_context[n]->clipping_mask=
2808 DestroyImage(graphic_context[n]->clipping_mask);
2809 graphic_context[n]->clipping_mask=DrawClippingMask(image,
2810 graphic_context[n],token,clip_path,exception);
2811 if (graphic_context[n]->compliance != SVGCompliance)
2812 {
2813 clip_path=(const char *) GetValueFromSplayTree(macros,
2814 graphic_context[n]->clip_mask);
2815 if (clip_path != (const char *) NULL)
2816 (void) SetImageArtifact(image,
2817 graphic_context[n]->clip_mask,clip_path);
2818 status&=(MagickStatusType) DrawClipPath(image,
2819 graphic_context[n],graphic_context[n]->clip_mask,
2820 exception);
2821 }
2822 }
2823 break;
2824 }
2825 if (LocaleCompare("clip-rule",keyword) == 0)
2826 {
2827 ssize_t
2828 fill_rule;
2829
2830 (void) GetNextToken(q,&q,extent,token);
2831 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2832 token);
2833 if (fill_rule == -1)
2834 {
2835 status=MagickFalse;
2836 break;
2837 }
2838 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2839 break;
2840 }
2841 if (LocaleCompare("clip-units",keyword) == 0)
2842 {
2843 ssize_t
2844 clip_units;
2845
2846 (void) GetNextToken(q,&q,extent,token);
2847 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
2848 token);
2849 if (clip_units == -1)
2850 {
2851 status=MagickFalse;
2852 break;
2853 }
2854 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
2855 if (clip_units == ObjectBoundingBox)
2856 {
2857 GetAffineMatrix(&current);
2858 affine.sx=draw_info->bounds.x2;
2859 affine.sy=draw_info->bounds.y2;
2860 affine.tx=draw_info->bounds.x1;
2861 affine.ty=draw_info->bounds.y1;
2862 break;
2863 }
2864 break;
2865 }
2866 if (LocaleCompare("circle",keyword) == 0)
2867 {
2868 primitive_type=CirclePrimitive;
2869 break;
2870 }
2871 if (LocaleCompare("color",keyword) == 0)
2872 {
2873 primitive_type=ColorPrimitive;
2874 break;
2875 }
2876 if (LocaleCompare("compliance",keyword) == 0)
2877 {
2878 /*
2879 MVG compliance associates a clipping mask with an image; SVG
2880 compliance associates a clipping mask with a graphics context.
2881 */
2882 (void) GetNextToken(q,&q,extent,token);
2883 graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
2884 MagickComplianceOptions,MagickFalse,token);
2885 break;
2886 }
2887 if (LocaleCompare("currentColor",keyword) == 0)
2888 {
2889 (void) GetNextToken(q,&q,extent,token);
2890 break;
2891 }
2892 status=MagickFalse;
2893 break;
2894 }
2895 case 'd':
2896 case 'D':
2897 {
2898 if (LocaleCompare("decorate",keyword) == 0)
2899 {
2900 ssize_t
2901 decorate;
2902
2903 (void) GetNextToken(q,&q,extent,token);
2904 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
2905 token);
2906 if (decorate == -1)
2907 {
2908 status=MagickFalse;
2909 break;
2910 }
2911 graphic_context[n]->decorate=(DecorationType) decorate;
2912 break;
2913 }
2914 if (LocaleCompare("density",keyword) == 0)
2915 {
2916 (void) GetNextToken(q,&q,extent,token);
2917 (void) CloneString(&graphic_context[n]->density,token);
2918 break;
2919 }
2920 if (LocaleCompare("direction",keyword) == 0)
2921 {
2922 ssize_t
2923 direction;
2924
2925 (void) GetNextToken(q,&q,extent,token);
2926 direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
2927 token);
2928 if (direction == -1)
2929 status=MagickFalse;
2930 else
2931 graphic_context[n]->direction=(DirectionType) direction;
2932 break;
2933 }
2934 status=MagickFalse;
2935 break;
2936 }
2937 case 'e':
2938 case 'E':
2939 {
2940 if (LocaleCompare("ellipse",keyword) == 0)
2941 {
2942 primitive_type=EllipsePrimitive;
2943 break;
2944 }
2945 if (LocaleCompare("encoding",keyword) == 0)
2946 {
2947 (void) GetNextToken(q,&q,extent,token);
2948 (void) CloneString(&graphic_context[n]->encoding,token);
2949 break;
2950 }
2951 status=MagickFalse;
2952 break;
2953 }
2954 case 'f':
2955 case 'F':
2956 {
2957 if (LocaleCompare("fill",keyword) == 0)
2958 {
2959 const char
2960 *mvg_class;
2961
2962 (void) GetNextToken(q,&q,extent,token);
2963 if (graphic_context[n]->clip_path != MagickFalse)
2964 break;
2965 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
2966 if (mvg_class != (const char *) NULL)
2967 {
2968 (void) DrawPatternPath(image,draw_info,mvg_class,
2969 &graphic_context[n]->fill_pattern,exception);
2970 break;
2971 }
2972 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
2973 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2974 {
2975 (void) DrawPatternPath(image,draw_info,token,
2976 &graphic_context[n]->fill_pattern,exception);
2977 break;
2978 }
2979 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
2980 &graphic_context[n]->fill,exception);
2981 if (graphic_context[n]->fill_alpha != (double) OpaqueAlpha)
2982 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
2983 break;
2984 }
2985 if (LocaleCompare("fill-opacity",keyword) == 0)
2986 {
2987 double
2988 opacity;
2989
2990 (void) GetNextToken(q,&q,extent,token);
2991 if (graphic_context[n]->clip_path != MagickFalse)
2992 break;
2993 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
2994 opacity=MagickMin(MagickMax(factor*
2995 GetDrawValue(token,&next_token),0.0),1.0);
2996 if (token == next_token)
2997 ThrowPointExpectedException(token,exception);
2998 if (graphic_context[n]->compliance == SVGCompliance)
2999 graphic_context[n]->fill_alpha*=opacity;
3000 else
3001 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3002 if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3003 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3004 else
3005 graphic_context[n]->fill.alpha=(MagickRealType)
3006 ClampToQuantum((double) QuantumRange*opacity);
3007 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3008 break;
3009 }
3010 if (LocaleCompare("fill-rule",keyword) == 0)
3011 {
3012 ssize_t
3013 fill_rule;
3014
3015 (void) GetNextToken(q,&q,extent,token);
3016 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
3017 token);
3018 if (fill_rule == -1)
3019 {
3020 status=MagickFalse;
3021 break;
3022 }
3023 graphic_context[n]->fill_rule=(FillRule) fill_rule;
3024 break;
3025 }
3026 if (LocaleCompare("font",keyword) == 0)
3027 {
3028 (void) GetNextToken(q,&q,extent,token);
3029 (void) CloneString(&graphic_context[n]->font,token);
3030 if (LocaleCompare("none",token) == 0)
3031 graphic_context[n]->font=(char *) RelinquishMagickMemory(
3032 graphic_context[n]->font);
3033 break;
3034 }
3035 if (LocaleCompare("font-family",keyword) == 0)
3036 {
3037 (void) GetNextToken(q,&q,extent,token);
3038 (void) CloneString(&graphic_context[n]->family,token);
3039 break;
3040 }
3041 if (LocaleCompare("font-size",keyword) == 0)
3042 {
3043 (void) GetNextToken(q,&q,extent,token);
3044 graphic_context[n]->pointsize=GetDrawValue(token,&next_token);
3045 if (token == next_token)
3046 ThrowPointExpectedException(token,exception);
3047 break;
3048 }
3049 if (LocaleCompare("font-stretch",keyword) == 0)
3050 {
3051 ssize_t
3052 stretch;
3053
3054 (void) GetNextToken(q,&q,extent,token);
3055 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
3056 if (stretch == -1)
3057 {
3058 status=MagickFalse;
3059 break;
3060 }
3061 graphic_context[n]->stretch=(StretchType) stretch;
3062 break;
3063 }
3064 if (LocaleCompare("font-style",keyword) == 0)
3065 {
3066 ssize_t
3067 style;
3068
3069 (void) GetNextToken(q,&q,extent,token);
3070 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
3071 if (style == -1)
3072 {
3073 status=MagickFalse;
3074 break;
3075 }
3076 graphic_context[n]->style=(StyleType) style;
3077 break;
3078 }
3079 if (LocaleCompare("font-weight",keyword) == 0)
3080 {
3081 ssize_t
3082 weight;
3083
3084 (void) GetNextToken(q,&q,extent,token);
3085 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
3086 if (weight == -1)
3087 weight=(ssize_t) StringToUnsignedLong(token);
3088 graphic_context[n]->weight=(size_t) weight;
3089 break;
3090 }
3091 status=MagickFalse;
3092 break;
3093 }
3094 case 'g':
3095 case 'G':
3096 {
3097 if (LocaleCompare("gradient-units",keyword) == 0)
3098 {
3099 (void) GetNextToken(q,&q,extent,token);
3100 break;
3101 }
3102 if (LocaleCompare("gravity",keyword) == 0)
3103 {
3104 ssize_t
3105 gravity;
3106
3107 (void) GetNextToken(q,&q,extent,token);
3108 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
3109 if (gravity == -1)
3110 {
3111 status=MagickFalse;
3112 break;
3113 }
3114 graphic_context[n]->gravity=(GravityType) gravity;
3115 break;
3116 }
3117 status=MagickFalse;
3118 break;
3119 }
3120 case 'i':
3121 case 'I':
3122 {
3123 if (LocaleCompare("image",keyword) == 0)
3124 {
3125 ssize_t
3126 compose;
3127
3128 primitive_type=ImagePrimitive;
3129 (void) GetNextToken(q,&q,extent,token);
3130 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
3131 if (compose == -1)
3132 {
3133 status=MagickFalse;
3134 break;
3135 }
3136 graphic_context[n]->compose=(CompositeOperator) compose;
3137 break;
3138 }
3139 if (LocaleCompare("interline-spacing",keyword) == 0)
3140 {
3141 (void) GetNextToken(q,&q,extent,token);
3142 graphic_context[n]->interline_spacing=GetDrawValue(token,
3143 &next_token);
3144 if (token == next_token)
3145 ThrowPointExpectedException(token,exception);
3146 break;
3147 }
3148 if (LocaleCompare("interword-spacing",keyword) == 0)
3149 {
3150 (void) GetNextToken(q,&q,extent,token);
3151 graphic_context[n]->interword_spacing=GetDrawValue(token,
3152 &next_token);
3153 if (token == next_token)
3154 ThrowPointExpectedException(token,exception);
3155 break;
3156 }
3157 status=MagickFalse;
3158 break;
3159 }
3160 case 'k':
3161 case 'K':
3162 {
3163 if (LocaleCompare("kerning",keyword) == 0)
3164 {
3165 (void) GetNextToken(q,&q,extent,token);
3166 graphic_context[n]->kerning=GetDrawValue(token,&next_token);
3167 if (token == next_token)
3168 ThrowPointExpectedException(token,exception);
3169 break;
3170 }
3171 status=MagickFalse;
3172 break;
3173 }
3174 case 'l':
3175 case 'L':
3176 {
3177 if (LocaleCompare("letter-spacing",keyword) == 0)
3178 {
3179 (void) GetNextToken(q,&q,extent,token);
3180 if (IsPoint(token) == MagickFalse)
3181 break;
3182 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3183 clone_info->text=AcquireString(" ");
3184 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,&metrics,
3185 exception);
3186 graphic_context[n]->kerning=metrics.width*
3187 GetDrawValue(token,&next_token);
3188 clone_info=DestroyDrawInfo(clone_info);
3189 if (token == next_token)
3190 ThrowPointExpectedException(token,exception);
3191 break;
3192 }
3193 if (LocaleCompare("line",keyword) == 0)
3194 {
3195 primitive_type=LinePrimitive;
3196 break;
3197 }
3198 status=MagickFalse;
3199 break;
3200 }
3201 case 'm':
3202 case 'M':
3203 {
3204 if (LocaleCompare("mask",keyword) == 0)
3205 {
3206 const char
3207 *mask_path;
3208
3209 /*
3210 Take a node from within the MVG document, and duplicate it here.
3211 */
3212 (void) GetNextToken(q,&q,extent,token);
3213 mask_path=(const char *) GetValueFromSplayTree(macros,token);
3214 if (mask_path != (const char *) NULL)
3215 {
3216 if (graphic_context[n]->composite_mask != (Image *) NULL)
3217 graphic_context[n]->composite_mask=
3218 DestroyImage(graphic_context[n]->composite_mask);
3219 graphic_context[n]->composite_mask=DrawCompositeMask(image,
3220 graphic_context[n],token,mask_path,exception);
3221 if (graphic_context[n]->compliance != SVGCompliance)
3222 status=SetImageMask(image,CompositePixelMask,
3223 graphic_context[n]->composite_mask,exception);
3224 }
3225 break;
3226 }
3227 status=MagickFalse;
3228 break;
3229 }
3230 case 'o':
3231 case 'O':
3232 {
3233 if (LocaleCompare("offset",keyword) == 0)
3234 {
3235 (void) GetNextToken(q,&q,extent,token);
3236 break;
3237 }
3238 if (LocaleCompare("opacity",keyword) == 0)
3239 {
3240 double
3241 opacity;
3242
3243 (void) GetNextToken(q,&q,extent,token);
3244 if (graphic_context[n]->clip_path != MagickFalse)
3245 break;
3246 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3247 opacity=MagickMin(MagickMax(factor*
3248 GetDrawValue(token,&next_token),0.0),1.0);
3249 if (token == next_token)
3250 ThrowPointExpectedException(token,exception);
3251 if (graphic_context[n]->compliance == SVGCompliance)
3252 {
3253 graphic_context[n]->fill_alpha*=opacity;
3254 graphic_context[n]->stroke_alpha*=opacity;
3255 }
3256 else
3257 {
3258 graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3259 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3260 }
3261 if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3262 {
3263 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3264 graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3265 }
3266 else
3267 {
3268 graphic_context[n]->fill.alpha=(MagickRealType)
3269 ClampToQuantum((double) QuantumRange*opacity);
3270 graphic_context[n]->stroke.alpha=(MagickRealType)
3271 ClampToQuantum((double) QuantumRange*opacity);
3272 }
3273 graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3274 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3275 break;
3276 }
3277 status=MagickFalse;
3278 break;
3279 }
3280 case 'p':
3281 case 'P':
3282 {
3283 if (LocaleCompare("path",keyword) == 0)
3284 {
3285 primitive_type=PathPrimitive;
3286 break;
3287 }
3288 if (LocaleCompare("point",keyword) == 0)
3289 {
3290 primitive_type=PointPrimitive;
3291 break;
3292 }
3293 if (LocaleCompare("polyline",keyword) == 0)
3294 {
3295 primitive_type=PolylinePrimitive;
3296 break;
3297 }
3298 if (LocaleCompare("polygon",keyword) == 0)
3299 {
3300 primitive_type=PolygonPrimitive;
3301 break;
3302 }
3303 if (LocaleCompare("pop",keyword) == 0)
3304 {
3305 if (GetNextToken(q,&q,extent,token) < 1)
3306 break;
3307 if (LocaleCompare("class",token) == 0)
3308 break;
3309 if (LocaleCompare("clip-path",token) == 0)
3310 break;
3311 if (LocaleCompare("defs",token) == 0)
3312 {
3313 defsDepth--;
3314 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3315 MagickTrue;
3316 break;
3317 }
3318 if (LocaleCompare("gradient",token) == 0)
3319 break;
3320 if (LocaleCompare("graphic-context",token) == 0)
3321 {
3322 if (n <= 0)
3323 {
3324 (void) ThrowMagickException(exception,GetMagickModule(),
3325 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
3326 status=MagickFalse;
3327 n=0;
3328 break;
3329 }
3330 if ((graphic_context[n]->clip_mask != (char *) NULL) &&
3331 (graphic_context[n]->compliance != SVGCompliance))
3332 if (LocaleCompare(graphic_context[n]->clip_mask,
3333 graphic_context[n-1]->clip_mask) != 0)
3334 status=SetImageMask(image,WritePixelMask,(Image *) NULL,
3335 exception);
3336 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3337 n--;
3338 break;
3339 }
3340 if (LocaleCompare("mask",token) == 0)
3341 break;
3342 if (LocaleCompare("pattern",token) == 0)
3343 break;
3344 if (LocaleCompare("symbol",token) == 0)
3345 {
3346 symbolDepth--;
3347 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3348 MagickTrue;
3349 break;
3350 }
3351 status=MagickFalse;
3352 break;
3353 }
3354 if (LocaleCompare("push",keyword) == 0)
3355 {
3356 if (GetNextToken(q,&q,extent,token) < 1)
3357 break;
3358 if (LocaleCompare("class",token) == 0)
3359 {
3360 /*
3361 Class context.
3362 */
3363 for (p=q; *q != '\0'; )
3364 {
3365 if (GetNextToken(q,&q,extent,token) < 1)
3366 break;
3367 if (LocaleCompare(token,"pop") != 0)
3368 continue;
3369 (void) GetNextToken(q,(const char **) NULL,extent,token);
3370 if (LocaleCompare(token,"class") != 0)
3371 continue;
3372 break;
3373 }
3374 (void) GetNextToken(q,&q,extent,token);
3375 break;
3376 }
3377 if (LocaleCompare("clip-path",token) == 0)
3378 {
3379 (void) GetNextToken(q,&q,extent,token);
3380 for (p=q; *q != '\0'; )
3381 {
3382 if (GetNextToken(q,&q,extent,token) < 1)
3383 break;
3384 if (LocaleCompare(token,"pop") != 0)
3385 continue;
3386 (void) GetNextToken(q,(const char **) NULL,extent,token);
3387 if (LocaleCompare(token,"clip-path") != 0)
3388 continue;
3389 break;
3390 }
3391 if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
3392 {
3393 status=MagickFalse;
3394 break;
3395 }
3396 (void) GetNextToken(q,&q,extent,token);
3397 break;
3398 }
3399 if (LocaleCompare("defs",token) == 0)
3400 {
3401 defsDepth++;
3402 graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3403 MagickTrue;
3404 break;
3405 }
3406 if (LocaleCompare("gradient",token) == 0)
3407 {
3408 char
3409 key[2*MagickPathExtent],
3410 name[MagickPathExtent],
3411 type[MagickPathExtent];
3412
3414 segment;
3415
3416 (void) GetNextToken(q,&q,extent,token);
3417 (void) CopyMagickString(name,token,MagickPathExtent);
3418 (void) GetNextToken(q,&q,extent,token);
3419 (void) CopyMagickString(type,token,MagickPathExtent);
3420 (void) GetNextToken(q,&q,extent,token);
3421 segment.x1=GetDrawValue(token,&next_token);
3422 if (token == next_token)
3423 ThrowPointExpectedException(token,exception);
3424 (void) GetNextToken(q,&q,extent,token);
3425 if (*token == ',')
3426 (void) GetNextToken(q,&q,extent,token);
3427 segment.y1=GetDrawValue(token,&next_token);
3428 if (token == next_token)
3429 ThrowPointExpectedException(token,exception);
3430 (void) GetNextToken(q,&q,extent,token);
3431 if (*token == ',')
3432 (void) GetNextToken(q,&q,extent,token);
3433 segment.x2=GetDrawValue(token,&next_token);
3434 if (token == next_token)
3435 ThrowPointExpectedException(token,exception);
3436 (void) GetNextToken(q,&q,extent,token);
3437 if (*token == ',')
3438 (void) GetNextToken(q,&q,extent,token);
3439 segment.y2=GetDrawValue(token,&next_token);
3440 if (token == next_token)
3441 ThrowPointExpectedException(token,exception);
3442 if (LocaleCompare(type,"radial") == 0)
3443 {
3444 (void) GetNextToken(q,&q,extent,token);
3445 if (*token == ',')
3446 (void) GetNextToken(q,&q,extent,token);
3447 }
3448 for (p=q; *q != '\0'; )
3449 {
3450 if (GetNextToken(q,&q,extent,token) < 1)
3451 break;
3452 if (LocaleCompare(token,"pop") != 0)
3453 continue;
3454 (void) GetNextToken(q,(const char **) NULL,extent,token);
3455 if (LocaleCompare(token,"gradient") != 0)
3456 continue;
3457 break;
3458 }
3459 if ((q == (char *) NULL) || (*q == '\0') ||
3460 (p == (char *) NULL) || ((q-4) < p) ||
3461 ((q-p+4+1) > MagickPathExtent))
3462 {
3463 status=MagickFalse;
3464 break;
3465 }
3466 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3467 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3468 graphic_context[n]->affine.ry*segment.y1+
3469 graphic_context[n]->affine.tx;
3470 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3471 graphic_context[n]->affine.sy*segment.y1+
3472 graphic_context[n]->affine.ty;
3473 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3474 graphic_context[n]->affine.ry*segment.y2+
3475 graphic_context[n]->affine.tx;
3476 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3477 graphic_context[n]->affine.sy*segment.y2+
3478 graphic_context[n]->affine.ty;
3479 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3480 (void) SetImageArtifact(image,key,token);
3481 (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name);
3482 (void) SetImageArtifact(image,key,type);
3483 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3484 name);
3485 (void) FormatLocaleString(geometry,MagickPathExtent,
3486 "%gx%g%+.15g%+.15g",
3487 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
3488 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
3489 bounds.x1,bounds.y1);
3490 (void) SetImageArtifact(image,key,geometry);
3491 (void) GetNextToken(q,&q,extent,token);
3492 break;
3493 }
3494 if (LocaleCompare("graphic-context",token) == 0)
3495 {
3496 n++;
3497 graphic_context=(DrawInfo **) ResizeQuantumMemory(
3498 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
3499 if (graphic_context == (DrawInfo **) NULL)
3500 {
3501 (void) ThrowMagickException(exception,GetMagickModule(),
3502 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3503 image->filename);
3504 break;
3505 }
3506 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
3507 graphic_context[n-1]);
3508 if (*q == '"')
3509 {
3510 (void) GetNextToken(q,&q,extent,token);
3511 (void) CloneString(&graphic_context[n]->id,token);
3512 }
3513 if (n > MagickMaxRecursionDepth)
3514 (void) ThrowMagickException(exception,GetMagickModule(),
3515 DrawError,"VectorGraphicsNestedTooDeeply","`%s'",
3516 image->filename);
3517 break;
3518 }
3519 if (LocaleCompare("mask",token) == 0)
3520 {
3521 (void) GetNextToken(q,&q,extent,token);
3522 break;
3523 }
3524 if (LocaleCompare("pattern",token) == 0)
3525 {
3526 char
3527 key[2*MagickPathExtent],
3528 name[MagickPathExtent];
3529
3531 region;
3532
3533 (void) GetNextToken(q,&q,extent,token);
3534 (void) CopyMagickString(name,token,MagickPathExtent);
3535 (void) GetNextToken(q,&q,extent,token);
3536 region.x=CastDoubleToLong(ceil(GetDrawValue(token,
3537 &next_token)-0.5));
3538 if (token == next_token)
3539 ThrowPointExpectedException(token,exception);
3540 (void) GetNextToken(q,&q,extent,token);
3541 if (*token == ',')
3542 (void) GetNextToken(q,&q,extent,token);
3543 region.y=CastDoubleToLong(ceil(GetDrawValue(token,
3544 &next_token)-0.5));
3545 if (token == next_token)
3546 ThrowPointExpectedException(token,exception);
3547 (void) GetNextToken(q,&q,extent,token);
3548 if (*token == ',')
3549 (void) GetNextToken(q,&q,extent,token);
3550 region.width=CastDoubleToUnsigned(floor(GetDrawValue(
3551 token,&next_token)+0.5));
3552 if (token == next_token)
3553 ThrowPointExpectedException(token,exception);
3554 (void) GetNextToken(q,&q,extent,token);
3555 if (*token == ',')
3556 (void) GetNextToken(q,&q,extent,token);
3557 region.height=CastDoubleToUnsigned(GetDrawValue(token,
3558 &next_token)+0.5);
3559 if (token == next_token)
3560 ThrowPointExpectedException(token,exception);
3561 for (p=q; *q != '\0'; )
3562 {
3563 if (GetNextToken(q,&q,extent,token) < 1)
3564 break;
3565 if (LocaleCompare(token,"pop") != 0)
3566 continue;
3567 (void) GetNextToken(q,(const char **) NULL,extent,token);
3568 if (LocaleCompare(token,"pattern") != 0)
3569 continue;
3570 break;
3571 }
3572 if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p) ||
3573 ((q-p+4+1) > MagickPathExtent))
3574 {
3575 status=MagickFalse;
3576 break;
3577 }
3578 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3579 (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3580 (void) SetImageArtifact(image,key,token);
3581 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3582 name);
3583 (void) FormatLocaleString(geometry,MagickPathExtent,
3584 "%.20gx%.20g%+.20g%+.20g",(double) region.width,(double)
3585 region.height,(double) region.x,(double) region.y);
3586 (void) SetImageArtifact(image,key,geometry);
3587 (void) GetNextToken(q,&q,extent,token);
3588 break;
3589 }
3590 if (LocaleCompare("symbol",token) == 0)
3591 {
3592 symbolDepth++;
3593 graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3594 MagickTrue;
3595 break;
3596 }
3597 status=MagickFalse;
3598 break;
3599 }
3600 status=MagickFalse;
3601 break;
3602 }
3603 case 'r':
3604 case 'R':
3605 {
3606 if (LocaleCompare("rectangle",keyword) == 0)
3607 {
3608 primitive_type=RectanglePrimitive;
3609 break;
3610 }
3611 if (LocaleCompare("rotate",keyword) == 0)
3612 {
3613 (void) GetNextToken(q,&q,extent,token);
3614 angle=GetDrawValue(token,&next_token);
3615 if (token == next_token)
3616 ThrowPointExpectedException(token,exception);
3617 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
3618 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
3619 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
3620 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
3621 break;
3622 }
3623 if (LocaleCompare("roundRectangle",keyword) == 0)
3624 {
3625 primitive_type=RoundRectanglePrimitive;
3626 break;
3627 }
3628 status=MagickFalse;
3629 break;
3630 }
3631 case 's':
3632 case 'S':
3633 {
3634 if (LocaleCompare("scale",keyword) == 0)
3635 {
3636 (void) GetNextToken(q,&q,extent,token);
3637 affine.sx=GetDrawValue(token,&next_token);
3638 if (token == next_token)
3639 ThrowPointExpectedException(token,exception);
3640 (void) GetNextToken(q,&q,extent,token);
3641 if (*token == ',')
3642 (void) GetNextToken(q,&q,extent,token);
3643 affine.sy=GetDrawValue(token,&next_token);
3644 if (token == next_token)
3645 ThrowPointExpectedException(token,exception);
3646 break;
3647 }
3648 if (LocaleCompare("skewX",keyword) == 0)
3649 {
3650 (void) GetNextToken(q,&q,extent,token);
3651 angle=GetDrawValue(token,&next_token);
3652 if (token == next_token)
3653 ThrowPointExpectedException(token,exception);
3654 affine.ry=sin(DegreesToRadians(angle));
3655 break;
3656 }
3657 if (LocaleCompare("skewY",keyword) == 0)
3658 {
3659 (void) GetNextToken(q,&q,extent,token);
3660 angle=GetDrawValue(token,&next_token);
3661 if (token == next_token)
3662 ThrowPointExpectedException(token,exception);
3663 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
3664 break;
3665 }
3666 if (LocaleCompare("stop-color",keyword) == 0)
3667 {
3668 PixelInfo
3669 stop_color;
3670
3671 number_stops++;
3672 if (number_stops == 1)
3673 stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops));
3674 else
3675 if (number_stops > 2)
3676 stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
3677 sizeof(*stops));
3678 if (stops == (StopInfo *) NULL)
3679 {
3680 (void) ThrowMagickException(exception,GetMagickModule(),
3681 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3682 image->filename);
3683 break;
3684 }
3685 (void) GetNextToken(q,&q,extent,token);
3686 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3687 &stop_color,exception);
3688 stops[number_stops-1].color=stop_color;
3689 (void) GetNextToken(q,&q,extent,token);
3690 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3691 stops[number_stops-1].offset=factor*GetDrawValue(token,
3692 &next_token);
3693 if (token == next_token)
3694 ThrowPointExpectedException(token,exception);
3695 break;
3696 }
3697 if (LocaleCompare("stroke",keyword) == 0)
3698 {
3699 const char
3700 *mvg_class;
3701
3702 (void) GetNextToken(q,&q,extent,token);
3703 if (graphic_context[n]->clip_path != MagickFalse)
3704 break;
3705 mvg_class=(const char *) GetValueFromSplayTree(macros,token);
3706 if (mvg_class != (const char *) NULL)
3707 {
3708 (void) DrawPatternPath(image,draw_info,mvg_class,
3709 &graphic_context[n]->stroke_pattern,exception);
3710 break;
3711 }
3712 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3713 if (GetImageArtifact(image,pattern) != (const char *) NULL)
3714 {
3715 (void) DrawPatternPath(image,draw_info,token,
3716 &graphic_context[n]->stroke_pattern,exception);
3717 break;
3718 }
3719 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3720 &graphic_context[n]->stroke,exception);
3721 if (graphic_context[n]->stroke_alpha != (double) OpaqueAlpha)
3722 graphic_context[n]->stroke.alpha=
3723 graphic_context[n]->stroke_alpha;
3724 break;
3725 }
3726 if (LocaleCompare("stroke-antialias",keyword) == 0)
3727 {
3728 (void) GetNextToken(q,&q,extent,token);
3729 graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
3730 MagickTrue : MagickFalse;
3731 break;
3732 }
3733 if (LocaleCompare("stroke-dasharray",keyword) == 0)
3734 {
3735 if (graphic_context[n]->dash_pattern != (double *) NULL)
3736 graphic_context[n]->dash_pattern=(double *)
3737 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
3738 if (IsPoint(q) != MagickFalse)
3739 {
3740 const char
3741 *r;
3742
3743 r=q;
3744 (void) GetNextToken(r,&r,extent,token);
3745 if (*token == ',')
3746 (void) GetNextToken(r,&r,extent,token);
3747 for (x=0; IsPoint(token) != MagickFalse; x++)
3748 {
3749 (void) GetNextToken(r,&r,extent,token);
3750 if (*token == ',')
3751 (void) GetNextToken(r,&r,extent,token);
3752 }
3753 graphic_context[n]->dash_pattern=(double *)
3754 AcquireQuantumMemory((size_t) (2*x+2),
3755 sizeof(*graphic_context[n]->dash_pattern));
3756 if (graphic_context[n]->dash_pattern == (double *) NULL)
3757 {
3758 (void) ThrowMagickException(exception,GetMagickModule(),
3759 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3760 image->filename);
3761 status=MagickFalse;
3762 break;
3763 }
3764 (void) memset(graphic_context[n]->dash_pattern,0,(size_t)
3765 (2*x+2)*sizeof(*graphic_context[n]->dash_pattern));
3766 for (j=0; j < x; j++)
3767 {
3768 (void) GetNextToken(q,&q,extent,token);
3769 if (*token == ',')
3770 (void) GetNextToken(q,&q,extent,token);
3771 graphic_context[n]->dash_pattern[j]=GetDrawValue(token,
3772 &next_token);
3773 if (token == next_token)
3774 ThrowPointExpectedException(token,exception);
3775 if (graphic_context[n]->dash_pattern[j] <= 0.0)
3776 status=MagickFalse;
3777 }
3778 if ((x & 0x01) != 0)
3779 for ( ; j < (2*x); j++)
3780 graphic_context[n]->dash_pattern[j]=
3781 graphic_context[n]->dash_pattern[j-x];
3782 graphic_context[n]->dash_pattern[j]=0.0;
3783 break;
3784 }
3785 (void) GetNextToken(q,&q,extent,token);
3786 break;
3787 }
3788 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
3789 {
3790 (void) GetNextToken(q,&q,extent,token);
3791 graphic_context[n]->dash_offset=GetDrawValue(token,&next_token);
3792 if (token == next_token)
3793 ThrowPointExpectedException(token,exception);
3794 break;
3795 }
3796 if (LocaleCompare("stroke-linecap",keyword) == 0)
3797 {
3798 ssize_t
3799 linecap;
3800
3801 (void) GetNextToken(q,&q,extent,token);
3802 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
3803 if (linecap == -1)
3804 {
3805 status=MagickFalse;
3806 break;
3807 }
3808 graphic_context[n]->linecap=(LineCap) linecap;
3809 break;
3810 }
3811 if (LocaleCompare("stroke-linejoin",keyword) == 0)
3812 {
3813 ssize_t
3814 linejoin;
3815
3816 (void) GetNextToken(q,&q,extent,token);
3817 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
3818 token);
3819 if (linejoin == -1)
3820 {
3821 status=MagickFalse;
3822 break;
3823 }
3824 graphic_context[n]->linejoin=(LineJoin) linejoin;
3825 break;
3826 }
3827 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
3828 {
3829 (void) GetNextToken(q,&q,extent,token);
3830 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
3831 break;
3832 }
3833 if (LocaleCompare("stroke-opacity",keyword) == 0)
3834 {
3835 double
3836 opacity;
3837
3838 (void) GetNextToken(q,&q,extent,token);
3839 if (graphic_context[n]->clip_path != MagickFalse)
3840 break;
3841 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3842 opacity=MagickMin(MagickMax(factor*GetDrawValue(token,&next_token),
3843 0.0),1.0);
3844 if (token == next_token)
3845 ThrowPointExpectedException(token,exception);
3846 if (graphic_context[n]->compliance == SVGCompliance)
3847 graphic_context[n]->stroke_alpha*=opacity;
3848 else
3849 graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3850 if (graphic_context[n]->stroke.alpha != (double) TransparentAlpha)
3851 graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3852 else
3853 graphic_context[n]->stroke.alpha=(MagickRealType)
3854 ClampToQuantum((double) QuantumRange*opacity);
3855 graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3856 break;
3857 }
3858 if (LocaleCompare("stroke-width",keyword) == 0)
3859 {
3860 (void) GetNextToken(q,&q,extent,token);
3861 if (graphic_context[n]->clip_path != MagickFalse)
3862 break;
3863 graphic_context[n]->stroke_width=GetDrawValue(token,&next_token);
3864 if ((token == next_token) ||
3865 (graphic_context[n]->stroke_width < 0.0))
3866 ThrowPointExpectedException(token,exception);
3867 break;
3868 }
3869 status=MagickFalse;
3870 break;
3871 }
3872 case 't':
3873 case 'T':
3874 {
3875 if (LocaleCompare("text",keyword) == 0)
3876 {
3877 primitive_type=TextPrimitive;
3878 cursor=0.0;
3879 break;
3880 }
3881 if (LocaleCompare("text-align",keyword) == 0)
3882 {
3883 ssize_t
3884 align;
3885
3886 (void) GetNextToken(q,&q,extent,token);
3887 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3888 if (align == -1)
3889 {
3890 status=MagickFalse;
3891 break;
3892 }
3893 graphic_context[n]->align=(AlignType) align;
3894 break;
3895 }
3896 if (LocaleCompare("text-anchor",keyword) == 0)
3897 {
3898 ssize_t
3899 align;
3900
3901 (void) GetNextToken(q,&q,extent,token);
3902 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3903 if (align == -1)
3904 {
3905 status=MagickFalse;
3906 break;
3907 }
3908 graphic_context[n]->align=(AlignType) align;
3909 break;
3910 }
3911 if (LocaleCompare("text-antialias",keyword) == 0)
3912 {
3913 (void) GetNextToken(q,&q,extent,token);
3914 graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
3915 MagickTrue : MagickFalse;
3916 break;
3917 }
3918 if (LocaleCompare("text-undercolor",keyword) == 0)
3919 {
3920 (void) GetNextToken(q,&q,extent,token);
3921 status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3922 &graphic_context[n]->undercolor,exception);
3923 break;
3924 }
3925 if (LocaleCompare("translate",keyword) == 0)
3926 {
3927 (void) GetNextToken(q,&q,extent,token);
3928 affine.tx=GetDrawValue(token,&next_token);
3929 if (token == next_token)
3930 ThrowPointExpectedException(token,exception);
3931 (void) GetNextToken(q,&q,extent,token);
3932 if (*token == ',')
3933 (void) GetNextToken(q,&q,extent,token);
3934 affine.ty=GetDrawValue(token,&next_token);
3935 if (token == next_token)
3936 ThrowPointExpectedException(token,exception);
3937 cursor=0.0;
3938 break;
3939 }
3940 status=MagickFalse;
3941 break;
3942 }
3943 case 'u':
3944 case 'U':
3945 {
3946 if (LocaleCompare("use",keyword) == 0)
3947 {
3948 const char
3949 *use;
3950
3951 /*
3952 Get a macro from the MVG document, and "use" it here.
3953 */
3954 (void) GetNextToken(q,&q,extent,token);
3955 use=(const char *) GetValueFromSplayTree(macros,token);
3956 if (use != (const char *) NULL)
3957 {
3958 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3959 (void) CloneString(&clone_info->primitive,use);
3960 status=RenderMVGContent(image,clone_info,depth+1,exception);
3961 clone_info=DestroyDrawInfo(clone_info);
3962 }
3963 break;
3964 }
3965 status=MagickFalse;
3966 break;
3967 }
3968 case 'v':
3969 case 'V':
3970 {
3971 if (LocaleCompare("viewbox",keyword) == 0)
3972 {
3973 (void) GetNextToken(q,&q,extent,token);
3974 graphic_context[n]->viewbox.x=CastDoubleToLong(ceil(
3975 GetDrawValue(token,&next_token)-0.5));
3976 if (token == next_token)
3977 ThrowPointExpectedException(token,exception);
3978 (void) GetNextToken(q,&q,extent,token);
3979 if (*token == ',')
3980 (void) GetNextToken(q,&q,extent,token);
3981 graphic_context[n]->viewbox.y=CastDoubleToLong(
3982 ceil(GetDrawValue(token,&next_token)-0.5));
3983 if (token == next_token)
3984 ThrowPointExpectedException(token,exception);
3985 (void) GetNextToken(q,&q,extent,token);
3986 if (*token == ',')
3987 (void) GetNextToken(q,&q,extent,token);
3988 graphic_context[n]->viewbox.width=CastDoubleToUnsigned(
3989 floor(GetDrawValue(token,&next_token)+0.5));
3990 if (token == next_token)
3991 ThrowPointExpectedException(token,exception);
3992 (void) GetNextToken(q,&q,extent,token);
3993 if (*token == ',')
3994 (void) GetNextToken(q,&q,extent,token);
3995 graphic_context[n]->viewbox.height=(size_t) CastDoubleToUnsigned(
3996 floor(GetDrawValue(token,&next_token)+0.5));
3997 if (token == next_token)
3998 ThrowPointExpectedException(token,exception);
3999 break;
4000 }
4001 status=MagickFalse;
4002 break;
4003 }
4004 case 'w':
4005 case 'W':
4006 {
4007 if (LocaleCompare("word-spacing",keyword) == 0)
4008 {
4009 (void) GetNextToken(q,&q,extent,token);
4010 graphic_context[n]->interword_spacing=GetDrawValue(token,
4011 &next_token);
4012 if (token == next_token)
4013 ThrowPointExpectedException(token,exception);
4014 break;
4015 }
4016 status=MagickFalse;
4017 break;
4018 }
4019 default:
4020 {
4021 status=MagickFalse;
4022 break;
4023 }
4024 }
4025 if (status == MagickFalse)
4026 break;
4027 if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
4028 (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
4029 (fabs(affine.sy-1.0) >= MagickEpsilon) ||
4030 (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
4031 {
4032 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
4033 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
4034 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
4035 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
4036 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
4037 current.tx;
4038 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
4039 current.ty;
4040 }
4041 if (primitive_type == UndefinedPrimitive)
4042 {
4043 if (*q == '\0')
4044 {
4045 if (number_stops > 1)
4046 {
4047 GradientType
4048 type;
4049
4050 type=LinearGradient;
4051 if (draw_info->gradient.type == RadialGradient)
4052 type=RadialGradient;
4053 (void) GradientImage(image,type,PadSpread,stops,number_stops,
4054 exception);
4055 }
4056 if (number_stops > 0)
4057 stops=(StopInfo *) RelinquishMagickMemory(stops);
4058 }
4059 if ((draw_info->debug != MagickFalse) && (q > p))
4060 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int)
4061 (q-p-1),p);
4062 continue;
4063 }
4064 /*
4065 Parse the primitive attributes.
4066 */
4067 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4068 if (primitive_info[i].text != (char *) NULL)
4069 primitive_info[i].text=DestroyString(primitive_info[i].text);
4070 i=0;
4071 mvg_info.offset=i;
4072 j=0;
4073 primitive_info[0].point.x=0.0;
4074 primitive_info[0].point.y=0.0;
4075 primitive_info[0].coordinates=0;
4076 primitive_info[0].method=FloodfillMethod;
4077 primitive_info[0].closed_subpath=MagickFalse;
4078 for (x=0; *q != '\0'; x++)
4079 {
4080 /*
4081 Define points.
4082 */
4083 if (IsPoint(q) == MagickFalse)
4084 break;
4085 (void) GetNextToken(q,&q,extent,token);
4086 point.x=GetDrawValue(token,&next_token);
4087 if (token == next_token)
4088 ThrowPointExpectedException(token,exception);
4089 (void) GetNextToken(q,&q,extent,token);
4090 if (*token == ',')
4091 (void) GetNextToken(q,&q,extent,token);
4092 point.y=GetDrawValue(token,&next_token);
4093 if (token == next_token)
4094 ThrowPointExpectedException(token,exception);
4095 (void) GetNextToken(q,(const char **) NULL,extent,token);
4096 if (*token == ',')
4097 (void) GetNextToken(q,&q,extent,token);
4098 primitive_info[i].primitive=primitive_type;
4099 primitive_info[i].point=point;
4100 primitive_info[i].coordinates=0;
4101 primitive_info[i].method=FloodfillMethod;
4102 primitive_info[i].closed_subpath=MagickFalse;
4103 i++;
4104 mvg_info.offset=i;
4105 if (i < (ssize_t) number_points)
4106 continue;
4107 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4108 number_points);
4109 primitive_info=(*mvg_info.primitive_info);
4110 }
4111 if (status == MagickFalse)
4112 break;
4113 if (primitive_info[j].text != (char *) NULL)
4114 primitive_info[j].text=DestroyString(primitive_info[j].text);
4115 primitive_info[j].primitive=primitive_type;
4116 primitive_info[j].coordinates=(size_t) x;
4117 primitive_info[j].method=FloodfillMethod;
4118 primitive_info[j].closed_subpath=MagickFalse;
4119 /*
4120 Circumscribe primitive within a circle.
4121 */
4122 bounds.x1=primitive_info[j].point.x;
4123 bounds.y1=primitive_info[j].point.y;
4124 bounds.x2=primitive_info[j].point.x;
4125 bounds.y2=primitive_info[j].point.y;
4126 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
4127 {
4128 point=primitive_info[j+k].point;
4129 if (point.x < bounds.x1)
4130 bounds.x1=point.x;
4131 if (point.y < bounds.y1)
4132 bounds.y1=point.y;
4133 if (point.x > bounds.x2)
4134 bounds.x2=point.x;
4135 if (point.y > bounds.y2)
4136 bounds.y2=point.y;
4137 }
4138 /*
4139 Speculate how many points our primitive might consume.
4140 */
4141 coordinates=(double) primitive_info[j].coordinates;
4142 switch (primitive_type)
4143 {
4144 case RectanglePrimitive:
4145 {
4146 coordinates*=5.0;
4147 break;
4148 }
4149 case RoundRectanglePrimitive:
4150 {
4151 double
4152 alpha,
4153 beta,
4154 radius;
4155
4156 alpha=bounds.x2-bounds.x1;
4157 beta=bounds.y2-bounds.y1;
4158 radius=hypot(alpha,beta);
4159 coordinates*=5.0;
4160 coordinates+=2.0*((size_t) ceil((double) MagickPI*radius))+6.0*
4161 BezierQuantum+360.0;
4162 break;
4163 }
4164 case BezierPrimitive:
4165 {
4166 coordinates=(BezierQuantum*(double) primitive_info[j].coordinates);
4167 break;
4168 }
4169 case PathPrimitive:
4170 {
4171 char
4172 *s,
4173 *t;
4174
4175 (void) GetNextToken(q,&q,extent,token);
4176 coordinates=1.0;
4177 t=token;
4178 for (s=token; *s != '\0'; s=t)
4179 {
4180 double
4181 value;
4182
4183 value=GetDrawValue(s,&t);
4184 (void) value;
4185 if (s == t)
4186 {
4187 t++;
4188 continue;
4189 }
4190 coordinates++;
4191 }
4192 for (s=token; *s != '\0'; s++)
4193 if (strspn(s,"AaCcQqSsTt") != 0)
4194 coordinates+=(20.0*BezierQuantum)+360.0;
4195 break;
4196 }
4197 default:
4198 break;
4199 }
4200 if (status == MagickFalse)
4201 break;
4202 if (((size_t) (i+coordinates)) >= number_points)
4203 {
4204 /*
4205 Resize based on speculative points required by primitive.
4206 */
4207 number_points+=coordinates+1;
4208 if (number_points < (size_t) coordinates)
4209 {
4210 (void) ThrowMagickException(exception,GetMagickModule(),
4211 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4212 image->filename);
4213 break;
4214 }
4215 mvg_info.offset=i;
4216 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4217 number_points);
4218 primitive_info=(*mvg_info.primitive_info);
4219 }
4220 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,
4221 PrimitiveExtentPad);
4222 primitive_info=(*mvg_info.primitive_info);
4223 if (status == MagickFalse)
4224 break;
4225 mvg_info.offset=j;
4226 switch (primitive_type)
4227 {
4228 case PointPrimitive:
4229 default:
4230 {
4231 if (primitive_info[j].coordinates != 1)
4232 {
4233 status=MagickFalse;
4234 break;
4235 }
4236 status&=(MagickStatusType) TracePoint(primitive_info+j,
4237 primitive_info[j].point);
4238 primitive_info=(*mvg_info.primitive_info);
4239 i=j+(ssize_t) primitive_info[j].coordinates;
4240 break;
4241 }
4242 case LinePrimitive:
4243 {
4244 if (primitive_info[j].coordinates != 2)
4245 {
4246 status=MagickFalse;
4247 break;
4248 }
4249 status&=(MagickStatusType) TraceLine(primitive_info+j,
4250 primitive_info[j].point,primitive_info[j+1].point);
4251 primitive_info=(*mvg_info.primitive_info);
4252 i=j+(ssize_t) primitive_info[j].coordinates;
4253 break;
4254 }
4255 case RectanglePrimitive:
4256 {
4257 if (primitive_info[j].coordinates != 2)
4258 {
4259 status=MagickFalse;
4260 break;
4261 }
4262 status&=(MagickStatusType) TraceRectangle(primitive_info+j,
4263 primitive_info[j].point,primitive_info[j+1].point);
4264 primitive_info=(*mvg_info.primitive_info);
4265 i=j+(ssize_t) primitive_info[j].coordinates;
4266 break;
4267 }
4268 case RoundRectanglePrimitive:
4269 {
4270 if (primitive_info[j].coordinates != 3)
4271 {
4272 status=MagickFalse;
4273 break;
4274 }
4275 if ((primitive_info[j+2].point.x < 0.0) ||
4276 (primitive_info[j+2].point.y < 0.0))
4277 {
4278 status=MagickFalse;
4279 break;
4280 }
4281 if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
4282 {
4283 status=MagickFalse;
4284 break;
4285 }
4286 if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
4287 {
4288 status=MagickFalse;
4289 break;
4290 }
4291 status&=(MagickStatusType) TraceRoundRectangle(&mvg_info,
4292 primitive_info[j].point,primitive_info[j+1].point,
4293 primitive_info[j+2].point);
4294 primitive_info=(*mvg_info.primitive_info);
4295 i=j+(ssize_t) primitive_info[j].coordinates;
4296 break;
4297 }
4298 case ArcPrimitive:
4299 {
4300 if (primitive_info[j].coordinates != 3)
4301 {
4302 status=MagickFalse;
4303 break;
4304 }
4305 status&=(MagickStatusType) TraceArc(&mvg_info,primitive_info[j].point,
4306 primitive_info[j+1].point,primitive_info[j+2].point);
4307 primitive_info=(*mvg_info.primitive_info);
4308 i=j+(ssize_t) primitive_info[j].coordinates;
4309 break;
4310 }
4311 case EllipsePrimitive:
4312 {
4313 if (primitive_info[j].coordinates != 3)
4314 {
4315 status=MagickFalse;
4316 break;
4317 }
4318 if ((primitive_info[j+1].point.x < 0.0) ||
4319 (primitive_info[j+1].point.y < 0.0))
4320 {
4321 status=MagickFalse;
4322 break;
4323 }
4324 status&=(MagickStatusType) TraceEllipse(&mvg_info,
4325 primitive_info[j].point,primitive_info[j+1].point,
4326 primitive_info[j+2].point);
4327 primitive_info=(*mvg_info.primitive_info);
4328 i=j+(ssize_t) primitive_info[j].coordinates;
4329 break;
4330 }
4331 case CirclePrimitive:
4332 {
4333 if (primitive_info[j].coordinates != 2)
4334 {
4335 status=MagickFalse;
4336 break;
4337 }
4338 status&=(MagickStatusType) TraceCircle(&mvg_info,
4339 primitive_info[j].point,primitive_info[j+1].point);
4340 primitive_info=(*mvg_info.primitive_info);
4341 i=j+(ssize_t) primitive_info[j].coordinates;
4342 break;
4343 }
4344 case PolylinePrimitive:
4345 {
4346 if (primitive_info[j].coordinates < 1)
4347 {
4348 status=MagickFalse;
4349 break;
4350 }
4351 break;
4352 }
4353 case PolygonPrimitive:
4354 {
4355 if (primitive_info[j].coordinates < 3)
4356 {
4357 status=MagickFalse;
4358 break;
4359 }
4360 primitive_info[i]=primitive_info[j];
4361 primitive_info[i].coordinates=0;
4362 primitive_info[j].coordinates++;
4363 primitive_info[j].closed_subpath=MagickTrue;
4364 i++;
4365 break;
4366 }
4367 case BezierPrimitive:
4368 {
4369 if (primitive_info[j].coordinates < 3)
4370 {
4371 status=MagickFalse;
4372 break;
4373 }
4374 status&=(MagickStatusType) TraceBezier(&mvg_info,
4375 primitive_info[j].coordinates);
4376 primitive_info=(*mvg_info.primitive_info);
4377 i=j+(ssize_t) primitive_info[j].coordinates;
4378 break;
4379 }
4380 case PathPrimitive:
4381 {
4382 coordinates=(double) TracePath(&mvg_info,token,exception);
4383 primitive_info=(*mvg_info.primitive_info);
4384 if (coordinates < 0.0)
4385 {
4386 status=MagickFalse;
4387 break;
4388 }
4389 i=(ssize_t) (j+coordinates);
4390 break;
4391 }
4392 case AlphaPrimitive:
4393 case ColorPrimitive:
4394 {
4395 ssize_t
4396 method;
4397
4398 if (primitive_info[j].coordinates != 1)
4399 {
4400 status=MagickFalse;
4401 break;
4402 }
4403 (void) GetNextToken(q,&q,extent,token);
4404 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
4405 if (method == -1)
4406 {
4407 status=MagickFalse;
4408 break;
4409 }
4410 primitive_info[j].method=(PaintMethod) method;
4411 break;
4412 }
4413 case TextPrimitive:
4414 {
4415 if (primitive_info[j].coordinates != 1)
4416 {
4417 status=MagickFalse;
4418 break;
4419 }
4420 if (*token != ',')
4421 (void) GetNextToken(q,&q,extent,token);
4422 (void) CloneString(&primitive_info[j].text,token);
4423 /*
4424 Compute text cursor offset.
4425 */
4426 clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4427 if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
4428 (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
4429 {
4430 mvg_info.point=primitive_info->point;
4431 primitive_info->point.x+=cursor;
4432 }
4433 else
4434 {
4435 mvg_info.point=primitive_info->point;
4436 cursor=0.0;
4437 }
4438 clone_info->render=MagickFalse;
4439 clone_info->text=AcquireString(token);
4440 status&=(MagickStatusType) GetTypeMetrics(image,clone_info,
4441 &metrics,exception);
4442 clone_info=DestroyDrawInfo(clone_info);
4443 cursor+=metrics.width;
4444 if (graphic_context[n]->compliance != SVGCompliance)
4445 cursor=0.0;
4446 break;
4447 }
4448 case ImagePrimitive:
4449 {
4450 if (primitive_info[j].coordinates != 2)
4451 {
4452 status=MagickFalse;
4453 break;
4454 }
4455 (void) GetNextToken(q,&q,extent,token);
4456 (void) CloneString(&primitive_info[j].text,token);
4457 break;
4458 }
4459 }
4460 mvg_info.offset=i;
4461 if (status == 0)
4462 break;
4463 primitive_info[i].primitive=UndefinedPrimitive;
4464 if ((draw_info->debug != MagickFalse) && (q > p))
4465 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
4466 /*
4467 Sanity check.
4468 */
4469 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,ExpandAffine(
4470 &graphic_context[n]->affine));
4471 primitive_info=(*mvg_info.primitive_info);
4472 if (status == 0)
4473 break;
4474 status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4475 graphic_context[n]->stroke_width);
4476 primitive_info=(*mvg_info.primitive_info);
4477 if (status == 0)
4478 break;
4479 if (i == 0)
4480 continue;
4481 /*
4482 Transform points.
4483 */
4484 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4485 {
4486 point=primitive_info[i].point;
4487 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
4488 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
4489 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
4490 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
4491 point=primitive_info[i].point;
4492 if (point.x < graphic_context[n]->bounds.x1)
4493 graphic_context[n]->bounds.x1=point.x;
4494 if (point.y < graphic_context[n]->bounds.y1)
4495 graphic_context[n]->bounds.y1=point.y;
4496 if (point.x > graphic_context[n]->bounds.x2)
4497 graphic_context[n]->bounds.x2=point.x;
4498 if (point.y > graphic_context[n]->bounds.y2)
4499 graphic_context[n]->bounds.y2=point.y;
4500 if (primitive_info[i].primitive == ImagePrimitive)
4501 break;
4502 if (i >= (ssize_t) number_points)
4503 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
4504 }
4505 if (graphic_context[n]->render != MagickFalse)
4506 {
4507 if ((n != 0) && (graphic_context[n]->compliance != SVGCompliance) &&
4508 (graphic_context[n]->clip_mask != (char *) NULL) &&
4509 (LocaleCompare(graphic_context[n]->clip_mask,
4510 graphic_context[n-1]->clip_mask) != 0))
4511 {
4512 const char
4513 *clip_path;
4514
4515 clip_path=(const char *) GetValueFromSplayTree(macros,
4516 graphic_context[n]->clip_mask);
4517 if (clip_path != (const char *) NULL)
4518 (void) SetImageArtifact(image,graphic_context[n]->clip_mask,
4519 clip_path);
4520 status&=(MagickStatusType) DrawClipPath(image,graphic_context[n],
4521 graphic_context[n]->clip_mask,exception);
4522 }
4523 status&=(MagickStatusType) DrawPrimitive(image,graphic_context[n],
4524 primitive_info,exception);
4525 }
4526 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
4527 primitive_extent);
4528 if (proceed == MagickFalse)
4529 break;
4530 if (status == 0)
4531 break;
4532 }
4533 if (draw_info->debug != MagickFalse)
4534 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
4535 /*
4536 Relinquish resources.
4537 */
4538 macros=DestroySplayTree(macros);
4539 token=DestroyString(token);
4540 if (primitive_info != (PrimitiveInfo *) NULL)
4541 {
4542 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4543 if (primitive_info[i].text != (char *) NULL)
4544 primitive_info[i].text=DestroyString(primitive_info[i].text);
4545 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4546 }
4547 primitive=DestroyString(primitive);
4548 if (stops != (StopInfo *) NULL)
4549 stops=(StopInfo *) RelinquishMagickMemory(stops);
4550 for ( ; n >= 0; n--)
4551 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
4552 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
4553 if ((status == MagickFalse) && (exception->severity < ErrorException))
4554 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
4555 keyword);
4556 return(status != 0 ? MagickTrue : MagickFalse);
4557}
4558
4559MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
4560 ExceptionInfo *exception)
4561{
4562 return(RenderMVGContent(image,draw_info,0,exception));
4563}
4564
4565/*
4566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4567% %
4568% %
4569% %
4570% D r a w P a t t e r n P a t h %
4571% %
4572% %
4573% %
4574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4575%
4576% DrawPatternPath() draws a pattern.
4577%
4578% The format of the DrawPatternPath method is:
4579%
4580% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
4581% const char *name,Image **pattern,ExceptionInfo *exception)
4582%
4583% A description of each parameter follows:
4584%
4585% o image: the image.
4586%
4587% o draw_info: the draw info.
4588%
4589% o name: the pattern name.
4590%
4591% o image: the image.
4592%
4593% o exception: return any errors or warnings in this structure.
4594%
4595*/
4596MagickExport MagickBooleanType DrawPatternPath(Image *image,
4597 const DrawInfo *draw_info,const char *name,Image **pattern,
4598 ExceptionInfo *exception)
4599{
4600 char
4601 property[MagickPathExtent];
4602
4603 const char
4604 *geometry,
4605 *path,
4606 *type;
4607
4608 DrawInfo
4609 *clone_info;
4610
4611 ImageInfo
4612 *image_info;
4613
4614 MagickBooleanType
4615 status;
4616
4617 assert(image != (Image *) NULL);
4618 assert(image->signature == MagickCoreSignature);
4619 assert(draw_info != (const DrawInfo *) NULL);
4620 assert(name != (const char *) NULL);
4621 if (IsEventLogging() != MagickFalse)
4622 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4623 (void) FormatLocaleString(property,MagickPathExtent,"%s",name);
4624 path=GetImageArtifact(image,property);
4625 if (path == (const char *) NULL)
4626 return(MagickFalse);
4627 (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name);
4628 geometry=GetImageArtifact(image,property);
4629 if (geometry == (const char *) NULL)
4630 return(MagickFalse);
4631 if ((*pattern) != (Image *) NULL)
4632 *pattern=DestroyImage(*pattern);
4633 image_info=AcquireImageInfo();
4634 image_info->size=AcquireString(geometry);
4635 *pattern=AcquireImage(image_info,exception);
4636 image_info=DestroyImageInfo(image_info);
4637 (void) QueryColorCompliance("#00000000",AllCompliance,
4638 &(*pattern)->background_color,exception);
4639 (void) SetImageBackgroundColor(*pattern,exception);
4640 if (draw_info->debug != MagickFalse)
4641 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4642 "begin pattern-path %s %s",name,geometry);
4643 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4644 if (clone_info->fill_pattern != (Image *) NULL)
4645 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
4646 if (clone_info->stroke_pattern != (Image *) NULL)
4647 clone_info->stroke_pattern=DestroyImage(clone_info->stroke_pattern);
4648 (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name);
4649 type=GetImageArtifact(image,property);
4650 if (type != (const char *) NULL)
4651 clone_info->gradient.type=(GradientType) ParseCommandOption(
4652 MagickGradientOptions,MagickFalse,type);
4653 (void) CloneString(&clone_info->primitive,path);
4654 status=RenderMVGContent(*pattern,clone_info,0,exception);
4655 clone_info=DestroyDrawInfo(clone_info);
4656 if (draw_info->debug != MagickFalse)
4657 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
4658 return(status);
4659}
4660
4661/*
4662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4663% %
4664% %
4665% %
4666+ D r a w P o l y g o n P r i m i t i v e %
4667% %
4668% %
4669% %
4670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4671%
4672% DrawPolygonPrimitive() draws a polygon on the image.
4673%
4674% The format of the DrawPolygonPrimitive method is:
4675%
4676% MagickBooleanType DrawPolygonPrimitive(Image *image,
4677% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4678% ExceptionInfo *exception)
4679%
4680% A description of each parameter follows:
4681%
4682% o image: the image.
4683%
4684% o draw_info: the draw info.
4685%
4686% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4687%
4688% o exception: return any errors or warnings in this structure.
4689%
4690*/
4691
4692static PolygonInfo **DestroyPolygonTLS(PolygonInfo **polygon_info)
4693{
4694 ssize_t
4695 i;
4696
4697 assert(polygon_info != (PolygonInfo **) NULL);
4698 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
4699 if (polygon_info[i] != (PolygonInfo *) NULL)
4700 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
4701 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
4702 return(polygon_info);
4703}
4704
4705static PolygonInfo **AcquirePolygonTLS(const PrimitiveInfo *primitive_info,
4706 ExceptionInfo *exception)
4707{
4708 PathInfo
4709 *magick_restrict path_info;
4710
4712 **polygon_info;
4713
4714 size_t
4715 number_threads;
4716
4717 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4718 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
4719 sizeof(*polygon_info));
4720 if (polygon_info == (PolygonInfo **) NULL)
4721 {
4722 (void) ThrowMagickException(exception,GetMagickModule(),
4723 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4724 return((PolygonInfo **) NULL);
4725 }
4726 (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
4727 path_info=ConvertPrimitiveToPath(primitive_info,exception);
4728 if (path_info == (PathInfo *) NULL)
4729 return(DestroyPolygonTLS(polygon_info));
4730 polygon_info[0]=ConvertPathToPolygon(path_info,exception);
4731 if (polygon_info[0] == (PolygonInfo *) NULL)
4732 {
4733 (void) ThrowMagickException(exception,GetMagickModule(),
4734 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4735 return(DestroyPolygonTLS(polygon_info));
4736 }
4737 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
4738 return(polygon_info);
4739}
4740
4741static MagickBooleanType ClonePolygonEdgesTLS(PolygonInfo **polygon_info,
4742 const size_t number_threads,ExceptionInfo *exception)
4743{
4744 ssize_t
4745 i;
4746
4747 for (i=1; i < (ssize_t) number_threads; i++)
4748 {
4749 EdgeInfo
4750 *edge_info;
4751
4752 ssize_t
4753 j;
4754
4755 polygon_info[i]=(PolygonInfo *) AcquireMagickMemory(
4756 sizeof(*polygon_info[i]));
4757 if (polygon_info[i] == (PolygonInfo *) NULL)
4758 {
4759 (void) ThrowMagickException(exception,GetMagickModule(),
4760 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4761 return(MagickFalse);
4762 }
4763 polygon_info[i]->number_edges=0;
4764 edge_info=polygon_info[0]->edges;
4765 polygon_info[i]->edges=(EdgeInfo *) AcquireQuantumMemory(
4766 polygon_info[0]->number_edges,sizeof(*edge_info));
4767 if (polygon_info[i]->edges == (EdgeInfo *) NULL)
4768 {
4769 (void) ThrowMagickException(exception,GetMagickModule(),
4770 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4771 return(MagickFalse);
4772 }
4773 (void) memcpy(polygon_info[i]->edges,edge_info,
4774 polygon_info[0]->number_edges*sizeof(*edge_info));
4775 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4776 polygon_info[i]->edges[j].points=(PointInfo *) NULL;
4777 polygon_info[i]->number_edges=polygon_info[0]->number_edges;
4778 for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4779 {
4780 edge_info=polygon_info[0]->edges+j;
4781 polygon_info[i]->edges[j].points=(PointInfo *) AcquireQuantumMemory(
4782 edge_info->number_points,sizeof(*edge_info));
4783 if (polygon_info[i]->edges[j].points == (PointInfo *) NULL)
4784 {
4785 (void) ThrowMagickException(exception,GetMagickModule(),
4786 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4787 return(MagickFalse);
4788 }
4789 (void) memcpy(polygon_info[i]->edges[j].points,edge_info->points,
4790 edge_info->number_points*sizeof(*edge_info->points));
4791 }
4792 }
4793 return(MagickTrue);
4794}
4795
4796static size_t DestroyEdge(PolygonInfo *polygon_info,const ssize_t edge)
4797{
4798 assert(edge < (ssize_t) polygon_info->number_edges);
4799 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
4800 polygon_info->edges[edge].points);
4801 polygon_info->number_edges--;
4802 if (edge < (ssize_t) polygon_info->number_edges)
4803 (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
4804 (polygon_info->number_edges-(size_t) edge)*sizeof(*polygon_info->edges));
4805 return(polygon_info->number_edges);
4806}
4807
4808static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
4809 const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
4810 const ssize_t y,double *stroke_alpha)
4811{
4812 double
4813 alpha,
4814 beta,
4815 distance,
4816 subpath_alpha;
4817
4818 const PointInfo
4819 *q;
4820
4821 EdgeInfo
4822 *p;
4823
4824 PointInfo
4825 delta;
4826
4827 ssize_t
4828 i,
4829 j,
4830 winding_number;
4831
4832 /*
4833 Compute fill & stroke opacity for this (x,y) point.
4834 */
4835 *stroke_alpha=0.0;
4836 subpath_alpha=0.0;
4837 p=polygon_info->edges;
4838 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4839 {
4840 if ((double) y <= (p->bounds.y1-mid-0.5))
4841 break;
4842 if ((double) y > (p->bounds.y2+mid+0.5))
4843 {
4844 p--;
4845 (void) DestroyEdge(polygon_info,j--);
4846 continue;
4847 }
4848 if (((double) x <= (p->bounds.x1-mid-0.5)) ||
4849 ((double) x > (p->bounds.x2+mid+0.5)))
4850 continue;
4851 i=(ssize_t) MagickMax((double) p->highwater,1.0);
4852 for ( ; i < (ssize_t) p->number_points; i++)
4853 {
4854 if ((double) y <= (p->points[i-1].y-mid-0.5))
4855 break;
4856 if ((double) y > (p->points[i].y+mid+0.5))
4857 continue;
4858 if (p->scanline != (double) y)
4859 {
4860 p->scanline=(double) y;
4861 p->highwater=(size_t) i;
4862 }
4863 /*
4864 Compute distance between a point and an edge.
4865 */
4866 q=p->points+i-1;
4867 delta.x=(q+1)->x-q->x;
4868 delta.y=(q+1)->y-q->y;
4869 beta=delta.x*(x-q->x)+delta.y*(y-q->y); /* segLen*point-cos(theta) */
4870 if (beta <= 0.0)
4871 {
4872 /*
4873 Cosine <= 0, point is closest to q.
4874 */
4875 delta.x=(double) x-q->x;
4876 delta.y=(double) y-q->y;
4877 distance=delta.x*delta.x+delta.y*delta.y;
4878 }
4879 else
4880 {
4881 alpha=delta.x*delta.x+delta.y*delta.y; /* segLen*segLen */
4882 if (beta >= alpha)
4883 {
4884 /*
4885 Point is closest to q+1.
4886 */
4887 delta.x=(double) x-(q+1)->x;
4888 delta.y=(double) y-(q+1)->y;
4889 distance=delta.x*delta.x+delta.y*delta.y;
4890 }
4891 else
4892 {
4893 /*
4894 Point is closest to point between q & q+1.
4895 */
4896 alpha=PerceptibleReciprocal(alpha);
4897 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
4898 distance=alpha*beta*beta;
4899 }
4900 }
4901 /*
4902 Compute stroke & subpath opacity.
4903 */
4904 beta=0.0;
4905 if (p->ghostline == MagickFalse)
4906 {
4907 alpha=mid+0.5;
4908 if ((*stroke_alpha < 1.0) &&
4909 (distance <= ((alpha+0.25)*(alpha+0.25))))
4910 {
4911 alpha=mid-0.5;
4912 if (distance <= ((alpha+0.25)*(alpha+0.25)))
4913 *stroke_alpha=1.0;
4914 else
4915 {
4916 beta=1.0;
4917 if (fabs(distance-1.0) >= MagickEpsilon)
4918 beta=sqrt((double) distance);
4919 alpha=beta-mid-0.5;
4920 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
4921 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
4922 }
4923 }
4924 }
4925 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
4926 continue;
4927 if (distance <= 0.0)
4928 {
4929 subpath_alpha=1.0;
4930 continue;
4931 }
4932 if (distance > 1.0)
4933 continue;
4934 if (fabs(beta) < MagickEpsilon)
4935 {
4936 beta=1.0;
4937 if (fabs(distance-1.0) >= MagickEpsilon)
4938 beta=sqrt(distance);
4939 }
4940 alpha=beta-1.0;
4941 if (subpath_alpha < (alpha*alpha))
4942 subpath_alpha=alpha*alpha;
4943 }
4944 }
4945 /*
4946 Compute fill opacity.
4947 */
4948 if (fill == MagickFalse)
4949 return(0.0);
4950 if (subpath_alpha >= 1.0)
4951 return(1.0);
4952 /*
4953 Determine winding number.
4954 */
4955 winding_number=0;
4956 p=polygon_info->edges;
4957 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4958 {
4959 if ((double) y <= p->bounds.y1)
4960 break;
4961 if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
4962 continue;
4963 if ((double) x > p->bounds.x2)
4964 {
4965 winding_number+=p->direction != 0 ? 1 : -1;
4966 continue;
4967 }
4968 i=(ssize_t) MagickMax((double) p->highwater,1.0);
4969 for ( ; i < (ssize_t) (p->number_points-1); i++)
4970 if ((double) y <= p->points[i].y)
4971 break;
4972 q=p->points+i-1;
4973 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
4974 winding_number+=p->direction != 0 ? 1 : -1;
4975 }
4976 if (fill_rule != NonZeroRule)
4977 {
4978 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
4979 return(1.0);
4980 }
4981 else
4982 if (MagickAbsoluteValue(winding_number) != 0)
4983 return(1.0);
4984 return(subpath_alpha);
4985}
4986
4987static MagickBooleanType DrawPolygonPrimitive(Image *image,
4988 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4989 ExceptionInfo *exception)
4990{
4991 typedef struct _ExtentInfo
4992 {
4993 ssize_t
4994 x1,
4995 y1,
4996 x2,
4997 y2;
4998 } ExtentInfo;
4999
5000 CacheView
5001 *image_view;
5002
5003 const char
5004 *artifact;
5005
5006 double
5007 mid;
5008
5009 EdgeInfo
5010 *p;
5011
5012 ExtentInfo
5013 poly_extent;
5014
5015 MagickBooleanType
5016 fill,
5017 status;
5018
5020 **magick_restrict polygon_info;
5021
5023 bounds;
5024
5025 size_t
5026 number_threads;
5027
5028 ssize_t
5029 i,
5030 y;
5031
5032 assert(image != (Image *) NULL);
5033 assert(image->signature == MagickCoreSignature);
5034 assert(draw_info != (DrawInfo *) NULL);
5035 assert(draw_info->signature == MagickCoreSignature);
5036 assert(primitive_info != (PrimitiveInfo *) NULL);
5037 if (IsEventLogging() != MagickFalse)
5038 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5039 if (primitive_info->coordinates <= 1)
5040 return(MagickTrue);
5041 /*
5042 Compute bounding box.
5043 */
5044 polygon_info=AcquirePolygonTLS(primitive_info,exception);
5045 if (polygon_info == (PolygonInfo **) NULL)
5046 return(MagickFalse);
5047 if (draw_info->debug != MagickFalse)
5048 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
5049 fill=(primitive_info->method == FillToBorderMethod) ||
5050 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
5051 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5052 bounds=polygon_info[0]->edges[0].bounds;
5053 artifact=GetImageArtifact(image,"draw:render-bounding-rectangles");
5054 if (IsStringTrue(artifact) != MagickFalse)
5055 (void) DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
5056 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
5057 {
5058 p=polygon_info[0]->edges+i;
5059 if (p->bounds.x1 < bounds.x1)
5060 bounds.x1=p->bounds.x1;
5061 if (p->bounds.y1 < bounds.y1)
5062 bounds.y1=p->bounds.y1;
5063 if (p->bounds.x2 > bounds.x2)
5064 bounds.x2=p->bounds.x2;
5065 if (p->bounds.y2 > bounds.y2)
5066 bounds.y2=p->bounds.y2;
5067 }
5068 bounds.x1-=(mid+1.0);
5069 bounds.y1-=(mid+1.0);
5070 bounds.x2+=(mid+1.0);
5071 bounds.y2+=(mid+1.0);
5072 if ((bounds.x1 >= (double) image->columns) ||
5073 (bounds.y1 >= (double) image->rows) ||
5074 (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
5075 {
5076 polygon_info=DestroyPolygonTLS(polygon_info);
5077 return(MagickTrue); /* virtual polygon */
5078 }
5079 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
5080 (double) image->columns-1.0 : bounds.x1;
5081 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
5082 (double) image->rows-1.0 : bounds.y1;
5083 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
5084 (double) image->columns-1.0 : bounds.x2;
5085 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
5086 (double) image->rows-1.0 : bounds.y2;
5087 poly_extent.x1=CastDoubleToLong(ceil(bounds.x1-0.5));
5088 poly_extent.y1=CastDoubleToLong(ceil(bounds.y1-0.5));
5089 poly_extent.x2=CastDoubleToLong(floor(bounds.x2+0.5));
5090 poly_extent.y2=CastDoubleToLong(floor(bounds.y2+0.5));
5091 number_threads=(size_t) GetMagickNumberThreads(image,image,(size_t)
5092 (poly_extent.y2-poly_extent.y1+1),1);
5093 status=ClonePolygonEdgesTLS(polygon_info,number_threads,exception);
5094 if (status == MagickFalse)
5095 {
5096 polygon_info=DestroyPolygonTLS(polygon_info);
5097 return(status);
5098 }
5099 image_view=AcquireAuthenticCacheView(image,exception);
5100 if ((primitive_info->coordinates == 1) ||
5101 (polygon_info[0]->number_edges == 0))
5102 {
5103 /*
5104 Draw point.
5105 */
5106#if defined(MAGICKCORE_OPENMP_SUPPORT)
5107 #pragma omp parallel for schedule(static) shared(status) \
5108 num_threads((int) number_threads)
5109#endif
5110 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5111 {
5112 PixelInfo
5113 pixel;
5114
5115 ssize_t
5116 x;
5117
5118 Quantum
5119 *magick_restrict q;
5120
5121 if (status == MagickFalse)
5122 continue;
5123 x=poly_extent.x1;
5124 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (poly_extent.x2-
5125 x+1),1,exception);
5126 if (q == (Quantum *) NULL)
5127 {
5128 status=MagickFalse;
5129 continue;
5130 }
5131 GetPixelInfo(image,&pixel);
5132 for ( ; x <= poly_extent.x2; x++)
5133 {
5134 if ((x == CastDoubleToLong(ceil(primitive_info->point.x-0.5))) &&
5135 (y == CastDoubleToLong(ceil(primitive_info->point.y-0.5))))
5136 {
5137 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&pixel,
5138 exception);
5139 SetPixelViaPixelInfo(image,&pixel,q);
5140 }
5141 q+=(ptrdiff_t) GetPixelChannels(image);
5142 }
5143 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5144 status=MagickFalse;
5145 }
5146 image_view=DestroyCacheView(image_view);
5147 polygon_info=DestroyPolygonTLS(polygon_info);
5148 if (draw_info->debug != MagickFalse)
5149 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5150 " end draw-polygon");
5151 return(status);
5152 }
5153 /*
5154 Draw polygon or line.
5155 */
5156#if defined(MAGICKCORE_OPENMP_SUPPORT)
5157 #pragma omp parallel for schedule(static) shared(status) \
5158 num_threads((int) number_threads)
5159#endif
5160 for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5161 {
5162 const int
5163 id = GetOpenMPThreadId();
5164
5165 Quantum
5166 *magick_restrict q;
5167
5168 ssize_t
5169 x;
5170
5171 if (status == MagickFalse)
5172 continue;
5173 q=GetCacheViewAuthenticPixels(image_view,poly_extent.x1,y,(size_t)
5174 (poly_extent.x2-poly_extent.x1+1),1,exception);
5175 if (q == (Quantum *) NULL)
5176 {
5177 status=MagickFalse;
5178 continue;
5179 }
5180 for (x=poly_extent.x1; x <= poly_extent.x2; x++)
5181 {
5182 double
5183 fill_alpha,
5184 stroke_alpha;
5185
5186 PixelInfo
5187 fill_color,
5188 stroke_color;
5189
5190 /*
5191 Fill and/or stroke.
5192 */
5193 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
5194 x,y,&stroke_alpha);
5195 if (draw_info->stroke_antialias == MagickFalse)
5196 {
5197 fill_alpha=fill_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5198 stroke_alpha=stroke_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5199 }
5200 GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&fill_color,
5201 exception);
5202 CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
5203 (double) GetPixelAlpha(image,q),q);
5204 GetStrokeColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&stroke_color,
5205 exception);
5206 CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
5207 (double) GetPixelAlpha(image,q),q);
5208 q+=(ptrdiff_t) GetPixelChannels(image);
5209 }
5210 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5211 status=MagickFalse;
5212 }
5213 image_view=DestroyCacheView(image_view);
5214 polygon_info=DestroyPolygonTLS(polygon_info);
5215 if (draw_info->debug != MagickFalse)
5216 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
5217 return(status);
5218}
5219
5220/*
5221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5222% %
5223% %
5224% %
5225% D r a w P r i m i t i v e %
5226% %
5227% %
5228% %
5229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5230%
5231% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
5232%
5233% The format of the DrawPrimitive method is:
5234%
5235% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
5236% PrimitiveInfo *primitive_info,ExceptionInfo *exception)
5237%
5238% A description of each parameter follows:
5239%
5240% o image: the image.
5241%
5242% o draw_info: the draw info.
5243%
5244% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5245%
5246% o exception: return any errors or warnings in this structure.
5247%
5248*/
5249static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
5250{
5251 const char
5252 *methods[] =
5253 {
5254 "point",
5255 "replace",
5256 "floodfill",
5257 "filltoborder",
5258 "reset",
5259 "?"
5260 };
5261
5262 PointInfo
5263 p,
5264 point,
5265 q;
5266
5267 ssize_t
5268 i,
5269 x;
5270
5271 ssize_t
5272 coordinates,
5273 y;
5274
5275 x=CastDoubleToLong(ceil(primitive_info->point.x-0.5));
5276 y=CastDoubleToLong(ceil(primitive_info->point.y-0.5));
5277 switch (primitive_info->primitive)
5278 {
5279 case AlphaPrimitive:
5280 {
5281 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5282 "AlphaPrimitive %.20g,%.20g %s",(double) x,(double) y,
5283 methods[primitive_info->method]);
5284 return;
5285 }
5286 case ColorPrimitive:
5287 {
5288 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5289 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
5290 methods[primitive_info->method]);
5291 return;
5292 }
5293 case ImagePrimitive:
5294 {
5295 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5296 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
5297 return;
5298 }
5299 case PointPrimitive:
5300 {
5301 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5302 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
5303 methods[primitive_info->method]);
5304 return;
5305 }
5306 case TextPrimitive:
5307 {
5308 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5309 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
5310 return;
5311 }
5312 default:
5313 break;
5314 }
5315 coordinates=0;
5316 p=primitive_info[0].point;
5317 q.x=(-1.0);
5318 q.y=(-1.0);
5319 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5320 {
5321 point=primitive_info[i].point;
5322 if (coordinates <= 0)
5323 {
5324 coordinates=(ssize_t) primitive_info[i].coordinates;
5325 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5326 " begin open (%.20g)",(double) coordinates);
5327 p=point;
5328 }
5329 point=primitive_info[i].point;
5330 if ((fabs(q.x-point.x) >= MagickEpsilon) ||
5331 (fabs(q.y-point.y) >= MagickEpsilon))
5332 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5333 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
5334 else
5335 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5336 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
5337 q=point;
5338 coordinates--;
5339 if (coordinates > 0)
5340 continue;
5341 if ((fabs(p.x-point.x) >= MagickEpsilon) ||
5342 (fabs(p.y-point.y) >= MagickEpsilon))
5343 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
5344 (double) coordinates);
5345 else
5346 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
5347 (double) coordinates);
5348 }
5349}
5350
5351MagickExport MagickBooleanType DrawPrimitive(Image *image,
5352 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5353 ExceptionInfo *exception)
5354{
5355 CacheView
5356 *image_view;
5357
5358 MagickStatusType
5359 status;
5360
5361 ssize_t
5362 i,
5363 x;
5364
5365 ssize_t
5366 y;
5367
5368 if (draw_info->debug != MagickFalse)
5369 {
5370 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5371 " begin draw-primitive");
5372 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5373 " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
5374 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
5375 draw_info->affine.tx,draw_info->affine.ty);
5376 }
5377 status=MagickTrue;
5378 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5379 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
5380 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
5381 status&=(MagickStatusType) SetImageColorspace(image,sRGBColorspace,
5382 exception);
5383 if (draw_info->compliance == SVGCompliance)
5384 {
5385 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5386 draw_info->clipping_mask,exception);
5387 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5388 draw_info->composite_mask,exception);
5389 }
5390 x=CastDoubleToLong(ceil(primitive_info->point.x-0.5));
5391 y=CastDoubleToLong(ceil(primitive_info->point.y-0.5));
5392 image_view=AcquireAuthenticCacheView(image,exception);
5393 switch (primitive_info->primitive)
5394 {
5395 case AlphaPrimitive:
5396 {
5397 if ((image->alpha_trait & BlendPixelTrait) == 0)
5398 status&=(MagickStatusType) SetImageAlphaChannel(image,
5399 OpaqueAlphaChannel,exception);
5400 switch (primitive_info->method)
5401 {
5402 case PointMethod:
5403 default:
5404 {
5405 PixelInfo
5406 pixel;
5407
5408 Quantum
5409 *q;
5410
5411 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5412 if (q == (Quantum *) NULL)
5413 break;
5414 GetFillColor(draw_info,x,y,&pixel,exception);
5415 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5416 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5417 exception);
5418 break;
5419 }
5420 case ReplaceMethod:
5421 {
5422 PixelInfo
5423 pixel,
5424 target;
5425
5426 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5427 x,y,&target,exception);
5428 GetPixelInfo(image,&pixel);
5429 for (y=0; y < (ssize_t) image->rows; y++)
5430 {
5431 Quantum
5432 *magick_restrict q;
5433
5434 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5435 exception);
5436 if (q == (Quantum *) NULL)
5437 break;
5438 for (x=0; x < (ssize_t) image->columns; x++)
5439 {
5440 GetPixelInfoPixel(image,q,&pixel);
5441 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5442 {
5443 q+=(ptrdiff_t) GetPixelChannels(image);
5444 continue;
5445 }
5446 GetFillColor(draw_info,x,y,&pixel,exception);
5447 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5448 q+=(ptrdiff_t) GetPixelChannels(image);
5449 }
5450 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5451 exception);
5452 if (status == MagickFalse)
5453 break;
5454 }
5455 break;
5456 }
5457 case FloodfillMethod:
5458 case FillToBorderMethod:
5459 {
5460 ChannelType
5461 channel_mask;
5462
5463 PixelInfo
5464 target;
5465
5466 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5467 TileVirtualPixelMethod,x,y,&target,exception);
5468 if (primitive_info->method == FillToBorderMethod)
5469 {
5470 target.red=(double) draw_info->border_color.red;
5471 target.green=(double) draw_info->border_color.green;
5472 target.blue=(double) draw_info->border_color.blue;
5473 }
5474 channel_mask=SetImageChannelMask(image,AlphaChannel);
5475 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5476 &target,x,y,primitive_info->method == FloodfillMethod ?
5477 MagickFalse : MagickTrue,exception);
5478 (void) SetImageChannelMask(image,channel_mask);
5479 break;
5480 }
5481 case ResetMethod:
5482 {
5483 PixelInfo
5484 pixel;
5485
5486 for (y=0; y < (ssize_t) image->rows; y++)
5487 {
5488 Quantum
5489 *magick_restrict q;
5490
5491 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5492 exception);
5493 if (q == (Quantum *) NULL)
5494 break;
5495 for (x=0; x < (ssize_t) image->columns; x++)
5496 {
5497 GetFillColor(draw_info,x,y,&pixel,exception);
5498 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5499 q+=(ptrdiff_t) GetPixelChannels(image);
5500 }
5501 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5502 exception);
5503 if (status == MagickFalse)
5504 break;
5505 }
5506 break;
5507 }
5508 }
5509 break;
5510 }
5511 case ColorPrimitive:
5512 {
5513 switch (primitive_info->method)
5514 {
5515 case PointMethod:
5516 default:
5517 {
5518 PixelInfo
5519 pixel;
5520
5521 Quantum
5522 *q;
5523
5524 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5525 if (q == (Quantum *) NULL)
5526 break;
5527 GetPixelInfo(image,&pixel);
5528 GetFillColor(draw_info,x,y,&pixel,exception);
5529 SetPixelViaPixelInfo(image,&pixel,q);
5530 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5531 exception);
5532 break;
5533 }
5534 case ReplaceMethod:
5535 {
5536 PixelInfo
5537 pixel,
5538 target;
5539
5540 status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5541 x,y,&target,exception);
5542 for (y=0; y < (ssize_t) image->rows; y++)
5543 {
5544 Quantum
5545 *magick_restrict q;
5546
5547 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5548 exception);
5549 if (q == (Quantum *) NULL)
5550 break;
5551 for (x=0; x < (ssize_t) image->columns; x++)
5552 {
5553 GetPixelInfoPixel(image,q,&pixel);
5554 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5555 {
5556 q+=(ptrdiff_t) GetPixelChannels(image);
5557 continue;
5558 }
5559 GetFillColor(draw_info,x,y,&pixel,exception);
5560 SetPixelViaPixelInfo(image,&pixel,q);
5561 q+=(ptrdiff_t) GetPixelChannels(image);
5562 }
5563 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5564 exception);
5565 if (status == MagickFalse)
5566 break;
5567 }
5568 break;
5569 }
5570 case FloodfillMethod:
5571 case FillToBorderMethod:
5572 {
5573 PixelInfo
5574 target;
5575
5576 status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5577 TileVirtualPixelMethod,x,y,&target,exception);
5578 if (primitive_info->method == FillToBorderMethod)
5579 {
5580 target.red=(double) draw_info->border_color.red;
5581 target.green=(double) draw_info->border_color.green;
5582 target.blue=(double) draw_info->border_color.blue;
5583 }
5584 status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5585 &target,x,y,primitive_info->method == FloodfillMethod ?
5586 MagickFalse : MagickTrue,exception);
5587 break;
5588 }
5589 case ResetMethod:
5590 {
5591 PixelInfo
5592 pixel;
5593
5594 GetPixelInfo(image,&pixel);
5595 for (y=0; y < (ssize_t) image->rows; y++)
5596 {
5597 Quantum
5598 *magick_restrict q;
5599
5600 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5601 exception);
5602 if (q == (Quantum *) NULL)
5603 break;
5604 for (x=0; x < (ssize_t) image->columns; x++)
5605 {
5606 GetFillColor(draw_info,x,y,&pixel,exception);
5607 SetPixelViaPixelInfo(image,&pixel,q);
5608 q+=(ptrdiff_t) GetPixelChannels(image);
5609 }
5610 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5611 exception);
5612 if (status == MagickFalse)
5613 break;
5614 }
5615 break;
5616 }
5617 }
5618 break;
5619 }
5620 case ImagePrimitive:
5621 {
5623 affine;
5624
5625 char
5626 composite_geometry[MagickPathExtent];
5627
5628 Image
5629 *composite_image,
5630 *composite_images;
5631
5632 ImageInfo
5633 *clone_info;
5634
5636 geometry;
5637
5638 ssize_t
5639 x1,
5640 y1;
5641
5642 if (primitive_info->text == (char *) NULL)
5643 break;
5644 clone_info=AcquireImageInfo();
5645 composite_images=(Image *) NULL;
5646 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
5647 composite_images=ReadInlineImage(clone_info,primitive_info->text,
5648 exception);
5649 else
5650 if (*primitive_info->text != '\0')
5651 {
5652 /*
5653 Read composite image.
5654 */
5655 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5656 MagickPathExtent);
5657 (void) SetImageInfo(clone_info,1,exception);
5658 (void) CopyMagickString(clone_info->filename,primitive_info->text,
5659 MagickPathExtent);
5660 if (clone_info->size != (char *) NULL)
5661 clone_info->size=DestroyString(clone_info->size);
5662 if (clone_info->extract != (char *) NULL)
5663 clone_info->extract=DestroyString(clone_info->extract);
5664 if ((LocaleCompare(clone_info->magick,"ftp") != 0) &&
5665 (LocaleCompare(clone_info->magick,"http") != 0) &&
5666 (LocaleCompare(clone_info->magick,"https") != 0) &&
5667 (LocaleCompare(clone_info->magick,"mvg") != 0) &&
5668 (LocaleCompare(clone_info->magick,"vid") != 0))
5669 composite_images=ReadImage(clone_info,exception);
5670 else
5671 (void) ThrowMagickException(exception,GetMagickModule(),
5672 FileOpenError,"UnableToOpenFile","`%s'",clone_info->filename);
5673 }
5674 clone_info=DestroyImageInfo(clone_info);
5675 if (composite_images == (Image *) NULL)
5676 {
5677 status=MagickFalse;
5678 break;
5679 }
5680 composite_image=RemoveFirstImageFromList(&composite_images);
5681 composite_images=DestroyImageList(composite_images);
5682 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
5683 NULL,(void *) NULL);
5684 x1=CastDoubleToLong(ceil(primitive_info[1].point.x-0.5));
5685 y1=CastDoubleToLong(ceil(primitive_info[1].point.y-0.5));
5686 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
5687 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
5688 {
5689 /*
5690 Resize image.
5691 */
5692 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5693 "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
5694 composite_image->filter=image->filter;
5695 status&=(MagickStatusType) TransformImage(&composite_image,
5696 (char *) NULL,composite_geometry,exception);
5697 }
5698 if (composite_image->alpha_trait == UndefinedPixelTrait)
5699 status&=(MagickStatusType) SetImageAlphaChannel(composite_image,
5700 OpaqueAlphaChannel,exception);
5701 if (draw_info->alpha != OpaqueAlpha)
5702 status&=(MagickStatusType) SetImageAlpha(composite_image,
5703 draw_info->alpha,exception);
5704 SetGeometry(image,&geometry);
5705 image->gravity=draw_info->gravity;
5706 geometry.x=x;
5707 geometry.y=y;
5708 (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5709 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
5710 composite_image->rows,(double) geometry.x,(double) geometry.y);
5711 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
5712 affine=draw_info->affine;
5713 affine.tx=(double) geometry.x;
5714 affine.ty=(double) geometry.y;
5715 composite_image->interpolate=image->interpolate;
5716 if ((draw_info->compose == OverCompositeOp) ||
5717 (draw_info->compose == SrcOverCompositeOp))
5718 status&=(MagickStatusType) DrawAffineImage(image,composite_image,
5719 &affine,exception);
5720 else
5721 status&=(MagickStatusType) CompositeImage(image,composite_image,
5722 draw_info->compose,MagickTrue,geometry.x,geometry.y,exception);
5723 composite_image=DestroyImage(composite_image);
5724 break;
5725 }
5726 case PointPrimitive:
5727 {
5728 PixelInfo
5729 fill_color;
5730
5731 Quantum
5732 *q;
5733
5734 if ((y < 0) || (y >= (ssize_t) image->rows))
5735 break;
5736 if ((x < 0) || (x >= (ssize_t) image->columns))
5737 break;
5738 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5739 if (q == (Quantum *) NULL)
5740 break;
5741 GetFillColor(draw_info,x,y,&fill_color,exception);
5742 CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,(double)
5743 GetPixelAlpha(image,q),q);
5744 status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5745 exception);
5746 break;
5747 }
5748 case TextPrimitive:
5749 {
5750 char
5751 geometry[MagickPathExtent];
5752
5753 DrawInfo
5754 *clone_info;
5755
5756 if (primitive_info->text == (char *) NULL)
5757 break;
5758 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5759 (void) CloneString(&clone_info->text,primitive_info->text);
5760 (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
5761 primitive_info->point.x,primitive_info->point.y);
5762 (void) CloneString(&clone_info->geometry,geometry);
5763 status&=(MagickStatusType) AnnotateImage(image,clone_info,exception);
5764 clone_info=DestroyDrawInfo(clone_info);
5765 break;
5766 }
5767 default:
5768 {
5769 double
5770 mid,
5771 scale;
5772
5773 DrawInfo
5774 *clone_info;
5775
5776 if (IsEventLogging() != MagickFalse)
5777 LogPrimitiveInfo(primitive_info);
5778 scale=ExpandAffine(&draw_info->affine);
5779 if ((draw_info->dash_pattern != (double *) NULL) &&
5780 (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
5781 (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
5782 (draw_info->stroke.alpha != (double) TransparentAlpha))
5783 {
5784 /*
5785 Draw dash polygon.
5786 */
5787 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5788 clone_info->stroke_width=0.0;
5789 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5790 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5791 primitive_info,exception);
5792 clone_info=DestroyDrawInfo(clone_info);
5793 if (status != MagickFalse)
5794 status&=(MagickStatusType) DrawDashPolygon(draw_info,primitive_info,
5795 image,exception);
5796 break;
5797 }
5798 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5799 if ((mid > 1.0) &&
5800 ((draw_info->stroke.alpha != (double) TransparentAlpha) ||
5801 (draw_info->stroke_pattern != (Image *) NULL)))
5802 {
5803 double
5804 point_x,
5805 point_y;
5806
5807 MagickBooleanType
5808 closed_path;
5809
5810 /*
5811 Draw strokes while respecting line cap/join attributes.
5812 */
5813 closed_path=primitive_info[0].closed_subpath;
5814 i=(ssize_t) primitive_info[0].coordinates;
5815 point_x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
5816 point_y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
5817 if ((point_x < MagickEpsilon) && (point_y < MagickEpsilon))
5818 closed_path=MagickTrue;
5819 if ((((draw_info->linecap == RoundCap) ||
5820 (closed_path != MagickFalse)) &&
5821 (draw_info->linejoin == RoundJoin)) ||
5822 (primitive_info[i].primitive != UndefinedPrimitive))
5823 {
5824 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5825 primitive_info,exception);
5826 break;
5827 }
5828 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5829 clone_info->stroke_width=0.0;
5830 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5831 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5832 primitive_info,exception);
5833 clone_info=DestroyDrawInfo(clone_info);
5834 if (status != MagickFalse)
5835 status&=(MagickStatusType) DrawStrokePolygon(image,draw_info,
5836 primitive_info,exception);
5837 break;
5838 }
5839 status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5840 primitive_info,exception);
5841 break;
5842 }
5843 }
5844 image_view=DestroyCacheView(image_view);
5845 if (draw_info->compliance == SVGCompliance)
5846 {
5847 status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5848 (Image *) NULL,exception);
5849 status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5850 (Image *) NULL,exception);
5851 }
5852 if (draw_info->debug != MagickFalse)
5853 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
5854 return(status != 0 ? MagickTrue : MagickFalse);
5855}
5856
5857/*
5858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5859% %
5860% %
5861% %
5862+ D r a w S t r o k e P o l y g o n %
5863% %
5864% %
5865% %
5866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5867%
5868% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
5869% the image while respecting the line cap and join attributes.
5870%
5871% The format of the DrawStrokePolygon method is:
5872%
5873% MagickBooleanType DrawStrokePolygon(Image *image,
5874% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
5875%
5876% A description of each parameter follows:
5877%
5878% o image: the image.
5879%
5880% o draw_info: the draw info.
5881%
5882% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5883%
5884%
5885*/
5886
5887static MagickBooleanType DrawRoundLinecap(Image *image,
5888 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5889 ExceptionInfo *exception)
5890{
5892 linecap[5];
5893
5894 ssize_t
5895 i;
5896
5897 for (i=0; i < 4; i++)
5898 linecap[i]=(*primitive_info);
5899 linecap[0].coordinates=4;
5900 linecap[1].point.x+=2.0*MagickEpsilon;
5901 linecap[2].point.x+=2.0*MagickEpsilon;
5902 linecap[2].point.y+=2.0*MagickEpsilon;
5903 linecap[3].point.y+=2.0*MagickEpsilon;
5904 linecap[4].primitive=UndefinedPrimitive;
5905 return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
5906}
5907
5908static MagickBooleanType DrawStrokePolygon(Image *image,
5909 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5910 ExceptionInfo *exception)
5911{
5912 DrawInfo
5913 *clone_info;
5914
5915 MagickBooleanType
5916 closed_path;
5917
5918 MagickStatusType
5919 status;
5920
5922 *stroke_polygon;
5923
5924 const PrimitiveInfo
5925 *p,
5926 *q;
5927
5928 /*
5929 Draw stroked polygon.
5930 */
5931 if (draw_info->debug != MagickFalse)
5932 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5933 " begin draw-stroke-polygon");
5934 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5935 clone_info->fill=draw_info->stroke;
5936 if (clone_info->fill_pattern != (Image *) NULL)
5937 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
5938 if (clone_info->stroke_pattern != (Image *) NULL)
5939 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
5940 MagickTrue,exception);
5941 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5942 clone_info->stroke_width=0.0;
5943 clone_info->fill_rule=NonZeroRule;
5944 status=MagickTrue;
5945 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=(ptrdiff_t) p->coordinates)
5946 {
5947 if (p->coordinates == 1)
5948 continue;
5949 stroke_polygon=TraceStrokePolygon(draw_info,p,exception);
5950 if (stroke_polygon == (PrimitiveInfo *) NULL)
5951 {
5952 status=0;
5953 break;
5954 }
5955 status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5956 stroke_polygon,exception);
5957 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
5958 if (status == 0)
5959 break;
5960 q=p+p->coordinates-1;
5961 closed_path=p->closed_subpath;
5962 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
5963 {
5964 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,p,
5965 exception);
5966 status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,q,
5967 exception);
5968 }
5969 }
5970 clone_info=DestroyDrawInfo(clone_info);
5971 if (draw_info->debug != MagickFalse)
5972 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5973 " end draw-stroke-polygon");
5974 return(status != 0 ? MagickTrue : MagickFalse);
5975}
5976
5977/*
5978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5979% %
5980% %
5981% %
5982% G e t A f f i n e M a t r i x %
5983% %
5984% %
5985% %
5986%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5987%
5988% GetAffineMatrix() returns an AffineMatrix initialized to the identity
5989% matrix.
5990%
5991% The format of the GetAffineMatrix method is:
5992%
5993% void GetAffineMatrix(AffineMatrix *affine_matrix)
5994%
5995% A description of each parameter follows:
5996%
5997% o affine_matrix: the affine matrix.
5998%
5999*/
6000MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
6001{
6002 if (IsEventLogging() != MagickFalse)
6003 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6004 assert(affine_matrix != (AffineMatrix *) NULL);
6005 (void) memset(affine_matrix,0,sizeof(*affine_matrix));
6006 affine_matrix->sx=1.0;
6007 affine_matrix->sy=1.0;
6008}
6009
6010/*
6011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6012% %
6013% %
6014% %
6015+ G e t D r a w I n f o %
6016% %
6017% %
6018% %
6019%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6020%
6021% GetDrawInfo() initializes draw_info to default values from image_info.
6022%
6023% The format of the GetDrawInfo method is:
6024%
6025% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6026%
6027% A description of each parameter follows:
6028%
6029% o image_info: the image info..
6030%
6031% o draw_info: the draw info.
6032%
6033*/
6034MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6035{
6036 char
6037 *next_token;
6038
6039 const char
6040 *option;
6041
6043 *exception;
6044
6045 /*
6046 Initialize draw attributes.
6047 */
6048 assert(draw_info != (DrawInfo *) NULL);
6049 if (IsEventLogging() != MagickFalse)
6050 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6051 (void) memset(draw_info,0,sizeof(*draw_info));
6052 draw_info->image_info=CloneImageInfo(image_info);
6053 GetAffineMatrix(&draw_info->affine);
6054 exception=AcquireExceptionInfo();
6055 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
6056 exception);
6057 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
6058 exception);
6059 draw_info->stroke_antialias=draw_info->image_info->antialias;
6060 draw_info->stroke_width=1.0;
6061 draw_info->fill_rule=EvenOddRule;
6062 draw_info->alpha=OpaqueAlpha;
6063 draw_info->fill_alpha=OpaqueAlpha;
6064 draw_info->stroke_alpha=OpaqueAlpha;
6065 draw_info->linecap=ButtCap;
6066 draw_info->linejoin=MiterJoin;
6067 draw_info->miterlimit=10;
6068 draw_info->decorate=NoDecoration;
6069 draw_info->pointsize=12.0;
6070 draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
6071 draw_info->compose=OverCompositeOp;
6072 draw_info->render=MagickTrue;
6073 draw_info->clip_path=MagickFalse;
6074 draw_info->debug=(GetLogEventMask() & (DrawEvent | AnnotateEvent)) != 0 ?
6075 MagickTrue : MagickFalse;
6076 if (draw_info->image_info->font != (char *) NULL)
6077 draw_info->font=AcquireString(draw_info->image_info->font);
6078 if (draw_info->image_info->density != (char *) NULL)
6079 draw_info->density=AcquireString(draw_info->image_info->density);
6080 draw_info->text_antialias=draw_info->image_info->antialias;
6081 if (fabs(draw_info->image_info->pointsize) >= MagickEpsilon)
6082 draw_info->pointsize=draw_info->image_info->pointsize;
6083 draw_info->border_color=draw_info->image_info->border_color;
6084 if (draw_info->image_info->server_name != (char *) NULL)
6085 draw_info->server_name=AcquireString(draw_info->image_info->server_name);
6086 option=GetImageOption(draw_info->image_info,"direction");
6087 if (option != (const char *) NULL)
6088 draw_info->direction=(DirectionType) ParseCommandOption(
6089 MagickDirectionOptions,MagickFalse,option);
6090 else
6091 draw_info->direction=UndefinedDirection;
6092 option=GetImageOption(draw_info->image_info,"encoding");
6093 if (option != (const char *) NULL)
6094 (void) CloneString(&draw_info->encoding,option);
6095 option=GetImageOption(draw_info->image_info,"family");
6096 if (option != (const char *) NULL)
6097 (void) CloneString(&draw_info->family,option);
6098 option=GetImageOption(draw_info->image_info,"fill");
6099 if (option != (const char *) NULL)
6100 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
6101 exception);
6102 option=GetImageOption(draw_info->image_info,"gravity");
6103 if (option != (const char *) NULL)
6104 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
6105 MagickFalse,option);
6106 option=GetImageOption(draw_info->image_info,"interline-spacing");
6107 if (option != (const char *) NULL)
6108 draw_info->interline_spacing=GetDrawValue(option,&next_token);
6109 option=GetImageOption(draw_info->image_info,"interword-spacing");
6110 if (option != (const char *) NULL)
6111 draw_info->interword_spacing=GetDrawValue(option,&next_token);
6112 option=GetImageOption(draw_info->image_info,"kerning");
6113 if (option != (const char *) NULL)
6114 draw_info->kerning=GetDrawValue(option,&next_token);
6115 option=GetImageOption(draw_info->image_info,"stroke");
6116 if (option != (const char *) NULL)
6117 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
6118 exception);
6119 option=GetImageOption(draw_info->image_info,"strokewidth");
6120 if (option != (const char *) NULL)
6121 draw_info->stroke_width=GetDrawValue(option,&next_token);
6122 option=GetImageOption(draw_info->image_info,"style");
6123 if (option != (const char *) NULL)
6124 draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
6125 MagickFalse,option);
6126 option=GetImageOption(draw_info->image_info,"undercolor");
6127 if (option != (const char *) NULL)
6128 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
6129 exception);
6130 option=GetImageOption(draw_info->image_info,"weight");
6131 if (option != (const char *) NULL)
6132 {
6133 ssize_t
6134 weight;
6135
6136 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
6137 if (weight == -1)
6138 weight=(ssize_t) StringToUnsignedLong(option);
6139 draw_info->weight=(size_t) weight;
6140 }
6141 option=GetImageOption(draw_info->image_info,"word-break");
6142 if (option != (const char *) NULL)
6143 draw_info->word_break=(WordBreakType) ParseCommandOption(
6144 MagickWordBreakOptions,MagickFalse,option);
6145 exception=DestroyExceptionInfo(exception);
6146 draw_info->signature=MagickCoreSignature;
6147}
6148
6149/*
6150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6151% %
6152% %
6153% %
6154+ P e r m u t a t e %
6155% %
6156% %
6157% %
6158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6159%
6160% Permutate() returns the permutation of the (n,k).
6161%
6162% The format of the Permutate method is:
6163%
6164% void Permutate(ssize_t n,ssize_t k)
6165%
6166% A description of each parameter follows:
6167%
6168% o n:
6169%
6170% o k:
6171%
6172%
6173*/
6174static inline double Permutate(const ssize_t n,const ssize_t k)
6175{
6176 double
6177 r;
6178
6179 ssize_t
6180 i;
6181
6182 r=1.0;
6183 for (i=k+1; i <= n; i++)
6184 r*=i;
6185 for (i=1; i <= (n-k); i++)
6186 r/=i;
6187 return(r);
6188}
6189
6190/*
6191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6192% %
6193% %
6194% %
6195+ T r a c e P r i m i t i v e %
6196% %
6197% %
6198% %
6199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6200%
6201% TracePrimitive is a collection of methods for generating graphic
6202% primitives such as arcs, ellipses, paths, etc.
6203%
6204*/
6205
6206static MagickBooleanType TraceArc(MVGInfo *mvg_info,const PointInfo start,
6207 const PointInfo end,const PointInfo degrees)
6208{
6209 PointInfo
6210 center,
6211 radius;
6212
6213 center.x=0.5*(end.x+start.x);
6214 center.y=0.5*(end.y+start.y);
6215 radius.x=fabs(center.x-start.x);
6216 radius.y=fabs(center.y-start.y);
6217 return(TraceEllipse(mvg_info,center,radius,degrees));
6218}
6219
6220static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,const PointInfo start,
6221 const PointInfo end,const PointInfo arc,const double angle,
6222 const MagickBooleanType large_arc,const MagickBooleanType sweep)
6223{
6224 double
6225 alpha,
6226 beta,
6227 delta,
6228 factor,
6229 gamma,
6230 theta;
6231
6232 MagickStatusType
6233 status;
6234
6235 PointInfo
6236 center,
6237 points[3],
6238 radii;
6239
6240 double
6241 cosine,
6242 sine;
6243
6245 *primitive_info;
6246
6248 *p;
6249
6250 ssize_t
6251 i;
6252
6253 size_t
6254 arc_segments;
6255
6256 ssize_t
6257 offset;
6258
6259 offset=mvg_info->offset;
6260 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6261 primitive_info->coordinates=0;
6262 if ((fabs(start.x-end.x) < MagickEpsilon) &&
6263 (fabs(start.y-end.y) < MagickEpsilon))
6264 return(TracePoint(primitive_info,end));
6265 radii.x=fabs(arc.x);
6266 radii.y=fabs(arc.y);
6267 if ((radii.x < MagickEpsilon) || (radii.y < MagickEpsilon))
6268 return(TraceLine(primitive_info,start,end));
6269 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
6270 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
6271 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
6272 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
6273 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
6274 (radii.y*radii.y);
6275 if (delta < MagickEpsilon)
6276 return(TraceLine(primitive_info,start,end));
6277 if (delta > 1.0)
6278 {
6279 radii.x*=sqrt((double) delta);
6280 radii.y*=sqrt((double) delta);
6281 }
6282 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
6283 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
6284 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
6285 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
6286 alpha=points[1].x-points[0].x;
6287 beta=points[1].y-points[0].y;
6288 if (fabs(alpha*alpha+beta*beta) < MagickEpsilon)
6289 return(TraceLine(primitive_info,start,end));
6290 factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25;
6291 if (factor <= 0.0)
6292 factor=0.0;
6293 else
6294 {
6295 factor=sqrt((double) factor);
6296 if (sweep == large_arc)
6297 factor=(-factor);
6298 }
6299 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
6300 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
6301 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
6302 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
6303 if ((theta < 0.0) && (sweep != MagickFalse))
6304 theta+=2.0*MagickPI;
6305 else
6306 if ((theta > 0.0) && (sweep == MagickFalse))
6307 theta-=2.0*MagickPI;
6308 arc_segments=(size_t) CastDoubleToLong(ceil(fabs((double) (theta/(0.5*
6309 MagickPI+MagickEpsilon)))));
6310 status=MagickTrue;
6311 p=primitive_info;
6312 for (i=0; i < (ssize_t) arc_segments; i++)
6313 {
6314 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
6315 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
6316 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
6317 sin(fmod((double) beta,DegreesToRadians(360.0)));
6318 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
6319 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
6320 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6321 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
6322 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
6323 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6324 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
6325 theta/arc_segments),DegreesToRadians(360.0))));
6326 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
6327 theta/arc_segments),DegreesToRadians(360.0))));
6328 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
6329 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6330 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
6331 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6332 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
6333 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
6334 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
6335 points[0].y);
6336 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
6337 points[0].y);
6338 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
6339 points[1].y);
6340 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
6341 points[1].y);
6342 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
6343 points[2].y);
6344 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
6345 points[2].y);
6346 if (i == (ssize_t) (arc_segments-1))
6347 (p+3)->point=end;
6348 status&=(MagickStatusType) TraceBezier(mvg_info,4);
6349 if (status == 0)
6350 break;
6351 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
6352 mvg_info->offset+=(ssize_t) p->coordinates;
6353 p+=(ptrdiff_t) p->coordinates;
6354 }
6355 if (status == 0)
6356 return(MagickFalse);
6357 mvg_info->offset=offset;
6358 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6359 primitive_info->coordinates=(size_t) (p-primitive_info);
6360 primitive_info->closed_subpath=MagickFalse;
6361 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6362 {
6363 p->primitive=primitive_info->primitive;
6364 p--;
6365 }
6366 return(MagickTrue);
6367}
6368
6369static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
6370 const size_t number_coordinates)
6371{
6372 double
6373 alpha,
6374 *coefficients,
6375 weight;
6376
6377 PointInfo
6378 end,
6379 point,
6380 *points;
6381
6383 *primitive_info;
6384
6386 *p;
6387
6388 ssize_t
6389 i,
6390 j;
6391
6392 size_t
6393 control_points,
6394 quantum;
6395
6396 /*
6397 Allocate coefficients.
6398 */
6399 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6400 quantum=number_coordinates;
6401 for (i=0; i < (ssize_t) number_coordinates; i++)
6402 {
6403 for (j=i+1; j < (ssize_t) number_coordinates; j++)
6404 {
6405 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
6406 if (alpha > (double) GetMaxMemoryRequest())
6407 {
6408 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6409 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6410 return(MagickFalse);
6411 }
6412 if (alpha > (double) quantum)
6413 quantum=(size_t) alpha;
6414 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
6415 if (alpha > (double) quantum)
6416 quantum=(size_t) alpha;
6417 }
6418 }
6419 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6420 quantum=MagickMin(quantum/number_coordinates,BezierQuantum);
6421 if (quantum > (double) GetMaxMemoryRequest())
6422 {
6423 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6424 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6425 return(MagickFalse);
6426 }
6427 coefficients=(double *) AcquireQuantumMemory(number_coordinates,
6428 sizeof(*coefficients));
6429 points=(PointInfo *) AcquireQuantumMemory(quantum,number_coordinates*
6430 sizeof(*points));
6431 if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
6432 {
6433 if (points != (PointInfo *) NULL)
6434 points=(PointInfo *) RelinquishMagickMemory(points);
6435 if (coefficients != (double *) NULL)
6436 coefficients=(double *) RelinquishMagickMemory(coefficients);
6437 (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6438 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6439 return(MagickFalse);
6440 }
6441 control_points=quantum*number_coordinates;
6442 if (CheckPrimitiveExtent(mvg_info,(double) control_points+1) == MagickFalse)
6443 {
6444 points=(PointInfo *) RelinquishMagickMemory(points);
6445 coefficients=(double *) RelinquishMagickMemory(coefficients);
6446 return(MagickFalse);
6447 }
6448 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6449 /*
6450 Compute bezier points.
6451 */
6452 end=primitive_info[number_coordinates-1].point;
6453 for (i=0; i < (ssize_t) number_coordinates; i++)
6454 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
6455 weight=0.0;
6456 for (i=0; i < (ssize_t) control_points; i++)
6457 {
6458 p=primitive_info;
6459 point.x=0.0;
6460 point.y=0.0;
6461 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
6462 for (j=0; j < (ssize_t) number_coordinates; j++)
6463 {
6464 point.x+=alpha*coefficients[j]*p->point.x;
6465 point.y+=alpha*coefficients[j]*p->point.y;
6466 alpha*=weight/(1.0-weight);
6467 p++;
6468 }
6469 points[i]=point;
6470 weight+=1.0/control_points;
6471 }
6472 /*
6473 Bezier curves are just short segmented polys.
6474 */
6475 p=primitive_info;
6476 for (i=0; i < (ssize_t) control_points; i++)
6477 {
6478 if (TracePoint(p,points[i]) == MagickFalse)
6479 {
6480 points=(PointInfo *) RelinquishMagickMemory(points);
6481 coefficients=(double *) RelinquishMagickMemory(coefficients);
6482 return(MagickFalse);
6483 }
6484 p+=(ptrdiff_t) p->coordinates;
6485 }
6486 if (TracePoint(p,end) == MagickFalse)
6487 {
6488 points=(PointInfo *) RelinquishMagickMemory(points);
6489 coefficients=(double *) RelinquishMagickMemory(coefficients);
6490 return(MagickFalse);
6491 }
6492 p+=(ptrdiff_t) p->coordinates;
6493 primitive_info->coordinates=(size_t) (p-primitive_info);
6494 primitive_info->closed_subpath=MagickFalse;
6495 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6496 {
6497 p->primitive=primitive_info->primitive;
6498 p--;
6499 }
6500 points=(PointInfo *) RelinquishMagickMemory(points);
6501 coefficients=(double *) RelinquishMagickMemory(coefficients);
6502 return(MagickTrue);
6503}
6504
6505static MagickBooleanType TraceCircle(MVGInfo *mvg_info,const PointInfo start,
6506 const PointInfo end)
6507{
6508 double
6509 alpha,
6510 beta,
6511 radius;
6512
6513 PointInfo
6514 offset,
6515 degrees;
6516
6517 alpha=end.x-start.x;
6518 beta=end.y-start.y;
6519 radius=hypot((double) alpha,(double) beta);
6520 offset.x=(double) radius;
6521 offset.y=(double) radius;
6522 degrees.x=0.0;
6523 degrees.y=360.0;
6524 return(TraceEllipse(mvg_info,start,offset,degrees));
6525}
6526
6527static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
6528 const PointInfo radii,const PointInfo arc)
6529{
6530 double
6531 coordinates,
6532 delta,
6533 step,
6534 x,
6535 y;
6536
6537 PointInfo
6538 angle,
6539 point;
6540
6542 *primitive_info;
6543
6545 *p;
6546
6547 ssize_t
6548 i;
6549
6550 /*
6551 Ellipses are just short segmented polys.
6552 */
6553 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6554 primitive_info->coordinates=0;
6555 if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
6556 return(MagickTrue);
6557 delta=PerceptibleReciprocal(MagickMax(radii.x,radii.y));
6558 step=MagickPI/(MagickPI*PerceptibleReciprocal(delta))/8.0;
6559 angle.x=DegreesToRadians(arc.x);
6560 y=arc.y;
6561 while (y < arc.x)
6562 y+=360.0;
6563 angle.y=DegreesToRadians(y);
6564 coordinates=ceil((angle.y-angle.x)/step+1.0);
6565 if (CheckPrimitiveExtent(mvg_info,coordinates+1) == MagickFalse)
6566 return(MagickFalse);
6567 i=0;
6568 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6569 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
6570 {
6571 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
6572 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
6573 if (i++ >= (ssize_t) coordinates)
6574 break;
6575 if (TracePoint(p,point) == MagickFalse)
6576 return(MagickFalse);
6577 p+=(ptrdiff_t) p->coordinates;
6578 }
6579 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
6580 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
6581 if (TracePoint(p,point) == MagickFalse)
6582 return(MagickFalse);
6583 p+=(ptrdiff_t) p->coordinates;
6584 primitive_info->coordinates=(size_t) (p-primitive_info);
6585 primitive_info->closed_subpath=MagickFalse;
6586 x=fabs(primitive_info[0].point.x-
6587 primitive_info[primitive_info->coordinates-1].point.x);
6588 y=fabs(primitive_info[0].point.y-
6589 primitive_info[primitive_info->coordinates-1].point.y);
6590 if ((x < MagickEpsilon) && (y < MagickEpsilon))
6591 primitive_info->closed_subpath=MagickTrue;
6592 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6593 {
6594 p->primitive=primitive_info->primitive;
6595 p--;
6596 }
6597 return(MagickTrue);
6598}
6599
6600static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
6601 const PointInfo start,const PointInfo end)
6602{
6603 if (TracePoint(primitive_info,start) == MagickFalse)
6604 return(MagickFalse);
6605 if (TracePoint(primitive_info+1,end) == MagickFalse)
6606 return(MagickFalse);
6607 (primitive_info+1)->primitive=primitive_info->primitive;
6608 primitive_info->coordinates=2;
6609 primitive_info->closed_subpath=MagickFalse;
6610 return(MagickTrue);
6611}
6612
6613static ssize_t TracePath(MVGInfo *mvg_info,const char *path,
6614 ExceptionInfo *exception)
6615{
6616 char
6617 *next_token,
6618 token[MagickPathExtent] = "";
6619
6620 const char
6621 *p;
6622
6623 double
6624 x,
6625 y;
6626
6627 int
6628 attribute,
6629 last_attribute;
6630
6631 MagickBooleanType
6632 status;
6633
6634 PointInfo
6635 end = {0.0, 0.0},
6636 points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
6637 point = {0.0, 0.0},
6638 start = {0.0, 0.0};
6639
6641 *primitive_info;
6642
6643 PrimitiveType
6644 primitive_type;
6645
6647 *q;
6648
6649 ssize_t
6650 i;
6651
6652 size_t
6653 number_coordinates,
6654 z_count;
6655
6656 ssize_t
6657 subpath_offset;
6658
6659 subpath_offset=mvg_info->offset;
6660 primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6661 status=MagickTrue;
6662 attribute=0;
6663 number_coordinates=0;
6664 z_count=0;
6665 primitive_type=primitive_info->primitive;
6666 q=primitive_info;
6667 for (p=path; *p != '\0'; )
6668 {
6669 if (status == MagickFalse)
6670 break;
6671 while (isspace((int) ((unsigned char) *p)) != 0)
6672 p++;
6673 if (*p == '\0')
6674 break;
6675 last_attribute=attribute;
6676 attribute=(int) (*p++);
6677 switch (attribute)
6678 {
6679 case 'a':
6680 case 'A':
6681 {
6682 double
6683 angle = 0.0;
6684
6685 MagickBooleanType
6686 large_arc = MagickFalse,
6687 sweep = MagickFalse;
6688
6689 PointInfo
6690 arc = {0.0, 0.0};
6691
6692 /*
6693 Elliptical arc.
6694 */
6695 do
6696 {
6697 (void) GetNextToken(p,&p,MagickPathExtent,token);
6698 if (*token == ',')
6699 (void) GetNextToken(p,&p,MagickPathExtent,token);
6700 arc.x=GetDrawValue(token,&next_token);
6701 if (token == next_token)
6702 ThrowPointExpectedException(token,exception);
6703 (void) GetNextToken(p,&p,MagickPathExtent,token);
6704 if (*token == ',')
6705 (void) GetNextToken(p,&p,MagickPathExtent,token);
6706 arc.y=GetDrawValue(token,&next_token);
6707 if (token == next_token)
6708 ThrowPointExpectedException(token,exception);
6709 (void) GetNextToken(p,&p,MagickPathExtent,token);
6710 if (*token == ',')
6711 (void) GetNextToken(p,&p,MagickPathExtent,token);
6712 angle=GetDrawValue(token,&next_token);
6713 if (token == next_token)
6714 ThrowPointExpectedException(token,exception);
6715 (void) GetNextToken(p,&p,MagickPathExtent,token);
6716 if (*token == ',')
6717 (void) GetNextToken(p,&p,MagickPathExtent,token);
6718 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6719 (void) GetNextToken(p,&p,MagickPathExtent,token);
6720 if (*token == ',')
6721 (void) GetNextToken(p,&p,MagickPathExtent,token);
6722 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6723 if (*token == ',')
6724 (void) GetNextToken(p,&p,MagickPathExtent,token);
6725 (void) GetNextToken(p,&p,MagickPathExtent,token);
6726 if (*token == ',')
6727 (void) GetNextToken(p,&p,MagickPathExtent,token);
6728 x=GetDrawValue(token,&next_token);
6729 if (token == next_token)
6730 ThrowPointExpectedException(token,exception);
6731 (void) GetNextToken(p,&p,MagickPathExtent,token);
6732 if (*token == ',')
6733 (void) GetNextToken(p,&p,MagickPathExtent,token);
6734 y=GetDrawValue(token,&next_token);
6735 if (token == next_token)
6736 ThrowPointExpectedException(token,exception);
6737 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
6738 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
6739 if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
6740 return(-1);
6741 q=(*mvg_info->primitive_info)+mvg_info->offset;
6742 mvg_info->offset+=(ssize_t) q->coordinates;
6743 q+=(ptrdiff_t) q->coordinates;
6744 point=end;
6745 while (isspace((int) ((unsigned char) *p)) != 0)
6746 p++;
6747 if (*p == ',')
6748 p++;
6749 } while (IsPoint(p) != MagickFalse);
6750 break;
6751 }
6752 case 'c':
6753 case 'C':
6754 {
6755 /*
6756 Cubic Bézier curve.
6757 */
6758 do
6759 {
6760 points[0]=point;
6761 for (i=1; i < 4; i++)
6762 {
6763 (void) GetNextToken(p,&p,MagickPathExtent,token);
6764 if (*token == ',')
6765 (void) GetNextToken(p,&p,MagickPathExtent,token);
6766 x=GetDrawValue(token,&next_token);
6767 if (token == next_token)
6768 ThrowPointExpectedException(token,exception);
6769 (void) GetNextToken(p,&p,MagickPathExtent,token);
6770 if (*token == ',')
6771 (void) GetNextToken(p,&p,MagickPathExtent,token);
6772 y=GetDrawValue(token,&next_token);
6773 if (token == next_token)
6774 ThrowPointExpectedException(token,exception);
6775 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
6776 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
6777 points[i]=end;
6778 }
6779 for (i=0; i < 4; i++)
6780 (q+i)->point=points[i];
6781 if (TraceBezier(mvg_info,4) == MagickFalse)
6782 return(-1);
6783 q=(*mvg_info->primitive_info)+mvg_info->offset;
6784 mvg_info->offset+=(ssize_t) q->coordinates;
6785 q+=(ptrdiff_t) q->coordinates;
6786 point=end;
6787 while (isspace((int) ((unsigned char) *p)) != 0)
6788 p++;
6789 if (*p == ',')
6790 p++;
6791 } while (IsPoint(p) != MagickFalse);
6792 break;
6793 }
6794 case 'H':
6795 case 'h':
6796 {
6797 do
6798 {
6799 (void) GetNextToken(p,&p,MagickPathExtent,token);
6800 if (*token == ',')
6801 (void) GetNextToken(p,&p,MagickPathExtent,token);
6802 x=GetDrawValue(token,&next_token);
6803 if (token == next_token)
6804 ThrowPointExpectedException(token,exception);
6805 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
6806 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6807 return(-1);
6808 q=(*mvg_info->primitive_info)+mvg_info->offset;
6809 if (TracePoint(q,point) == MagickFalse)
6810 return(-1);
6811 mvg_info->offset+=(ssize_t) q->coordinates;
6812 q+=(ptrdiff_t) q->coordinates;
6813 while (isspace((int) ((unsigned char) *p)) != 0)
6814 p++;
6815 if (*p == ',')
6816 p++;
6817 } while (IsPoint(p) != MagickFalse);
6818 break;
6819 }
6820 case 'l':
6821 case 'L':
6822 {
6823 /*
6824 Line to.
6825 */
6826 do
6827 {
6828 (void) GetNextToken(p,&p,MagickPathExtent,token);
6829 if (*token == ',')
6830 (void) GetNextToken(p,&p,MagickPathExtent,token);
6831 x=GetDrawValue(token,&next_token);
6832 if (token == next_token)
6833 ThrowPointExpectedException(token,exception);
6834 (void) GetNextToken(p,&p,MagickPathExtent,token);
6835 if (*token == ',')
6836 (void) GetNextToken(p,&p,MagickPathExtent,token);
6837 y=GetDrawValue(token,&next_token);
6838 if (token == next_token)
6839 ThrowPointExpectedException(token,exception);
6840 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
6841 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
6842 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6843 return(-1);
6844 q=(*mvg_info->primitive_info)+mvg_info->offset;
6845 if (TracePoint(q,point) == MagickFalse)
6846 return(-1);
6847 mvg_info->offset+=(ssize_t) q->coordinates;
6848 q+=(ptrdiff_t) q->coordinates;
6849 while (isspace((int) ((unsigned char) *p)) != 0)
6850 p++;
6851 if (*p == ',')
6852 p++;
6853 } while (IsPoint(p) != MagickFalse);
6854 break;
6855 }
6856 case 'M':
6857 case 'm':
6858 {
6859 /*
6860 Move to.
6861 */
6862 if (mvg_info->offset != subpath_offset)
6863 {
6864 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6865 primitive_info->coordinates=(size_t) (q-primitive_info);
6866 number_coordinates+=primitive_info->coordinates;
6867 primitive_info=q;
6868 subpath_offset=mvg_info->offset;
6869 }
6870 i=0;
6871 do
6872 {
6873 (void) GetNextToken(p,&p,MagickPathExtent,token);
6874 if (*token == ',')
6875 (void) GetNextToken(p,&p,MagickPathExtent,token);
6876 x=GetDrawValue(token,&next_token);
6877 if (token == next_token)
6878 ThrowPointExpectedException(token,exception);
6879 (void) GetNextToken(p,&p,MagickPathExtent,token);
6880 if (*token == ',')
6881 (void) GetNextToken(p,&p,MagickPathExtent,token);
6882 y=GetDrawValue(token,&next_token);
6883 if (token == next_token)
6884 ThrowPointExpectedException(token,exception);
6885 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
6886 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
6887 if (i == 0)
6888 start=point;
6889 i++;
6890 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6891 return(-1);
6892 q=(*mvg_info->primitive_info)+mvg_info->offset;
6893 if (TracePoint(q,point) == MagickFalse)
6894 return(-1);
6895 mvg_info->offset+=(ssize_t) q->coordinates;
6896 q+=(ptrdiff_t) q->coordinates;
6897 while (isspace((int) ((unsigned char) *p)) != 0)
6898 p++;
6899 if (*p == ',')
6900 p++;
6901 } while (IsPoint(p) != MagickFalse);
6902 break;
6903 }
6904 case 'q':
6905 case 'Q':
6906 {
6907 /*
6908 Quadratic Bézier curve.
6909 */
6910 do
6911 {
6912 points[0]=point;
6913 for (i=1; i < 3; i++)
6914 {
6915 (void) GetNextToken(p,&p,MagickPathExtent,token);
6916 if (*token == ',')
6917 (void) GetNextToken(p,&p,MagickPathExtent,token);
6918 x=GetDrawValue(token,&next_token);
6919 if (token == next_token)
6920 ThrowPointExpectedException(token,exception);
6921 (void) GetNextToken(p,&p,MagickPathExtent,token);
6922 if (*token == ',')
6923 (void) GetNextToken(p,&p,MagickPathExtent,token);
6924 y=GetDrawValue(token,&next_token);
6925 if (token == next_token)
6926 ThrowPointExpectedException(token,exception);
6927 if (*p == ',')
6928 p++;
6929 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
6930 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
6931 points[i]=end;
6932 }
6933 for (i=0; i < 3; i++)
6934 (q+i)->point=points[i];
6935 if (TraceBezier(mvg_info,3) == MagickFalse)
6936 return(-1);
6937 q=(*mvg_info->primitive_info)+mvg_info->offset;
6938 mvg_info->offset+=(ssize_t) q->coordinates;
6939 q+=(ptrdiff_t) q->coordinates;
6940 point=end;
6941 while (isspace((int) ((unsigned char) *p)) != 0)
6942 p++;
6943 if (*p == ',')
6944 p++;
6945 } while (IsPoint(p) != MagickFalse);
6946 break;
6947 }
6948 case 's':
6949 case 'S':
6950 {
6951 /*
6952 Cubic Bézier curve.
6953 */
6954 do
6955 {
6956 points[0]=points[3];
6957 points[1].x=2.0*points[3].x-points[2].x;
6958 points[1].y=2.0*points[3].y-points[2].y;
6959 for (i=2; i < 4; i++)
6960 {
6961 (void) GetNextToken(p,&p,MagickPathExtent,token);
6962 if (*token == ',')
6963 (void) GetNextToken(p,&p,MagickPathExtent,token);
6964 x=GetDrawValue(token,&next_token);
6965 if (token == next_token)
6966 ThrowPointExpectedException(token,exception);
6967 (void) GetNextToken(p,&p,MagickPathExtent,token);
6968 if (*token == ',')
6969 (void) GetNextToken(p,&p,MagickPathExtent,token);
6970 y=GetDrawValue(token,&next_token);
6971 if (token == next_token)
6972 ThrowPointExpectedException(token,exception);
6973 if (*p == ',')
6974 p++;
6975 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
6976 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
6977 points[i]=end;
6978 }
6979 if (strchr("CcSs",last_attribute) == (char *) NULL)
6980 {
6981 points[0]=point;
6982 points[1]=point;
6983 }
6984 for (i=0; i < 4; i++)
6985 (q+i)->point=points[i];
6986 if (TraceBezier(mvg_info,4) == MagickFalse)
6987 return(-1);
6988 q=(*mvg_info->primitive_info)+mvg_info->offset;
6989 mvg_info->offset+=(ssize_t) q->coordinates;
6990 q+=(ptrdiff_t) q->coordinates;
6991 point=end;
6992 last_attribute=attribute;
6993 while (isspace((int) ((unsigned char) *p)) != 0)
6994 p++;
6995 if (*p == ',')
6996 p++;
6997 } while (IsPoint(p) != MagickFalse);
6998 break;
6999 }
7000 case 't':
7001 case 'T':
7002 {
7003 /*
7004 Quadratic Bézier curve.
7005 */
7006 do
7007 {
7008 points[0]=points[2];
7009 points[1].x=2.0*points[2].x-points[1].x;
7010 points[1].y=2.0*points[2].y-points[1].y;
7011 for (i=2; i < 3; i++)
7012 {
7013 (void) GetNextToken(p,&p,MagickPathExtent,token);
7014 if (*token == ',')
7015 (void) GetNextToken(p,&p,MagickPathExtent,token);
7016 x=GetDrawValue(token,&next_token);
7017 if (token == next_token)
7018 ThrowPointExpectedException(token,exception);
7019 (void) GetNextToken(p,&p,MagickPathExtent,token);
7020 if (*token == ',')
7021 (void) GetNextToken(p,&p,MagickPathExtent,token);
7022 y=GetDrawValue(token,&next_token);
7023 if (token == next_token)
7024 ThrowPointExpectedException(token,exception);
7025 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
7026 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
7027 points[i]=end;
7028 }
7029 if (status == MagickFalse)
7030 break;
7031 if (strchr("QqTt",last_attribute) == (char *) NULL)
7032 {
7033 points[0]=point;
7034 points[1]=point;
7035 }
7036 for (i=0; i < 3; i++)
7037 (q+i)->point=points[i];
7038 if (TraceBezier(mvg_info,3) == MagickFalse)
7039 return(-1);
7040 q=(*mvg_info->primitive_info)+mvg_info->offset;
7041 mvg_info->offset+=(ssize_t) q->coordinates;
7042 q+=(ptrdiff_t) q->coordinates;
7043 point=end;
7044 last_attribute=attribute;
7045 while (isspace((int) ((unsigned char) *p)) != 0)
7046 p++;
7047 if (*p == ',')
7048 p++;
7049 } while (IsPoint(p) != MagickFalse);
7050 break;
7051 }
7052 case 'v':
7053 case 'V':
7054 {
7055 /*
7056 Line to.
7057 */
7058 do
7059 {
7060 (void) GetNextToken(p,&p,MagickPathExtent,token);
7061 if (*token == ',')
7062 (void) GetNextToken(p,&p,MagickPathExtent,token);
7063 y=GetDrawValue(token,&next_token);
7064 if (token == next_token)
7065 ThrowPointExpectedException(token,exception);
7066 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
7067 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7068 return(-1);
7069 q=(*mvg_info->primitive_info)+mvg_info->offset;
7070 if (TracePoint(q,point) == MagickFalse)
7071 return(-1);
7072 mvg_info->offset+=(ssize_t) q->coordinates;
7073 q+=(ptrdiff_t) q->coordinates;
7074 while (isspace((int) ((unsigned char) *p)) != 0)
7075 p++;
7076 if (*p == ',')
7077 p++;
7078 } while (IsPoint(p) != MagickFalse);
7079 break;
7080 }
7081 case 'z':
7082 case 'Z':
7083 {
7084 /*
7085 Close path.
7086 */
7087 point=start;
7088 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7089 return(-1);
7090 q=(*mvg_info->primitive_info)+mvg_info->offset;
7091 if (TracePoint(q,point) == MagickFalse)
7092 return(-1);
7093 mvg_info->offset+=(ssize_t) q->coordinates;
7094 q+=(ptrdiff_t) q->coordinates;
7095 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7096 primitive_info->coordinates=(size_t) (q-primitive_info);
7097 primitive_info->closed_subpath=MagickTrue;
7098 number_coordinates+=primitive_info->coordinates;
7099 primitive_info=q;
7100 subpath_offset=mvg_info->offset;
7101 z_count++;
7102 break;
7103 }
7104 default:
7105 {
7106 ThrowPointExpectedException(token,exception);
7107 break;
7108 }
7109 }
7110 }
7111 if (status == MagickFalse)
7112 return(-1);
7113 primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7114 primitive_info->coordinates=(size_t) (q-primitive_info);
7115 number_coordinates+=primitive_info->coordinates;
7116 for (i=0; i < (ssize_t) number_coordinates; i++)
7117 {
7118 q--;
7119 q->primitive=primitive_type;
7120 if (z_count > 1)
7121 q->method=FillToBorderMethod;
7122 }
7123 q=primitive_info;
7124 return((ssize_t) number_coordinates);
7125}
7126
7127static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
7128 const PointInfo start,const PointInfo end)
7129{
7130 PointInfo
7131 point;
7132
7134 *p;
7135
7136 ssize_t
7137 i;
7138
7139 p=primitive_info;
7140 if (TracePoint(p,start) == MagickFalse)
7141 return(MagickFalse);
7142 p+=(ptrdiff_t) p->coordinates;
7143 point.x=start.x;
7144 point.y=end.y;
7145 if (TracePoint(p,point) == MagickFalse)
7146 return(MagickFalse);
7147 p+=(ptrdiff_t) p->coordinates;
7148 if (TracePoint(p,end) == MagickFalse)
7149 return(MagickFalse);
7150 p+=(ptrdiff_t) p->coordinates;
7151 point.x=end.x;
7152 point.y=start.y;
7153 if (TracePoint(p,point) == MagickFalse)
7154 return(MagickFalse);
7155 p+=(ptrdiff_t) p->coordinates;
7156 if (TracePoint(p,start) == MagickFalse)
7157 return(MagickFalse);
7158 p+=(ptrdiff_t) p->coordinates;
7159 primitive_info->coordinates=(size_t) (p-primitive_info);
7160 primitive_info->closed_subpath=MagickTrue;
7161 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7162 {
7163 p->primitive=primitive_info->primitive;
7164 p--;
7165 }
7166 return(MagickTrue);
7167}
7168
7169static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
7170 const PointInfo start,const PointInfo end,PointInfo arc)
7171{
7172 PointInfo
7173 degrees,
7174 point,
7175 segment;
7176
7178 *primitive_info;
7179
7181 *p;
7182
7183 ssize_t
7184 i;
7185
7186 ssize_t
7187 offset;
7188
7189 offset=mvg_info->offset;
7190 segment.x=fabs(end.x-start.x);
7191 segment.y=fabs(end.y-start.y);
7192 if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
7193 {
7194 (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
7195 return(MagickTrue);
7196 }
7197 if (arc.x > (0.5*segment.x))
7198 arc.x=0.5*segment.x;
7199 if (arc.y > (0.5*segment.y))
7200 arc.y=0.5*segment.y;
7201 point.x=start.x+segment.x-arc.x;
7202 point.y=start.y+arc.y;
7203 degrees.x=270.0;
7204 degrees.y=360.0;
7205 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7206 return(MagickFalse);
7207 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7208 mvg_info->offset+=(ssize_t) p->coordinates;
7209 point.x=start.x+segment.x-arc.x;
7210 point.y=start.y+segment.y-arc.y;
7211 degrees.x=0.0;
7212 degrees.y=90.0;
7213 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7214 return(MagickFalse);
7215 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7216 mvg_info->offset+=(ssize_t) p->coordinates;
7217 point.x=start.x+arc.x;
7218 point.y=start.y+segment.y-arc.y;
7219 degrees.x=90.0;
7220 degrees.y=180.0;
7221 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7222 return(MagickFalse);
7223 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7224 mvg_info->offset+=(ssize_t) p->coordinates;
7225 point.x=start.x+arc.x;
7226 point.y=start.y+arc.y;
7227 degrees.x=180.0;
7228 degrees.y=270.0;
7229 if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7230 return(MagickFalse);
7231 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7232 mvg_info->offset+=(ssize_t) p->coordinates;
7233 if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7234 return(MagickFalse);
7235 p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7236 if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
7237 return(MagickFalse);
7238 p+=(ptrdiff_t) p->coordinates;
7239 mvg_info->offset=offset;
7240 primitive_info=(*mvg_info->primitive_info)+offset;
7241 primitive_info->coordinates=(size_t) (p-primitive_info);
7242 primitive_info->closed_subpath=MagickTrue;
7243 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7244 {
7245 p->primitive=primitive_info->primitive;
7246 p--;
7247 }
7248 return(MagickTrue);
7249}
7250
7251static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
7252 const size_t number_vertices,const double offset)
7253{
7254 double
7255 distance;
7256
7257 double
7258 dx,
7259 dy;
7260
7261 ssize_t
7262 i;
7263
7264 ssize_t
7265 j;
7266
7267 dx=0.0;
7268 dy=0.0;
7269 for (i=1; i < (ssize_t) number_vertices; i++)
7270 {
7271 dx=primitive_info[0].point.x-primitive_info[i].point.x;
7272 dy=primitive_info[0].point.y-primitive_info[i].point.y;
7273 if ((fabs((double) dx) >= MagickEpsilon) ||
7274 (fabs((double) dy) >= MagickEpsilon))
7275 break;
7276 }
7277 if (i == (ssize_t) number_vertices)
7278 i=(ssize_t) number_vertices-1L;
7279 distance=hypot((double) dx,(double) dy);
7280 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
7281 dx*(distance+offset)/distance);
7282 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
7283 dy*(distance+offset)/distance);
7284 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
7285 {
7286 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
7287 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
7288 if ((fabs((double) dx) >= MagickEpsilon) ||
7289 (fabs((double) dy) >= MagickEpsilon))
7290 break;
7291 }
7292 distance=hypot((double) dx,(double) dy);
7293 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
7294 dx*(distance+offset)/distance);
7295 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
7296 dy*(distance+offset)/distance);
7297 return(MagickTrue);
7298}
7299
7300static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
7301 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
7302{
7303#define MaxStrokePad (6*BezierQuantum+360)
7304#define CheckPathExtent(pad_p,pad_q) \
7305{ \
7306 if ((pad_p) > MaxBezierCoordinates) \
7307 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7308 else \
7309 if ((p+(ptrdiff_t) (pad_p)) >= (ssize_t) extent_p) \
7310 { \
7311 if (~extent_p < (pad_p)) \
7312 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7313 else \
7314 { \
7315 extent_p+=(pad_p); \
7316 stroke_p=(PointInfo *) ResizeQuantumMemory(stroke_p,extent_p+ \
7317 MaxStrokePad,sizeof(*stroke_p)); \
7318 } \
7319 } \
7320 if ((pad_q) > MaxBezierCoordinates) \
7321 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7322 else \
7323 if ((q+(ptrdiff_t) (pad_q)) >= (ssize_t) extent_q) \
7324 { \
7325 if (~extent_q < (pad_q)) \
7326 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7327 else \
7328 { \
7329 extent_q+=(pad_q); \
7330 stroke_q=(PointInfo *) ResizeQuantumMemory(stroke_q,extent_q+ \
7331 MaxStrokePad,sizeof(*stroke_q)); \
7332 } \
7333 } \
7334 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL)) \
7335 { \
7336 if (stroke_p != (PointInfo *) NULL) \
7337 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7338 if (stroke_q != (PointInfo *) NULL) \
7339 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7340 polygon_primitive=(PrimitiveInfo *) \
7341 RelinquishMagickMemory(polygon_primitive); \
7342 (void) ThrowMagickException(exception,GetMagickModule(), \
7343 ResourceLimitError,"MemoryAllocationFailed","`%s'",""); \
7344 return((PrimitiveInfo *) NULL); \
7345 } \
7346}
7347
7348 typedef struct _StrokeSegment
7349 {
7350 double
7351 p,
7352 q;
7353 } StrokeSegment;
7354
7355 double
7356 delta_theta,
7357 dot_product,
7358 mid,
7359 miterlimit;
7360
7361 MagickBooleanType
7362 closed_path;
7363
7364 PointInfo
7365 box_p[5],
7366 box_q[5],
7367 center,
7368 offset,
7369 *stroke_p,
7370 *stroke_q;
7371
7373 *polygon_primitive,
7374 *stroke_polygon;
7375
7376 ssize_t
7377 i;
7378
7379 size_t
7380 arc_segments,
7381 extent_p,
7382 extent_q,
7383 number_vertices;
7384
7385 ssize_t
7386 j,
7387 n,
7388 p,
7389 q;
7390
7391 StrokeSegment
7392 dx = {0.0, 0.0},
7393 dy = {0.0, 0.0},
7394 inverse_slope = {0.0, 0.0},
7395 slope = {0.0, 0.0},
7396 theta = {0.0, 0.0};
7397
7398 /*
7399 Allocate paths.
7400 */
7401 number_vertices=primitive_info->coordinates;
7402 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7403 number_vertices+2UL,sizeof(*polygon_primitive));
7404 if (polygon_primitive == (PrimitiveInfo *) NULL)
7405 {
7406 (void) ThrowMagickException(exception,GetMagickModule(),
7407 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7408 return((PrimitiveInfo *) NULL);
7409 }
7410 (void) memcpy(polygon_primitive,primitive_info,(size_t) number_vertices*
7411 sizeof(*polygon_primitive));
7412 offset.x=primitive_info[number_vertices-1].point.x-primitive_info[0].point.x;
7413 offset.y=primitive_info[number_vertices-1].point.y-primitive_info[0].point.y;
7414 closed_path=(fabs(offset.x) < MagickEpsilon) &&
7415 (fabs(offset.y) < MagickEpsilon) ? MagickTrue : MagickFalse;
7416 if (((draw_info->linejoin == RoundJoin) ||
7417 (draw_info->linejoin == MiterJoin)) && (closed_path != MagickFalse))
7418 {
7419 polygon_primitive[number_vertices]=primitive_info[1];
7420 number_vertices++;
7421 }
7422 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
7423 /*
7424 Compute the slope for the first line segment, p.
7425 */
7426 dx.p=0.0;
7427 dy.p=0.0;
7428 for (n=1; n < (ssize_t) number_vertices; n++)
7429 {
7430 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
7431 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
7432 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
7433 break;
7434 }
7435 if (n == (ssize_t) number_vertices)
7436 {
7437 if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
7438 {
7439 /*
7440 Zero length subpath.
7441 */
7442 stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
7443 sizeof(*stroke_polygon));
7444 stroke_polygon[0]=polygon_primitive[0];
7445 stroke_polygon[0].coordinates=0;
7446 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7447 polygon_primitive);
7448 return(stroke_polygon);
7449 }
7450 n=(ssize_t) number_vertices-1L;
7451 }
7452 extent_p=2*number_vertices;
7453 extent_q=2*number_vertices;
7454 stroke_p=(PointInfo *) AcquireQuantumMemory((size_t) extent_p+MaxStrokePad,
7455 sizeof(*stroke_p));
7456 stroke_q=(PointInfo *) AcquireQuantumMemory((size_t) extent_q+MaxStrokePad,
7457 sizeof(*stroke_q));
7458 if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL))
7459 {
7460 if (stroke_p != (PointInfo *) NULL)
7461 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7462 if (stroke_q != (PointInfo *) NULL)
7463 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7464 polygon_primitive=(PrimitiveInfo *)
7465 RelinquishMagickMemory(polygon_primitive);
7466 (void) ThrowMagickException(exception,GetMagickModule(),
7467 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7468 return((PrimitiveInfo *) NULL);
7469 }
7470 slope.p=0.0;
7471 inverse_slope.p=0.0;
7472 if (fabs(dx.p) < MagickEpsilon)
7473 {
7474 if (dx.p >= 0.0)
7475 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7476 else
7477 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7478 }
7479 else
7480 if (fabs(dy.p) < MagickEpsilon)
7481 {
7482 if (dy.p >= 0.0)
7483 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7484 else
7485 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7486 }
7487 else
7488 {
7489 slope.p=dy.p/dx.p;
7490 inverse_slope.p=(-1.0*PerceptibleReciprocal(slope.p));
7491 }
7492 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
7493 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
7494 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
7495 (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
7496 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
7497 offset.y=(double) (offset.x*inverse_slope.p);
7498 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
7499 {
7500 box_p[0].x=polygon_primitive[0].point.x-offset.x;
7501 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
7502 box_p[1].x=polygon_primitive[n].point.x-offset.x;
7503 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
7504 box_q[0].x=polygon_primitive[0].point.x+offset.x;
7505 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
7506 box_q[1].x=polygon_primitive[n].point.x+offset.x;
7507 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
7508 }
7509 else
7510 {
7511 box_p[0].x=polygon_primitive[0].point.x+offset.x;
7512 box_p[0].y=polygon_primitive[0].point.y+offset.y;
7513 box_p[1].x=polygon_primitive[n].point.x+offset.x;
7514 box_p[1].y=polygon_primitive[n].point.y+offset.y;
7515 box_q[0].x=polygon_primitive[0].point.x-offset.x;
7516 box_q[0].y=polygon_primitive[0].point.y-offset.y;
7517 box_q[1].x=polygon_primitive[n].point.x-offset.x;
7518 box_q[1].y=polygon_primitive[n].point.y-offset.y;
7519 }
7520 /*
7521 Create strokes for the line join attribute: bevel, miter, round.
7522 */
7523 p=0;
7524 q=0;
7525 stroke_q[p++]=box_q[0];
7526 stroke_p[q++]=box_p[0];
7527 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
7528 {
7529 /*
7530 Compute the slope for this line segment, q.
7531 */
7532 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
7533 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
7534 dot_product=dx.q*dx.q+dy.q*dy.q;
7535 if (dot_product < 0.25)
7536 continue;
7537 slope.q=0.0;
7538 inverse_slope.q=0.0;
7539 if (fabs(dx.q) < MagickEpsilon)
7540 {
7541 if (dx.q >= 0.0)
7542 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7543 else
7544 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7545 }
7546 else
7547 if (fabs(dy.q) < MagickEpsilon)
7548 {
7549 if (dy.q >= 0.0)
7550 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7551 else
7552 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7553 }
7554 else
7555 {
7556 slope.q=dy.q/dx.q;
7557 inverse_slope.q=(-1.0*PerceptibleReciprocal(slope.q));
7558 }
7559 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
7560 offset.y=(double) (offset.x*inverse_slope.q);
7561 dot_product=dy.q*offset.x-dx.q*offset.y;
7562 if (dot_product > 0.0)
7563 {
7564 box_p[2].x=polygon_primitive[n].point.x-offset.x;
7565 box_p[2].y=polygon_primitive[n].point.y-offset.y;
7566 box_p[3].x=polygon_primitive[i].point.x-offset.x;
7567 box_p[3].y=polygon_primitive[i].point.y-offset.y;
7568 box_q[2].x=polygon_primitive[n].point.x+offset.x;
7569 box_q[2].y=polygon_primitive[n].point.y+offset.y;
7570 box_q[3].x=polygon_primitive[i].point.x+offset.x;
7571 box_q[3].y=polygon_primitive[i].point.y+offset.y;
7572 }
7573 else
7574 {
7575 box_p[2].x=polygon_primitive[n].point.x+offset.x;
7576 box_p[2].y=polygon_primitive[n].point.y+offset.y;
7577 box_p[3].x=polygon_primitive[i].point.x+offset.x;
7578 box_p[3].y=polygon_primitive[i].point.y+offset.y;
7579 box_q[2].x=polygon_primitive[n].point.x-offset.x;
7580 box_q[2].y=polygon_primitive[n].point.y-offset.y;
7581 box_q[3].x=polygon_primitive[i].point.x-offset.x;
7582 box_q[3].y=polygon_primitive[i].point.y-offset.y;
7583 }
7584 if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
7585 {
7586 box_p[4]=box_p[1];
7587 box_q[4]=box_q[1];
7588 }
7589 else
7590 {
7591 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
7592 box_p[3].y)/(slope.p-slope.q));
7593 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
7594 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
7595 box_q[3].y)/(slope.p-slope.q));
7596 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
7597 }
7598 DisableMSCWarning(4127)
7599 CheckPathExtent(MaxStrokePad,MaxStrokePad);
7600 RestoreMSCWarning
7601 dot_product=dx.q*dy.p-dx.p*dy.q;
7602 if (dot_product <= 0.0)
7603 switch (draw_info->linejoin)
7604 {
7605 case BevelJoin:
7606 {
7607 stroke_q[q++]=box_q[1];
7608 stroke_q[q++]=box_q[2];
7609 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7610 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7611 if (dot_product <= miterlimit)
7612 stroke_p[p++]=box_p[4];
7613 else
7614 {
7615 stroke_p[p++]=box_p[1];
7616 stroke_p[p++]=box_p[2];
7617 }
7618 break;
7619 }
7620 case MiterJoin:
7621 {
7622 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7623 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7624 if (dot_product <= miterlimit)
7625 {
7626 stroke_q[q++]=box_q[4];
7627 stroke_p[p++]=box_p[4];
7628 }
7629 else
7630 {
7631 stroke_q[q++]=box_q[1];
7632 stroke_q[q++]=box_q[2];
7633 stroke_p[p++]=box_p[1];
7634 stroke_p[p++]=box_p[2];
7635 }
7636 break;
7637 }
7638 case RoundJoin:
7639 {
7640 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7641 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7642 if (dot_product <= miterlimit)
7643 stroke_p[p++]=box_p[4];
7644 else
7645 {
7646 stroke_p[p++]=box_p[1];
7647 stroke_p[p++]=box_p[2];
7648 }
7649 center=polygon_primitive[n].point;
7650 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
7651 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
7652 if (theta.q < theta.p)
7653 theta.q+=2.0*MagickPI;
7654 arc_segments=(size_t) CastDoubleToLong(ceil((double) ((theta.q-
7655 theta.p)/(2.0*sqrt(PerceptibleReciprocal(mid))))));
7656 DisableMSCWarning(4127)
7657 CheckPathExtent(MaxStrokePad,arc_segments+MaxStrokePad);
7658 RestoreMSCWarning
7659 stroke_q[q].x=box_q[1].x;
7660 stroke_q[q].y=box_q[1].y;
7661 q++;
7662 for (j=1; j < (ssize_t) arc_segments; j++)
7663 {
7664 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7665 stroke_q[q].x=(double) (center.x+mid*cos(fmod((double)
7666 (theta.p+delta_theta),DegreesToRadians(360.0))));
7667 stroke_q[q].y=(double) (center.y+mid*sin(fmod((double)
7668 (theta.p+delta_theta),DegreesToRadians(360.0))));
7669 q++;
7670 }
7671 stroke_q[q++]=box_q[2];
7672 break;
7673 }
7674 default:
7675 break;
7676 }
7677 else
7678 switch (draw_info->linejoin)
7679 {
7680 case BevelJoin:
7681 {
7682 stroke_p[p++]=box_p[1];
7683 stroke_p[p++]=box_p[2];
7684 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7685 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7686 if (dot_product <= miterlimit)
7687 stroke_q[q++]=box_q[4];
7688 else
7689 {
7690 stroke_q[q++]=box_q[1];
7691 stroke_q[q++]=box_q[2];
7692 }
7693 break;
7694 }
7695 case MiterJoin:
7696 {
7697 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7698 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7699 if (dot_product <= miterlimit)
7700 {
7701 stroke_q[q++]=box_q[4];
7702 stroke_p[p++]=box_p[4];
7703 }
7704 else
7705 {
7706 stroke_q[q++]=box_q[1];
7707 stroke_q[q++]=box_q[2];
7708 stroke_p[p++]=box_p[1];
7709 stroke_p[p++]=box_p[2];
7710 }
7711 break;
7712 }
7713 case RoundJoin:
7714 {
7715 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7716 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7717 if (dot_product <= miterlimit)
7718 stroke_q[q++]=box_q[4];
7719 else
7720 {
7721 stroke_q[q++]=box_q[1];
7722 stroke_q[q++]=box_q[2];
7723 }
7724 center=polygon_primitive[n].point;
7725 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
7726 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
7727 if (theta.p < theta.q)
7728 theta.p+=2.0*MagickPI;
7729 arc_segments=(size_t) CastDoubleToLong(ceil((double) ((theta.p-
7730 theta.q)/(2.0*sqrt((double) (PerceptibleReciprocal(mid)))))));
7731 DisableMSCWarning(4127)
7732 CheckPathExtent(arc_segments+MaxStrokePad,MaxStrokePad);
7733 RestoreMSCWarning
7734 stroke_p[p++]=box_p[1];
7735 for (j=1; j < (ssize_t) arc_segments; j++)
7736 {
7737 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7738 stroke_p[p].x=(double) (center.x+mid*cos(fmod((double)
7739 (theta.p+delta_theta),DegreesToRadians(360.0))));
7740 stroke_p[p].y=(double) (center.y+mid*sin(fmod((double)
7741 (theta.p+delta_theta),DegreesToRadians(360.0))));
7742 p++;
7743 }
7744 stroke_p[p++]=box_p[2];
7745 break;
7746 }
7747 default:
7748 break;
7749 }
7750 slope.p=slope.q;
7751 inverse_slope.p=inverse_slope.q;
7752 box_p[0]=box_p[2];
7753 box_p[1]=box_p[3];
7754 box_q[0]=box_q[2];
7755 box_q[1]=box_q[3];
7756 dx.p=dx.q;
7757 dy.p=dy.q;
7758 n=i;
7759 }
7760 stroke_p[p++]=box_p[1];
7761 stroke_q[q++]=box_q[1];
7762 /*
7763 Trace stroked polygon.
7764 */
7765 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7766 (p+q+2L*closed_path+2L),sizeof(*stroke_polygon));
7767 if (stroke_polygon == (PrimitiveInfo *) NULL)
7768 {
7769 (void) ThrowMagickException(exception,GetMagickModule(),
7770 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7771 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7772 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7773 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7774 polygon_primitive);
7775 return(stroke_polygon);
7776 }
7777 for (i=0; i < (ssize_t) p; i++)
7778 {
7779 stroke_polygon[i]=polygon_primitive[0];
7780 stroke_polygon[i].point=stroke_p[i];
7781 }
7782 if (closed_path != MagickFalse)
7783 {
7784 stroke_polygon[i]=polygon_primitive[0];
7785 stroke_polygon[i].point=stroke_polygon[0].point;
7786 i++;
7787 }
7788 for ( ; i < (ssize_t) (p+q+closed_path); i++)
7789 {
7790 stroke_polygon[i]=polygon_primitive[0];
7791 stroke_polygon[i].point=stroke_q[p+q+closed_path-(i+1)];
7792 }
7793 if (closed_path != MagickFalse)
7794 {
7795 stroke_polygon[i]=polygon_primitive[0];
7796 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
7797 i++;
7798 }
7799 stroke_polygon[i]=polygon_primitive[0];
7800 stroke_polygon[i].point=stroke_polygon[0].point;
7801 i++;
7802 stroke_polygon[i].primitive=UndefinedPrimitive;
7803 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
7804 stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7805 stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7806 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
7807 return(stroke_polygon);
7808}