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