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