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