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