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