blob: c79c0e0f5fd2f4ab3a836e984671eed540122d50 [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% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 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
cristybb503372010-05-27 20:51:26 +0000102 size_t
cristy3ed852e2009-09-05 21:47:34 +0000103 number_points;
104
cristybb503372010-05-27 20:51:26 +0000105 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000106 direction;
107
108 MagickBooleanType
109 ghostline;
110
cristybb503372010-05-27 20:51:26 +0000111 size_t
cristy3ed852e2009-09-05 21:47:34 +0000112 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
cristybb503372010-05-27 20:51:26 +0000130 size_t
cristy3ed852e2009-09-05 21:47:34 +0000131 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
cristybb503372010-05-27 20:51:26 +0000161static size_t
cristy3ed852e2009-09-05 21:47:34 +0000162 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),
cristybb503372010-05-27 20:51:26 +0000168 TraceBezier(PrimitiveInfo *,const size_t),
cristy3ed852e2009-09-05 21:47:34 +0000169 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),
cristybb503372010-05-27 20:51:26 +0000175 TraceSquareLinecap(PrimitiveInfo *,const size_t,const MagickRealType);
cristy3ed852e2009-09-05 21:47:34 +0000176
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
cristy73bd4a52010-10-05 11:24:23 +0000200 draw_info=(DrawInfo *) AcquireMagickMemory(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
cristy73bd4a52010-10-05 11:24:23 +0000240 clone_info=(DrawInfo *) AcquireMagickMemory(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 {
cristybb503372010-05-27 20:51:26 +0000303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000304 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 {
cristybb503372010-05-27 20:51:26 +0000318 size_t
cristy3ed852e2009-09-05 21:47:34 +0000319 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
cristybb503372010-05-27 20:51:26 +0000411 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000412 i,
413 j;
414
415 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
416 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +0000417 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000418 {
cristye8c25f92010-06-03 00:53:06 +0000419 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
420 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000421 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
422 p->direction != MagickFalse ? "down" : "up");
423 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
424 p->ghostline != MagickFalse ? "transparent" : "opaque");
425 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000426 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000427 p->bounds.x2,p->bounds.y2);
cristybb503372010-05-27 20:51:26 +0000428 for (j=0; j < (ssize_t) p->number_points; j++)
cristy14388de2011-05-15 14:57:16 +0000429 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g",
cristy3ed852e2009-09-05 21:47:34 +0000430 p->points[j].x,p->points[j].y);
431 p++;
432 }
433 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
434}
435
cristybb503372010-05-27 20:51:26 +0000436static void ReversePoints(PointInfo *points,const size_t number_points)
cristy3ed852e2009-09-05 21:47:34 +0000437{
438 PointInfo
439 point;
440
cristybb503372010-05-27 20:51:26 +0000441 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000442 i;
443
cristybb503372010-05-27 20:51:26 +0000444 for (i=0; i < (ssize_t) (number_points >> 1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000445 {
446 point=points[i];
447 points[i]=points[number_points-(i+1)];
448 points[number_points-(i+1)]=point;
449 }
450}
451
452static PolygonInfo *ConvertPathToPolygon(
453 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
454{
cristycee97112010-05-28 00:44:52 +0000455 long
cristy3ed852e2009-09-05 21:47:34 +0000456 direction,
457 next_direction;
458
459 PointInfo
460 point,
461 *points;
462
463 PolygonInfo
464 *polygon_info;
465
466 SegmentInfo
467 bounds;
468
cristybb503372010-05-27 20:51:26 +0000469 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000470 i,
471 n;
472
473 MagickBooleanType
474 ghostline;
475
cristybb503372010-05-27 20:51:26 +0000476 size_t
cristy3ed852e2009-09-05 21:47:34 +0000477 edge,
478 number_edges,
479 number_points;
480
481 /*
482 Convert a path to the more efficient sorted rendering form.
483 */
cristy73bd4a52010-10-05 11:24:23 +0000484 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +0000485 if (polygon_info == (PolygonInfo *) NULL)
486 return((PolygonInfo *) NULL);
487 number_edges=16;
488 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
489 sizeof(*polygon_info->edges));
490 if (polygon_info->edges == (EdgeInfo *) NULL)
491 return((PolygonInfo *) NULL);
492 direction=0;
493 edge=0;
494 ghostline=MagickFalse;
495 n=0;
496 number_points=0;
497 points=(PointInfo *) NULL;
498 (void) ResetMagickMemory(&point,0,sizeof(point));
499 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
500 for (i=0; path_info[i].code != EndCode; i++)
501 {
502 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
503 (path_info[i].code == GhostlineCode))
504 {
505 /*
506 Move to.
507 */
508 if ((points != (PointInfo *) NULL) && (n >= 2))
509 {
510 if (edge == number_edges)
511 {
512 number_edges<<=1;
513 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
514 polygon_info->edges,(size_t) number_edges,
515 sizeof(*polygon_info->edges));
516 if (polygon_info->edges == (EdgeInfo *) NULL)
517 return((PolygonInfo *) NULL);
518 }
cristybb503372010-05-27 20:51:26 +0000519 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000520 polygon_info->edges[edge].scanline=(-1.0);
521 polygon_info->edges[edge].highwater=0;
522 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000523 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000524 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000525 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000526 polygon_info->edges[edge].points=points;
527 polygon_info->edges[edge].bounds=bounds;
528 polygon_info->edges[edge].bounds.y1=points[0].y;
529 polygon_info->edges[edge].bounds.y2=points[n-1].y;
530 points=(PointInfo *) NULL;
531 ghostline=MagickFalse;
532 edge++;
533 }
534 if (points == (PointInfo *) NULL)
535 {
536 number_points=16;
537 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
538 sizeof(*points));
539 if (points == (PointInfo *) NULL)
540 return((PolygonInfo *) NULL);
541 }
542 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
543 point=path_info[i].point;
544 points[0]=point;
545 bounds.x1=point.x;
546 bounds.x2=point.x;
547 direction=0;
548 n=1;
549 continue;
550 }
551 /*
552 Line to.
553 */
554 next_direction=((path_info[i].point.y > point.y) ||
555 ((path_info[i].point.y == point.y) &&
556 (path_info[i].point.x > point.x))) ? 1 : -1;
557 if ((direction != 0) && (direction != next_direction))
558 {
559 /*
560 New edge.
561 */
562 point=points[n-1];
563 if (edge == number_edges)
564 {
565 number_edges<<=1;
566 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
567 polygon_info->edges,(size_t) number_edges,
568 sizeof(*polygon_info->edges));
569 if (polygon_info->edges == (EdgeInfo *) NULL)
570 return((PolygonInfo *) NULL);
571 }
cristybb503372010-05-27 20:51:26 +0000572 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000573 polygon_info->edges[edge].scanline=(-1.0);
574 polygon_info->edges[edge].highwater=0;
575 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000576 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000577 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000578 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000579 polygon_info->edges[edge].points=points;
580 polygon_info->edges[edge].bounds=bounds;
581 polygon_info->edges[edge].bounds.y1=points[0].y;
582 polygon_info->edges[edge].bounds.y2=points[n-1].y;
583 number_points=16;
584 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
585 sizeof(*points));
586 if (points == (PointInfo *) NULL)
587 return((PolygonInfo *) NULL);
588 n=1;
589 ghostline=MagickFalse;
590 points[0]=point;
591 bounds.x1=point.x;
592 bounds.x2=point.x;
593 edge++;
594 }
595 direction=next_direction;
596 if (points == (PointInfo *) NULL)
597 continue;
cristybb503372010-05-27 20:51:26 +0000598 if (n == (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +0000599 {
600 number_points<<=1;
601 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
602 sizeof(*points));
603 if (points == (PointInfo *) NULL)
604 return((PolygonInfo *) NULL);
605 }
606 point=path_info[i].point;
607 points[n]=point;
608 if (point.x < bounds.x1)
609 bounds.x1=point.x;
610 if (point.x > bounds.x2)
611 bounds.x2=point.x;
612 n++;
613 }
614 if (points != (PointInfo *) NULL)
615 {
616 if (n < 2)
617 points=(PointInfo *) RelinquishMagickMemory(points);
618 else
619 {
620 if (edge == number_edges)
621 {
622 number_edges<<=1;
623 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
624 polygon_info->edges,(size_t) number_edges,
625 sizeof(*polygon_info->edges));
626 if (polygon_info->edges == (EdgeInfo *) NULL)
627 return((PolygonInfo *) NULL);
628 }
cristybb503372010-05-27 20:51:26 +0000629 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000630 polygon_info->edges[edge].scanline=(-1.0);
631 polygon_info->edges[edge].highwater=0;
632 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000633 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000634 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000635 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000636 polygon_info->edges[edge].points=points;
637 polygon_info->edges[edge].bounds=bounds;
638 polygon_info->edges[edge].bounds.y1=points[0].y;
639 polygon_info->edges[edge].bounds.y2=points[n-1].y;
640 ghostline=MagickFalse;
641 edge++;
642 }
643 }
644 polygon_info->number_edges=edge;
645 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
646 sizeof(*polygon_info->edges),CompareEdges);
647 if (IsEventLogging() != MagickFalse)
648 LogPolygonInfo(polygon_info);
649 return(polygon_info);
650}
651
652/*
653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654% %
655% %
656% %
657+ C o n v e r t P r i m i t i v e T o P a t h %
658% %
659% %
660% %
661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662%
663% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
664% path structure.
665%
666% The format of the ConvertPrimitiveToPath method is:
667%
668% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
669% const PrimitiveInfo *primitive_info)
670%
671% A description of each parameter follows:
672%
673% o Method ConvertPrimitiveToPath returns a vector path structure of type
674% PathInfo.
675%
676% o draw_info: a structure of type DrawInfo.
677%
678% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
679%
680%
681*/
682
683static void LogPathInfo(const PathInfo *path_info)
684{
685 register const PathInfo
686 *p;
687
688 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
689 for (p=path_info; p->code != EndCode; p++)
690 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000691 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
cristy3ed852e2009-09-05 21:47:34 +0000692 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
693 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
694 "?");
695 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
696}
697
698static PathInfo *ConvertPrimitiveToPath(
699 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
700{
cristy3ed852e2009-09-05 21:47:34 +0000701 PathInfo
702 *path_info;
703
704 PathInfoCode
705 code;
706
707 PointInfo
708 p,
709 q;
710
cristybb503372010-05-27 20:51:26 +0000711 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000712 i,
713 n;
714
cristy826a5472010-08-31 23:21:38 +0000715 ssize_t
716 coordinates,
717 start;
718
cristy3ed852e2009-09-05 21:47:34 +0000719 /*
720 Converts a PrimitiveInfo structure into a vector path structure.
721 */
722 switch (primitive_info->primitive)
723 {
724 case PointPrimitive:
725 case ColorPrimitive:
726 case MattePrimitive:
727 case TextPrimitive:
728 case ImagePrimitive:
729 return((PathInfo *) NULL);
730 default:
731 break;
732 }
733 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
734 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
735 sizeof(*path_info));
736 if (path_info == (PathInfo *) NULL)
737 return((PathInfo *) NULL);
738 coordinates=0;
739 n=0;
740 p.x=(-1.0);
741 p.y=(-1.0);
742 q.x=(-1.0);
743 q.y=(-1.0);
744 start=0;
745 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
746 {
747 code=LineToCode;
748 if (coordinates <= 0)
749 {
cristybb503372010-05-27 20:51:26 +0000750 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +0000751 p=primitive_info[i].point;
752 start=n;
753 code=MoveToCode;
754 }
755 coordinates--;
756 /*
757 Eliminate duplicate points.
758 */
759 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
760 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
761 {
762 path_info[n].code=code;
763 path_info[n].point=primitive_info[i].point;
764 q=primitive_info[i].point;
765 n++;
766 }
767 if (coordinates > 0)
768 continue;
769 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
770 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
771 continue;
772 /*
773 Mark the p point as open if it does not match the q.
774 */
775 path_info[start].code=OpenCode;
776 path_info[n].code=GhostlineCode;
777 path_info[n].point=primitive_info[i].point;
778 n++;
779 path_info[n].code=LineToCode;
780 path_info[n].point=p;
781 n++;
782 }
783 path_info[n].code=EndCode;
784 path_info[n].point.x=0.0;
785 path_info[n].point.y=0.0;
786 if (IsEventLogging() != MagickFalse)
787 LogPathInfo(path_info);
788 return(path_info);
789}
790
791/*
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793% %
794% %
795% %
796% D e s t r o y D r a w I n f o %
797% %
798% %
799% %
800%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801%
802% DestroyDrawInfo() deallocates memory associated with an DrawInfo
803% structure.
804%
805% The format of the DestroyDrawInfo method is:
806%
807% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
808%
809% A description of each parameter follows:
810%
811% o draw_info: the draw info.
812%
813*/
814MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
815{
816 if (draw_info->debug != MagickFalse)
817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
818 assert(draw_info != (DrawInfo *) NULL);
819 assert(draw_info->signature == MagickSignature);
820 if (draw_info->primitive != (char *) NULL)
821 draw_info->primitive=DestroyString(draw_info->primitive);
822 if (draw_info->text != (char *) NULL)
823 draw_info->text=DestroyString(draw_info->text);
824 if (draw_info->geometry != (char *) NULL)
825 draw_info->geometry=DestroyString(draw_info->geometry);
826 if (draw_info->tile != (Image *) NULL)
827 draw_info->tile=DestroyImage(draw_info->tile);
828 if (draw_info->fill_pattern != (Image *) NULL)
829 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
830 if (draw_info->stroke_pattern != (Image *) NULL)
831 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
832 if (draw_info->font != (char *) NULL)
833 draw_info->font=DestroyString(draw_info->font);
834 if (draw_info->metrics != (char *) NULL)
835 draw_info->metrics=DestroyString(draw_info->metrics);
836 if (draw_info->family != (char *) NULL)
837 draw_info->family=DestroyString(draw_info->family);
838 if (draw_info->encoding != (char *) NULL)
839 draw_info->encoding=DestroyString(draw_info->encoding);
840 if (draw_info->density != (char *) NULL)
841 draw_info->density=DestroyString(draw_info->density);
842 if (draw_info->server_name != (char *) NULL)
843 draw_info->server_name=(char *)
844 RelinquishMagickMemory(draw_info->server_name);
845 if (draw_info->dash_pattern != (double *) NULL)
846 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
847 draw_info->dash_pattern);
848 if (draw_info->gradient.stops != (StopInfo *) NULL)
849 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
850 draw_info->gradient.stops);
851 if (draw_info->clip_mask != (char *) NULL)
852 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
853 draw_info->signature=(~MagickSignature);
854 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
855 return(draw_info);
856}
857
858/*
859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860% %
861% %
862% %
863+ D e s t r o y E d g e %
864% %
865% %
866% %
867%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868%
869% DestroyEdge() destroys the specified polygon edge.
870%
871% The format of the DestroyEdge method is:
872%
cristybb503372010-05-27 20:51:26 +0000873% ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
cristy3ed852e2009-09-05 21:47:34 +0000874%
875% A description of each parameter follows:
876%
877% o polygon_info: Specifies a pointer to an PolygonInfo structure.
878%
879% o edge: the polygon edge number to destroy.
880%
881*/
cristybb503372010-05-27 20:51:26 +0000882static size_t DestroyEdge(PolygonInfo *polygon_info,
883 const size_t edge)
cristy3ed852e2009-09-05 21:47:34 +0000884{
885 assert(edge < polygon_info->number_edges);
886 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
887 polygon_info->edges[edge].points);
888 polygon_info->number_edges--;
889 if (edge < polygon_info->number_edges)
890 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
891 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
892 return(polygon_info->number_edges);
893}
894
895/*
896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897% %
898% %
899% %
900+ D e s t r o y P o l y g o n I n f o %
901% %
902% %
903% %
904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905%
906% DestroyPolygonInfo() destroys the PolygonInfo data structure.
907%
908% The format of the DestroyPolygonInfo method is:
909%
910% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
911%
912% A description of each parameter follows:
913%
914% o polygon_info: Specifies a pointer to an PolygonInfo structure.
915%
916*/
917static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
918{
cristybb503372010-05-27 20:51:26 +0000919 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000920 i;
921
cristybb503372010-05-27 20:51:26 +0000922 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000923 polygon_info->edges[i].points=(PointInfo *)
924 RelinquishMagickMemory(polygon_info->edges[i].points);
925 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
926 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
927}
928
929/*
930%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
931% %
932% %
933% %
934% D r a w A f f i n e I m a g e %
935% %
936% %
937% %
938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939%
940% DrawAffineImage() composites the source over the destination image as
941% dictated by the affine transform.
942%
943% The format of the DrawAffineImage method is:
944%
945% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
946% const AffineMatrix *affine)
947%
948% A description of each parameter follows:
949%
950% o image: the image.
951%
952% o source: the source image.
953%
954% o affine: the affine transform.
955%
956*/
957static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
958 const double y,const SegmentInfo *edge)
959{
960 double
961 intercept,
962 z;
963
964 register double
965 x;
966
967 SegmentInfo
968 inverse_edge;
969
970 /*
971 Determine left and right edges.
972 */
973 inverse_edge.x1=edge->x1;
974 inverse_edge.y1=edge->y1;
975 inverse_edge.x2=edge->x2;
976 inverse_edge.y2=edge->y2;
977 z=affine->ry*y+affine->tx;
978 if (affine->sx > MagickEpsilon)
979 {
980 intercept=(-z/affine->sx);
981 x=intercept+MagickEpsilon;
982 if (x > inverse_edge.x1)
983 inverse_edge.x1=x;
984 intercept=(-z+(double) image->columns)/affine->sx;
985 x=intercept-MagickEpsilon;
986 if (x < inverse_edge.x2)
987 inverse_edge.x2=x;
988 }
989 else
990 if (affine->sx < -MagickEpsilon)
991 {
992 intercept=(-z+(double) image->columns)/affine->sx;
993 x=intercept+MagickEpsilon;
994 if (x > inverse_edge.x1)
995 inverse_edge.x1=x;
996 intercept=(-z/affine->sx);
997 x=intercept-MagickEpsilon;
998 if (x < inverse_edge.x2)
999 inverse_edge.x2=x;
1000 }
1001 else
cristybb503372010-05-27 20:51:26 +00001002 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
cristy3ed852e2009-09-05 21:47:34 +00001003 {
1004 inverse_edge.x2=edge->x1;
1005 return(inverse_edge);
1006 }
1007 /*
1008 Determine top and bottom edges.
1009 */
1010 z=affine->sy*y+affine->ty;
1011 if (affine->rx > MagickEpsilon)
1012 {
1013 intercept=(-z/affine->rx);
1014 x=intercept+MagickEpsilon;
1015 if (x > inverse_edge.x1)
1016 inverse_edge.x1=x;
1017 intercept=(-z+(double) image->rows)/affine->rx;
1018 x=intercept-MagickEpsilon;
1019 if (x < inverse_edge.x2)
1020 inverse_edge.x2=x;
1021 }
1022 else
1023 if (affine->rx < -MagickEpsilon)
1024 {
1025 intercept=(-z+(double) image->rows)/affine->rx;
1026 x=intercept+MagickEpsilon;
1027 if (x > inverse_edge.x1)
1028 inverse_edge.x1=x;
1029 intercept=(-z/affine->rx);
1030 x=intercept-MagickEpsilon;
1031 if (x < inverse_edge.x2)
1032 inverse_edge.x2=x;
1033 }
1034 else
cristybb503372010-05-27 20:51:26 +00001035 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001036 {
1037 inverse_edge.x2=edge->x2;
1038 return(inverse_edge);
1039 }
1040 return(inverse_edge);
1041}
1042
1043static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1044{
1045 AffineMatrix
1046 inverse_affine;
1047
1048 double
1049 determinant;
1050
1051 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
1052 inverse_affine.sx=determinant*affine->sy;
1053 inverse_affine.rx=determinant*(-affine->rx);
1054 inverse_affine.ry=determinant*(-affine->ry);
1055 inverse_affine.sy=determinant*affine->sx;
1056 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1057 inverse_affine.ry;
1058 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1059 inverse_affine.sy;
1060 return(inverse_affine);
1061}
1062
cristybb503372010-05-27 20:51:26 +00001063static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +00001064{
1065 if (x < 0)
1066 return(-x);
1067 return(x);
1068}
1069
1070static inline double MagickMax(const double x,const double y)
1071{
1072 if (x > y)
1073 return(x);
1074 return(y);
1075}
1076
1077static inline double MagickMin(const double x,const double y)
1078{
1079 if (x < y)
1080 return(x);
1081 return(y);
1082}
1083
1084MagickExport MagickBooleanType DrawAffineImage(Image *image,
1085 const Image *source,const AffineMatrix *affine)
1086{
1087 AffineMatrix
1088 inverse_affine;
1089
cristyfa112112010-01-04 17:48:07 +00001090 CacheView
1091 *image_view,
1092 *source_view;
1093
cristy3ed852e2009-09-05 21:47:34 +00001094 ExceptionInfo
1095 *exception;
1096
cristy3ed852e2009-09-05 21:47:34 +00001097 MagickBooleanType
1098 status;
1099
1100 MagickPixelPacket
1101 zero;
1102
1103 PointInfo
1104 extent[4],
1105 min,
1106 max,
1107 point;
1108
cristybb503372010-05-27 20:51:26 +00001109 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001110 i;
1111
cristy3ed852e2009-09-05 21:47:34 +00001112 SegmentInfo
1113 edge;
1114
cristy826a5472010-08-31 23:21:38 +00001115 ssize_t
1116 y;
1117
cristy3ed852e2009-09-05 21:47:34 +00001118 /*
1119 Determine bounding box.
1120 */
1121 assert(image != (Image *) NULL);
1122 assert(image->signature == MagickSignature);
1123 if (image->debug != MagickFalse)
1124 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1125 assert(source != (const Image *) NULL);
1126 assert(source->signature == MagickSignature);
1127 assert(affine != (AffineMatrix *) NULL);
1128 extent[0].x=0.0;
1129 extent[0].y=0.0;
1130 extent[1].x=(double) source->columns-1.0;
1131 extent[1].y=0.0;
1132 extent[2].x=(double) source->columns-1.0;
1133 extent[2].y=(double) source->rows-1.0;
1134 extent[3].x=0.0;
1135 extent[3].y=(double) source->rows-1.0;
1136 for (i=0; i < 4; i++)
1137 {
1138 point=extent[i];
1139 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1140 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1141 }
1142 min=extent[0];
1143 max=extent[0];
1144 for (i=1; i < 4; i++)
1145 {
1146 if (min.x > extent[i].x)
1147 min.x=extent[i].x;
1148 if (min.y > extent[i].y)
1149 min.y=extent[i].y;
1150 if (max.x < extent[i].x)
1151 max.x=extent[i].x;
1152 if (max.y < extent[i].y)
1153 max.y=extent[i].y;
1154 }
1155 /*
1156 Affine transform image.
1157 */
1158 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1159 return(MagickFalse);
1160 status=MagickTrue;
1161 edge.x1=MagickMax(min.x,0.0);
1162 edge.y1=MagickMax(min.y,0.0);
1163 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1164 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1165 inverse_affine=InverseAffineMatrix(affine);
1166 GetMagickPixelPacket(image,&zero);
1167 exception=(&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001168 image_view=AcquireCacheView(image);
1169 source_view=AcquireCacheView(source);
cristyb5d5f722009-11-04 03:03:49 +00001170#if defined(MAGICKCORE_OPENMP_SUPPORT)
1171 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001172#endif
cristybb503372010-05-27 20:51:26 +00001173 for (y=(ssize_t) ceil(edge.y1-0.5); y <= (ssize_t) floor(edge.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00001174 {
cristy3ed852e2009-09-05 21:47:34 +00001175 MagickPixelPacket
1176 composite,
1177 pixel;
1178
1179 PointInfo
1180 point;
1181
1182 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001183 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001184
cristybb503372010-05-27 20:51:26 +00001185 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001186 x;
1187
1188 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001189 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001190
1191 SegmentInfo
1192 inverse_edge;
1193
cristy826a5472010-08-31 23:21:38 +00001194 ssize_t
1195 x_offset;
1196
cristy3ed852e2009-09-05 21:47:34 +00001197 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1198 if (inverse_edge.x2 < inverse_edge.x1)
1199 continue;
cristy6ebe97c2010-07-03 01:17:28 +00001200 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
1201 0.5),y,(size_t) ((ssize_t) floor(inverse_edge.x2+0.5)-(ssize_t) floor(
cristy06609ee2010-03-17 20:21:27 +00001202 inverse_edge.x1+0.5)+1),1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001203 if (q == (PixelPacket *) NULL)
1204 continue;
cristy3ed852e2009-09-05 21:47:34 +00001205 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1206 pixel=zero;
1207 composite=zero;
1208 x_offset=0;
cristybb503372010-05-27 20:51:26 +00001209 for (x=(ssize_t) ceil(inverse_edge.x1-0.5); x <= (ssize_t) floor(inverse_edge.x2+0.5); x++)
cristy3ed852e2009-09-05 21:47:34 +00001210 {
1211 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1212 inverse_affine.tx;
1213 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1214 inverse_affine.ty;
cristyf998fb32011-04-27 23:00:47 +00001215 (void) InterpolateMagickPixelPacket(source,source_view,
cristy8a7c3e82011-03-26 02:10:53 +00001216 UndefinedInterpolatePixel,point.x,point.y,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00001217 SetMagickPixelPacket(image,q,indexes+x_offset,&composite);
1218 MagickPixelCompositeOver(&pixel,pixel.opacity,&composite,
1219 composite.opacity,&composite);
1220 SetPixelPacket(image,&composite,q,indexes+x_offset);
1221 x_offset++;
1222 q++;
1223 }
1224 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1225 status=MagickFalse;
1226 }
cristy3ed852e2009-09-05 21:47:34 +00001227 source_view=DestroyCacheView(source_view);
1228 image_view=DestroyCacheView(image_view);
1229 return(status);
1230}
1231
1232/*
1233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234% %
1235% %
1236% %
1237+ D r a w B o u n d i n g R e c t a n g l e s %
1238% %
1239% %
1240% %
1241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242%
1243% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1244% is only useful for developers debugging the rendering algorithm.
1245%
1246% The format of the DrawBoundingRectangles method is:
1247%
1248% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1249% PolygonInfo *polygon_info)
1250%
1251% A description of each parameter follows:
1252%
1253% o image: the image.
1254%
1255% o draw_info: the draw info.
1256%
1257% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1258%
1259*/
1260static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1261 const PolygonInfo *polygon_info)
1262{
1263 DrawInfo
1264 *clone_info;
1265
cristy3ed852e2009-09-05 21:47:34 +00001266 MagickRealType
1267 mid;
1268
1269 PointInfo
1270 end,
1271 resolution,
1272 start;
1273
1274 PrimitiveInfo
1275 primitive_info[6];
1276
cristybb503372010-05-27 20:51:26 +00001277 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001278 i;
1279
1280 SegmentInfo
1281 bounds;
1282
cristy826a5472010-08-31 23:21:38 +00001283 ssize_t
1284 coordinates;
1285
cristy3ed852e2009-09-05 21:47:34 +00001286 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1287 (void) QueryColorDatabase("#0000",&clone_info->fill,&image->exception);
1288 resolution.x=DefaultResolution;
1289 resolution.y=DefaultResolution;
1290 if (clone_info->density != (char *) NULL)
1291 {
1292 GeometryInfo
1293 geometry_info;
1294
1295 MagickStatusType
1296 flags;
1297
1298 flags=ParseGeometry(clone_info->density,&geometry_info);
1299 resolution.x=geometry_info.rho;
1300 resolution.y=geometry_info.sigma;
1301 if ((flags & SigmaValue) == MagickFalse)
1302 resolution.y=resolution.x;
1303 }
1304 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1305 clone_info->stroke_width/2.0;
1306 bounds.x1=0.0;
1307 bounds.y1=0.0;
1308 bounds.x2=0.0;
1309 bounds.y2=0.0;
1310 if (polygon_info != (PolygonInfo *) NULL)
1311 {
1312 bounds=polygon_info->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00001313 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001314 {
1315 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1316 bounds.x1=polygon_info->edges[i].bounds.x1;
1317 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1318 bounds.y1=polygon_info->edges[i].bounds.y1;
1319 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1320 bounds.x2=polygon_info->edges[i].bounds.x2;
1321 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1322 bounds.y2=polygon_info->edges[i].bounds.y2;
1323 }
1324 bounds.x1-=mid;
1325 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1326 image->columns ? (double) image->columns-1 : bounds.x1;
1327 bounds.y1-=mid;
1328 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1329 image->rows ? (double) image->rows-1 : bounds.y1;
1330 bounds.x2+=mid;
1331 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1332 image->columns ? (double) image->columns-1 : bounds.x2;
1333 bounds.y2+=mid;
1334 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1335 image->rows ? (double) image->rows-1 : bounds.y2;
cristybb503372010-05-27 20:51:26 +00001336 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001337 {
1338 if (polygon_info->edges[i].direction != 0)
1339 (void) QueryColorDatabase("red",&clone_info->stroke,
1340 &image->exception);
1341 else
1342 (void) QueryColorDatabase("green",&clone_info->stroke,
1343 &image->exception);
1344 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1345 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1346 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1347 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1348 primitive_info[0].primitive=RectanglePrimitive;
1349 TraceRectangle(primitive_info,start,end);
1350 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001351 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001352 primitive_info[coordinates].primitive=UndefinedPrimitive;
1353 (void) DrawPrimitive(image,clone_info,primitive_info);
1354 }
1355 }
1356 (void) QueryColorDatabase("blue",&clone_info->stroke,&image->exception);
1357 start.x=(double) (bounds.x1-mid);
1358 start.y=(double) (bounds.y1-mid);
1359 end.x=(double) (bounds.x2+mid);
1360 end.y=(double) (bounds.y2+mid);
1361 primitive_info[0].primitive=RectanglePrimitive;
1362 TraceRectangle(primitive_info,start,end);
1363 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001364 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001365 primitive_info[coordinates].primitive=UndefinedPrimitive;
1366 (void) DrawPrimitive(image,clone_info,primitive_info);
1367 clone_info=DestroyDrawInfo(clone_info);
1368}
1369
1370/*
1371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1372% %
1373% %
1374% %
1375% D r a w C l i p P a t h %
1376% %
1377% %
1378% %
1379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380%
1381% DrawClipPath() draws the clip path on the image mask.
1382%
1383% The format of the DrawClipPath method is:
1384%
1385% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
1386% const char *name)
1387%
1388% A description of each parameter follows:
1389%
1390% o image: the image.
1391%
1392% o draw_info: the draw info.
1393%
1394% o name: the name of the clip path.
1395%
1396*/
1397MagickExport MagickBooleanType DrawClipPath(Image *image,
1398 const DrawInfo *draw_info,const char *name)
1399{
1400 char
1401 clip_mask[MaxTextExtent];
1402
1403 const char
1404 *value;
1405
1406 DrawInfo
1407 *clone_info;
1408
1409 MagickStatusType
1410 status;
1411
1412 assert(image != (Image *) NULL);
1413 assert(image->signature == MagickSignature);
1414 if (image->debug != MagickFalse)
1415 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1416 assert(draw_info != (const DrawInfo *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001417 (void) FormatLocaleString(clip_mask,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00001418 value=GetImageArtifact(image,clip_mask);
1419 if (value == (const char *) NULL)
1420 return(MagickFalse);
1421 if (image->clip_mask == (Image *) NULL)
1422 {
1423 Image
1424 *clip_mask;
1425
1426 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
1427 &image->exception);
1428 if (clip_mask == (Image *) NULL)
1429 return(MagickFalse);
1430 (void) SetImageClipMask(image,clip_mask);
1431 clip_mask=DestroyImage(clip_mask);
1432 }
1433 (void) QueryColorDatabase("#00000000",&image->clip_mask->background_color,
1434 &image->exception);
1435 image->clip_mask->background_color.opacity=(Quantum) TransparentOpacity;
1436 (void) SetImageBackgroundColor(image->clip_mask);
1437 if (image->debug != MagickFalse)
1438 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1439 draw_info->clip_mask);
1440 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1441 (void) CloneString(&clone_info->primitive,value);
1442 (void) QueryColorDatabase("#ffffff",&clone_info->fill,&image->exception);
1443 clone_info->clip_mask=(char *) NULL;
1444 status=DrawImage(image->clip_mask,clone_info);
1445 status|=NegateImage(image->clip_mask,MagickFalse);
1446 clone_info=DestroyDrawInfo(clone_info);
1447 if (image->debug != MagickFalse)
1448 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1449 return(status != 0 ? MagickTrue : MagickFalse);
1450}
1451
1452/*
1453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1454% %
1455% %
1456% %
1457+ D r a w D a s h P o l y g o n %
1458% %
1459% %
1460% %
1461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462%
1463% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1464% image while respecting the dash offset and dash pattern attributes.
1465%
1466% The format of the DrawDashPolygon method is:
1467%
1468% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1469% const PrimitiveInfo *primitive_info,Image *image)
1470%
1471% A description of each parameter follows:
1472%
1473% o draw_info: the draw info.
1474%
1475% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1476%
1477% o image: the image.
1478%
1479%
1480*/
1481static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1482 const PrimitiveInfo *primitive_info,Image *image)
1483{
1484 DrawInfo
1485 *clone_info;
1486
cristy3ed852e2009-09-05 21:47:34 +00001487 MagickRealType
1488 length,
1489 maximum_length,
1490 offset,
1491 scale,
1492 total_length;
1493
1494 MagickStatusType
1495 status;
1496
1497 PrimitiveInfo
1498 *dash_polygon;
1499
cristybb503372010-05-27 20:51:26 +00001500 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001501 i;
1502
1503 register MagickRealType
1504 dx,
1505 dy;
1506
cristybb503372010-05-27 20:51:26 +00001507 size_t
cristy3ed852e2009-09-05 21:47:34 +00001508 number_vertices;
1509
cristy826a5472010-08-31 23:21:38 +00001510 ssize_t
1511 j,
1512 n;
1513
cristy3ed852e2009-09-05 21:47:34 +00001514 assert(draw_info != (const DrawInfo *) NULL);
1515 if (image->debug != MagickFalse)
1516 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1517 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1518 clone_info->miterlimit=0;
1519 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
cristybb503372010-05-27 20:51:26 +00001520 number_vertices=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001521 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1522 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
1523 if (dash_polygon == (PrimitiveInfo *) NULL)
1524 return(MagickFalse);
1525 dash_polygon[0]=primitive_info[0];
1526 scale=ExpandAffine(&draw_info->affine);
1527 length=scale*(draw_info->dash_pattern[0]-0.5);
1528 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
1529 j=1;
1530 for (n=0; offset > 0.0; j=0)
1531 {
1532 if (draw_info->dash_pattern[n] <= 0.0)
1533 break;
1534 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1535 if (offset > length)
1536 {
1537 offset-=length;
1538 n++;
1539 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1540 continue;
1541 }
1542 if (offset < length)
1543 {
1544 length-=offset;
1545 offset=0.0;
1546 break;
1547 }
1548 offset=0.0;
1549 n++;
1550 }
1551 status=MagickTrue;
1552 maximum_length=0.0;
1553 total_length=0.0;
cristybb503372010-05-27 20:51:26 +00001554 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00001555 {
1556 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1557 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1558 maximum_length=hypot((double) dx,dy);
1559 if (length == 0.0)
1560 {
1561 n++;
1562 if (draw_info->dash_pattern[n] == 0.0)
1563 n=0;
1564 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1565 }
1566 for (total_length=0.0; (total_length+length) < maximum_length; )
1567 {
1568 total_length+=length;
1569 if ((n & 0x01) != 0)
1570 {
1571 dash_polygon[0]=primitive_info[0];
1572 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1573 total_length/maximum_length);
1574 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1575 total_length/maximum_length);
1576 j=1;
1577 }
1578 else
1579 {
cristybb503372010-05-27 20:51:26 +00001580 if ((j+1) > (ssize_t) (2*number_vertices))
cristy3ed852e2009-09-05 21:47:34 +00001581 break;
1582 dash_polygon[j]=primitive_info[i-1];
1583 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1584 total_length/maximum_length);
1585 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1586 total_length/maximum_length);
1587 dash_polygon[j].coordinates=1;
1588 j++;
cristybb503372010-05-27 20:51:26 +00001589 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001590 dash_polygon[j].primitive=UndefinedPrimitive;
1591 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
1592 }
1593 n++;
1594 if (draw_info->dash_pattern[n] == 0.0)
1595 n=0;
1596 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1597 }
1598 length-=(maximum_length-total_length);
1599 if ((n & 0x01) != 0)
1600 continue;
1601 dash_polygon[j]=primitive_info[i];
1602 dash_polygon[j].coordinates=1;
1603 j++;
1604 }
1605 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
1606 {
1607 dash_polygon[j]=primitive_info[i-1];
1608 dash_polygon[j].point.x+=MagickEpsilon;
1609 dash_polygon[j].point.y+=MagickEpsilon;
1610 dash_polygon[j].coordinates=1;
1611 j++;
cristybb503372010-05-27 20:51:26 +00001612 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001613 dash_polygon[j].primitive=UndefinedPrimitive;
1614 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
1615 }
1616 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1617 clone_info=DestroyDrawInfo(clone_info);
1618 if (image->debug != MagickFalse)
1619 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1620 return(status != 0 ? MagickTrue : MagickFalse);
1621}
1622
1623/*
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625% %
1626% %
1627% %
1628% D r a w I m a g e %
1629% %
1630% %
1631% %
1632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633%
1634% DrawImage() draws a graphic primitive on your image. The primitive
1635% may be represented as a string or filename. Precede the filename with an
1636% "at" sign (@) and the contents of the file are drawn on the image. You
1637% can affect how text is drawn by setting one or more members of the draw
1638% info structure.
1639%
1640% The format of the DrawImage method is:
1641%
1642% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
1643%
1644% A description of each parameter follows:
1645%
1646% o image: the image.
1647%
1648% o draw_info: the draw info.
1649%
1650*/
1651
1652static inline MagickBooleanType IsPoint(const char *point)
1653{
1654 char
1655 *p;
1656
1657 double
1658 value;
1659
cristyc1acd842011-05-19 23:05:47 +00001660 value=InterpretLocaleValue(point,&p);
cristy3ed852e2009-09-05 21:47:34 +00001661 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1662}
1663
1664static inline void TracePoint(PrimitiveInfo *primitive_info,
1665 const PointInfo point)
1666{
1667 primitive_info->coordinates=1;
1668 primitive_info->point=point;
1669}
1670
1671MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
1672{
1673#define RenderImageTag "Render/Image"
1674
1675 AffineMatrix
1676 affine,
1677 current;
1678
1679 char
1680 key[2*MaxTextExtent],
1681 keyword[MaxTextExtent],
1682 geometry[MaxTextExtent],
1683 name[MaxTextExtent],
1684 pattern[MaxTextExtent],
1685 *primitive,
1686 *token;
1687
1688 const char
1689 *q;
1690
1691 DrawInfo
1692 **graphic_context;
1693
cristy3ed852e2009-09-05 21:47:34 +00001694 MagickBooleanType
1695 proceed,
1696 status;
1697
1698 MagickRealType
1699 angle,
1700 factor,
1701 primitive_extent;
1702
1703 PointInfo
1704 point;
1705
1706 PixelPacket
1707 start_color;
1708
1709 PrimitiveInfo
1710 *primitive_info;
1711
1712 PrimitiveType
1713 primitive_type;
1714
1715 register const char
1716 *p;
1717
cristybb503372010-05-27 20:51:26 +00001718 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001719 i,
1720 x;
1721
1722 SegmentInfo
1723 bounds;
1724
1725 size_t
cristy826a5472010-08-31 23:21:38 +00001726 length,
cristy3ed852e2009-09-05 21:47:34 +00001727 number_points;
1728
cristy826a5472010-08-31 23:21:38 +00001729 ssize_t
1730 j,
1731 k,
1732 n;
1733
cristy3ed852e2009-09-05 21:47:34 +00001734 /*
1735 Ensure the annotation info is valid.
1736 */
1737 assert(image != (Image *) NULL);
1738 assert(image->signature == MagickSignature);
1739 if (image->debug != MagickFalse)
1740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1741 assert(draw_info != (DrawInfo *) NULL);
1742 assert(draw_info->signature == MagickSignature);
1743 if (image->debug != MagickFalse)
1744 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1745 if ((draw_info->primitive == (char *) NULL) ||
1746 (*draw_info->primitive == '\0'))
1747 return(MagickFalse);
1748 if (image->debug != MagickFalse)
1749 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
1750 if (*draw_info->primitive != '@')
1751 primitive=AcquireString(draw_info->primitive);
1752 else
1753 primitive=FileToString(draw_info->primitive+1,~0,&image->exception);
1754 if (primitive == (char *) NULL)
1755 return(MagickFalse);
1756 primitive_extent=(MagickRealType) strlen(primitive);
1757 (void) SetImageArtifact(image,"MVG",primitive);
1758 n=0;
1759 /*
1760 Allocate primitive info memory.
1761 */
cristy73bd4a52010-10-05 11:24:23 +00001762 graphic_context=(DrawInfo **) AcquireMagickMemory(
cristyed110712010-03-23 01:16:38 +00001763 sizeof(*graphic_context));
cristy3ed852e2009-09-05 21:47:34 +00001764 if (graphic_context == (DrawInfo **) NULL)
1765 {
1766 primitive=DestroyString(primitive);
1767 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1768 image->filename);
1769 }
1770 number_points=2047;
1771 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
1772 sizeof(*primitive_info));
1773 if (primitive_info == (PrimitiveInfo *) NULL)
1774 {
1775 primitive=DestroyString(primitive);
1776 for ( ; n >= 0; n--)
1777 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
1778 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
1779 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1780 image->filename);
1781 }
1782 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1783 graphic_context[n]->viewbox=image->page;
1784 if ((image->page.width == 0) || (image->page.height == 0))
1785 {
1786 graphic_context[n]->viewbox.width=image->columns;
1787 graphic_context[n]->viewbox.height=image->rows;
1788 }
1789 token=AcquireString(primitive);
1790 (void) QueryColorDatabase("#000000",&start_color,&image->exception);
1791 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1792 return(MagickFalse);
1793 status=MagickTrue;
1794 for (q=primitive; *q != '\0'; )
1795 {
1796 /*
1797 Interpret graphic primitive.
1798 */
1799 GetMagickToken(q,&q,keyword);
1800 if (*keyword == '\0')
1801 break;
1802 if (*keyword == '#')
1803 {
1804 /*
1805 Comment.
1806 */
1807 while ((*q != '\n') && (*q != '\0'))
1808 q++;
1809 continue;
1810 }
1811 p=q-strlen(keyword)-1;
1812 primitive_type=UndefinedPrimitive;
1813 current=graphic_context[n]->affine;
1814 GetAffineMatrix(&affine);
1815 switch (*keyword)
1816 {
1817 case ';':
1818 break;
1819 case 'a':
1820 case 'A':
1821 {
1822 if (LocaleCompare("affine",keyword) == 0)
1823 {
1824 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001825 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001826 GetMagickToken(q,&q,token);
1827 if (*token == ',')
1828 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001829 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001830 GetMagickToken(q,&q,token);
1831 if (*token == ',')
1832 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001833 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001834 GetMagickToken(q,&q,token);
1835 if (*token == ',')
1836 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001837 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001838 GetMagickToken(q,&q,token);
1839 if (*token == ',')
1840 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001841 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001842 GetMagickToken(q,&q,token);
1843 if (*token == ',')
1844 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001845 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001846 break;
1847 }
1848 if (LocaleCompare("arc",keyword) == 0)
1849 {
1850 primitive_type=ArcPrimitive;
1851 break;
1852 }
1853 status=MagickFalse;
1854 break;
1855 }
1856 case 'b':
1857 case 'B':
1858 {
1859 if (LocaleCompare("bezier",keyword) == 0)
1860 {
1861 primitive_type=BezierPrimitive;
1862 break;
1863 }
1864 if (LocaleCompare("border-color",keyword) == 0)
1865 {
1866 GetMagickToken(q,&q,token);
1867 (void) QueryColorDatabase(token,&graphic_context[n]->border_color,
1868 &image->exception);
1869 break;
1870 }
1871 status=MagickFalse;
1872 break;
1873 }
1874 case 'c':
1875 case 'C':
1876 {
1877 if (LocaleCompare("clip-path",keyword) == 0)
1878 {
1879 /*
1880 Create clip mask.
1881 */
1882 GetMagickToken(q,&q,token);
1883 (void) CloneString(&graphic_context[n]->clip_mask,token);
1884 (void) DrawClipPath(image,graphic_context[n],
1885 graphic_context[n]->clip_mask);
1886 break;
1887 }
1888 if (LocaleCompare("clip-rule",keyword) == 0)
1889 {
cristybb503372010-05-27 20:51:26 +00001890 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001891 fill_rule;
1892
1893 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001894 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001895 token);
1896 if (fill_rule == -1)
1897 {
1898 status=MagickFalse;
1899 break;
1900 }
1901 graphic_context[n]->fill_rule=(FillRule) fill_rule;
1902 break;
1903 }
1904 if (LocaleCompare("clip-units",keyword) == 0)
1905 {
cristybb503372010-05-27 20:51:26 +00001906 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001907 clip_units;
1908
1909 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001910 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001911 token);
1912 if (clip_units == -1)
1913 {
1914 status=MagickFalse;
1915 break;
1916 }
1917 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1918 if (clip_units == ObjectBoundingBox)
1919 {
1920 GetAffineMatrix(&current);
1921 affine.sx=draw_info->bounds.x2;
1922 affine.sy=draw_info->bounds.y2;
1923 affine.tx=draw_info->bounds.x1;
1924 affine.ty=draw_info->bounds.y1;
1925 break;
1926 }
1927 break;
1928 }
1929 if (LocaleCompare("circle",keyword) == 0)
1930 {
1931 primitive_type=CirclePrimitive;
1932 break;
1933 }
1934 if (LocaleCompare("color",keyword) == 0)
1935 {
1936 primitive_type=ColorPrimitive;
1937 break;
1938 }
1939 status=MagickFalse;
1940 break;
1941 }
1942 case 'd':
1943 case 'D':
1944 {
1945 if (LocaleCompare("decorate",keyword) == 0)
1946 {
cristybb503372010-05-27 20:51:26 +00001947 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001948 decorate;
1949
1950 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001951 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001952 token);
1953 if (decorate == -1)
1954 {
1955 status=MagickFalse;
1956 break;
1957 }
1958 graphic_context[n]->decorate=(DecorationType) decorate;
1959 break;
1960 }
1961 status=MagickFalse;
1962 break;
1963 }
1964 case 'e':
1965 case 'E':
1966 {
1967 if (LocaleCompare("ellipse",keyword) == 0)
1968 {
1969 primitive_type=EllipsePrimitive;
1970 break;
1971 }
1972 if (LocaleCompare("encoding",keyword) == 0)
1973 {
1974 GetMagickToken(q,&q,token);
1975 (void) CloneString(&graphic_context[n]->encoding,token);
1976 break;
1977 }
1978 status=MagickFalse;
1979 break;
1980 }
1981 case 'f':
1982 case 'F':
1983 {
1984 if (LocaleCompare("fill",keyword) == 0)
1985 {
1986 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00001987 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00001988 if (GetImageArtifact(image,pattern) != (const char *) NULL)
1989 (void) DrawPatternPath(image,draw_info,token,
1990 &graphic_context[n]->fill_pattern);
1991 else
1992 {
1993 status=QueryColorDatabase(token,&graphic_context[n]->fill,
1994 &image->exception);
1995 if (status == MagickFalse)
1996 {
1997 ImageInfo
1998 *pattern_info;
1999
2000 pattern_info=AcquireImageInfo();
2001 (void) CopyMagickString(pattern_info->filename,token,
2002 MaxTextExtent);
2003 graphic_context[n]->fill_pattern=
2004 ReadImage(pattern_info,&image->exception);
2005 CatchException(&image->exception);
2006 pattern_info=DestroyImageInfo(pattern_info);
2007 }
2008 }
2009 break;
2010 }
2011 if (LocaleCompare("fill-opacity",keyword) == 0)
2012 {
2013 GetMagickToken(q,&q,token);
2014 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyce70c172010-01-07 17:15:30 +00002015 graphic_context[n]->fill.opacity=ClampToQuantum((MagickRealType)
cristyc1acd842011-05-19 23:05:47 +00002016 QuantumRange*(1.0-factor*InterpretLocaleValue(token,
2017 (char **) NULL)));
cristy3ed852e2009-09-05 21:47:34 +00002018 break;
2019 }
2020 if (LocaleCompare("fill-rule",keyword) == 0)
2021 {
cristybb503372010-05-27 20:51:26 +00002022 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002023 fill_rule;
2024
2025 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002026 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002027 token);
2028 if (fill_rule == -1)
2029 {
2030 status=MagickFalse;
2031 break;
2032 }
2033 graphic_context[n]->fill_rule=(FillRule) fill_rule;
2034 break;
2035 }
2036 if (LocaleCompare("font",keyword) == 0)
2037 {
2038 GetMagickToken(q,&q,token);
2039 (void) CloneString(&graphic_context[n]->font,token);
2040 if (LocaleCompare("none",token) == 0)
2041 graphic_context[n]->font=(char *)
2042 RelinquishMagickMemory(graphic_context[n]->font);
2043 break;
2044 }
2045 if (LocaleCompare("font-family",keyword) == 0)
2046 {
2047 GetMagickToken(q,&q,token);
2048 (void) CloneString(&graphic_context[n]->family,token);
2049 break;
2050 }
2051 if (LocaleCompare("font-size",keyword) == 0)
2052 {
2053 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002054 graphic_context[n]->pointsize=InterpretLocaleValue(token,
2055 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002056 break;
2057 }
2058 if (LocaleCompare("font-stretch",keyword) == 0)
2059 {
cristybb503372010-05-27 20:51:26 +00002060 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002061 stretch;
2062
2063 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002064 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002065 if (stretch == -1)
2066 {
2067 status=MagickFalse;
2068 break;
2069 }
2070 graphic_context[n]->stretch=(StretchType) stretch;
2071 break;
2072 }
2073 if (LocaleCompare("font-style",keyword) == 0)
2074 {
cristybb503372010-05-27 20:51:26 +00002075 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002076 style;
2077
2078 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002079 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002080 if (style == -1)
2081 {
2082 status=MagickFalse;
2083 break;
2084 }
2085 graphic_context[n]->style=(StyleType) style;
2086 break;
2087 }
2088 if (LocaleCompare("font-weight",keyword) == 0)
2089 {
2090 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002091 graphic_context[n]->weight=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002092 if (LocaleCompare(token,"all") == 0)
2093 graphic_context[n]->weight=0;
2094 if (LocaleCompare(token,"bold") == 0)
2095 graphic_context[n]->weight=700;
2096 if (LocaleCompare(token,"bolder") == 0)
2097 if (graphic_context[n]->weight <= 800)
2098 graphic_context[n]->weight+=100;
2099 if (LocaleCompare(token,"lighter") == 0)
2100 if (graphic_context[n]->weight >= 100)
2101 graphic_context[n]->weight-=100;
2102 if (LocaleCompare(token,"normal") == 0)
2103 graphic_context[n]->weight=400;
2104 break;
2105 }
2106 status=MagickFalse;
2107 break;
2108 }
2109 case 'g':
2110 case 'G':
2111 {
2112 if (LocaleCompare("gradient-units",keyword) == 0)
2113 {
2114 GetMagickToken(q,&q,token);
2115 break;
2116 }
2117 if (LocaleCompare("gravity",keyword) == 0)
2118 {
cristybb503372010-05-27 20:51:26 +00002119 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002120 gravity;
2121
2122 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002123 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002124 if (gravity == -1)
2125 {
2126 status=MagickFalse;
2127 break;
2128 }
2129 graphic_context[n]->gravity=(GravityType) gravity;
2130 break;
2131 }
2132 status=MagickFalse;
2133 break;
2134 }
2135 case 'i':
2136 case 'I':
2137 {
2138 if (LocaleCompare("image",keyword) == 0)
2139 {
cristybb503372010-05-27 20:51:26 +00002140 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002141 compose;
2142
2143 primitive_type=ImagePrimitive;
2144 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002145 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002146 if (compose == -1)
2147 {
2148 status=MagickFalse;
2149 break;
2150 }
2151 graphic_context[n]->compose=(CompositeOperator) compose;
2152 break;
2153 }
cristyb32b90a2009-09-07 21:45:48 +00002154 if (LocaleCompare("interline-spacing",keyword) == 0)
2155 {
2156 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002157 graphic_context[n]->interline_spacing=InterpretLocaleValue(token,
2158 (char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00002159 break;
2160 }
cristy3ed852e2009-09-05 21:47:34 +00002161 if (LocaleCompare("interword-spacing",keyword) == 0)
2162 {
2163 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002164 graphic_context[n]->interword_spacing=InterpretLocaleValue(token,
2165 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002166 break;
2167 }
2168 status=MagickFalse;
2169 break;
2170 }
2171 case 'k':
2172 case 'K':
2173 {
2174 if (LocaleCompare("kerning",keyword) == 0)
2175 {
2176 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002177 graphic_context[n]->kerning=InterpretLocaleValue(token,
2178 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002179 break;
2180 }
2181 status=MagickFalse;
2182 break;
2183 }
2184 case 'l':
2185 case 'L':
2186 {
2187 if (LocaleCompare("line",keyword) == 0)
2188 {
2189 primitive_type=LinePrimitive;
2190 break;
2191 }
2192 status=MagickFalse;
2193 break;
2194 }
2195 case 'm':
2196 case 'M':
2197 {
2198 if (LocaleCompare("matte",keyword) == 0)
2199 {
2200 primitive_type=MattePrimitive;
2201 break;
2202 }
2203 status=MagickFalse;
2204 break;
2205 }
2206 case 'o':
2207 case 'O':
2208 {
2209 if (LocaleCompare("offset",keyword) == 0)
2210 {
2211 GetMagickToken(q,&q,token);
2212 break;
2213 }
2214 if (LocaleCompare("opacity",keyword) == 0)
2215 {
2216 GetMagickToken(q,&q,token);
2217 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyce70c172010-01-07 17:15:30 +00002218 graphic_context[n]->opacity=ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00002219 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->opacity)*
cristyc1acd842011-05-19 23:05:47 +00002220 factor*InterpretLocaleValue(token,(char **) NULL))));
cristy3ed852e2009-09-05 21:47:34 +00002221 graphic_context[n]->fill.opacity=graphic_context[n]->opacity;
2222 graphic_context[n]->stroke.opacity=graphic_context[n]->opacity;
2223 break;
2224 }
2225 status=MagickFalse;
2226 break;
2227 }
2228 case 'p':
2229 case 'P':
2230 {
2231 if (LocaleCompare("path",keyword) == 0)
2232 {
2233 primitive_type=PathPrimitive;
2234 break;
2235 }
2236 if (LocaleCompare("point",keyword) == 0)
2237 {
2238 primitive_type=PointPrimitive;
2239 break;
2240 }
2241 if (LocaleCompare("polyline",keyword) == 0)
2242 {
2243 primitive_type=PolylinePrimitive;
2244 break;
2245 }
2246 if (LocaleCompare("polygon",keyword) == 0)
2247 {
2248 primitive_type=PolygonPrimitive;
2249 break;
2250 }
2251 if (LocaleCompare("pop",keyword) == 0)
2252 {
2253 GetMagickToken(q,&q,token);
2254 if (LocaleCompare("clip-path",token) == 0)
2255 break;
2256 if (LocaleCompare("defs",token) == 0)
2257 break;
2258 if (LocaleCompare("gradient",token) == 0)
2259 break;
2260 if (LocaleCompare("graphic-context",token) == 0)
2261 {
2262 if (n <= 0)
2263 {
2264 (void) ThrowMagickException(&image->exception,
2265 GetMagickModule(),DrawError,
2266 "UnbalancedGraphicContextPushPop","`%s'",token);
2267 n=0;
2268 break;
2269 }
2270 if (graphic_context[n]->clip_mask != (char *) NULL)
2271 if (LocaleCompare(graphic_context[n]->clip_mask,
2272 graphic_context[n-1]->clip_mask) != 0)
2273 (void) SetImageClipMask(image,(Image *) NULL);
2274 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2275 n--;
2276 break;
2277 }
2278 if (LocaleCompare("pattern",token) == 0)
2279 break;
2280 status=MagickFalse;
2281 break;
2282 }
2283 if (LocaleCompare("push",keyword) == 0)
2284 {
2285 GetMagickToken(q,&q,token);
2286 if (LocaleCompare("clip-path",token) == 0)
2287 {
2288 char
2289 name[MaxTextExtent];
2290
2291 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002292 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002293 for (p=q; *q != '\0'; )
2294 {
2295 GetMagickToken(q,&q,token);
2296 if (LocaleCompare(token,"pop") != 0)
2297 continue;
2298 GetMagickToken(q,(const char **) NULL,token);
2299 if (LocaleCompare(token,"clip-path") != 0)
2300 continue;
2301 break;
2302 }
2303 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2304 (void) SetImageArtifact(image,name,token);
2305 GetMagickToken(q,&q,token);
2306 break;
2307 }
2308 if (LocaleCompare("gradient",token) == 0)
2309 {
2310 char
2311 key[2*MaxTextExtent],
2312 name[MaxTextExtent],
2313 type[MaxTextExtent];
2314
cristy3ed852e2009-09-05 21:47:34 +00002315 SegmentInfo
2316 segment;
2317
2318 GetMagickToken(q,&q,token);
2319 (void) CopyMagickString(name,token,MaxTextExtent);
2320 GetMagickToken(q,&q,token);
2321 (void) CopyMagickString(type,token,MaxTextExtent);
2322 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002323 segment.x1=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002324 GetMagickToken(q,&q,token);
2325 if (*token == ',')
2326 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002327 segment.y1=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002328 GetMagickToken(q,&q,token);
2329 if (*token == ',')
2330 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002331 segment.x2=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002332 GetMagickToken(q,&q,token);
2333 if (*token == ',')
2334 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002335 segment.y2=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002336 if (LocaleCompare(type,"radial") == 0)
2337 {
2338 GetMagickToken(q,&q,token);
2339 if (*token == ',')
2340 GetMagickToken(q,&q,token);
cristy3ed852e2009-09-05 21:47:34 +00002341 }
2342 for (p=q; *q != '\0'; )
2343 {
2344 GetMagickToken(q,&q,token);
2345 if (LocaleCompare(token,"pop") != 0)
2346 continue;
2347 GetMagickToken(q,(const char **) NULL,token);
2348 if (LocaleCompare(token,"gradient") != 0)
2349 continue;
2350 break;
2351 }
2352 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2353 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2354 graphic_context[n]->affine.ry*segment.y1+
2355 graphic_context[n]->affine.tx;
2356 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2357 graphic_context[n]->affine.sy*segment.y1+
2358 graphic_context[n]->affine.ty;
2359 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2360 graphic_context[n]->affine.ry*segment.y2+
2361 graphic_context[n]->affine.tx;
2362 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2363 graphic_context[n]->affine.sy*segment.y2+
2364 graphic_context[n]->affine.ty;
cristyb51dff52011-05-19 16:55:47 +00002365 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002366 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002367 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2368 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002369 "%gx%g%+.15g%+.15g",
cristy3ed852e2009-09-05 21:47:34 +00002370 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2371 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2372 bounds.x1,bounds.y1);
2373 (void) SetImageArtifact(image,key,geometry);
2374 GetMagickToken(q,&q,token);
2375 break;
2376 }
2377 if (LocaleCompare("pattern",token) == 0)
2378 {
2379 RectangleInfo
2380 bounds;
2381
2382 GetMagickToken(q,&q,token);
2383 (void) CopyMagickString(name,token,MaxTextExtent);
2384 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002385 bounds.x=(ssize_t) ceil(InterpretLocaleValue(token,
2386 (char **) NULL)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002387 GetMagickToken(q,&q,token);
2388 if (*token == ',')
2389 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002390 bounds.y=(ssize_t) ceil(InterpretLocaleValue(token,
2391 (char **) NULL)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002392 GetMagickToken(q,&q,token);
2393 if (*token == ',')
2394 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002395 bounds.width=(size_t) floor(InterpretLocaleValue(token,
2396 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002397 GetMagickToken(q,&q,token);
2398 if (*token == ',')
2399 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002400 bounds.height=(size_t) floor(InterpretLocaleValue(token,
2401 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002402 for (p=q; *q != '\0'; )
2403 {
2404 GetMagickToken(q,&q,token);
2405 if (LocaleCompare(token,"pop") != 0)
2406 continue;
2407 GetMagickToken(q,(const char **) NULL,token);
2408 if (LocaleCompare(token,"pattern") != 0)
2409 continue;
2410 break;
2411 }
2412 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
cristyb51dff52011-05-19 16:55:47 +00002413 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002414 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002415 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2416 (void) FormatLocaleString(geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002417 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002418 bounds.height,(double) bounds.x,(double) bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00002419 (void) SetImageArtifact(image,key,geometry);
2420 GetMagickToken(q,&q,token);
2421 break;
2422 }
2423 if (LocaleCompare("graphic-context",token) == 0)
2424 {
2425 n++;
2426 graphic_context=(DrawInfo **) ResizeQuantumMemory(
2427 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2428 if (graphic_context == (DrawInfo **) NULL)
2429 {
2430 (void) ThrowMagickException(&image->exception,
2431 GetMagickModule(),ResourceLimitError,
2432 "MemoryAllocationFailed","`%s'",image->filename);
2433 break;
2434 }
2435 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2436 graphic_context[n-1]);
2437 break;
2438 }
2439 if (LocaleCompare("defs",token) == 0)
2440 break;
2441 status=MagickFalse;
2442 break;
2443 }
2444 status=MagickFalse;
2445 break;
2446 }
2447 case 'r':
2448 case 'R':
2449 {
2450 if (LocaleCompare("rectangle",keyword) == 0)
2451 {
2452 primitive_type=RectanglePrimitive;
2453 break;
2454 }
2455 if (LocaleCompare("rotate",keyword) == 0)
2456 {
2457 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002458 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002459 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2460 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2461 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2462 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2463 break;
2464 }
2465 if (LocaleCompare("roundRectangle",keyword) == 0)
2466 {
2467 primitive_type=RoundRectanglePrimitive;
2468 break;
2469 }
2470 status=MagickFalse;
2471 break;
2472 }
2473 case 's':
2474 case 'S':
2475 {
2476 if (LocaleCompare("scale",keyword) == 0)
2477 {
2478 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002479 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002480 GetMagickToken(q,&q,token);
2481 if (*token == ',')
2482 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002483 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002484 break;
2485 }
2486 if (LocaleCompare("skewX",keyword) == 0)
2487 {
2488 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002489 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002490 affine.ry=sin(DegreesToRadians(angle));
2491 break;
2492 }
2493 if (LocaleCompare("skewY",keyword) == 0)
2494 {
2495 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002496 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002497 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2498 break;
2499 }
2500 if (LocaleCompare("stop-color",keyword) == 0)
2501 {
2502 PixelPacket
2503 stop_color;
2504
2505 GetMagickToken(q,&q,token);
2506 (void) QueryColorDatabase(token,&stop_color,&image->exception);
2507 (void) GradientImage(image,LinearGradient,ReflectSpread,
2508 &start_color,&stop_color);
2509 start_color=stop_color;
2510 GetMagickToken(q,&q,token);
2511 break;
2512 }
2513 if (LocaleCompare("stroke",keyword) == 0)
2514 {
2515 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002516 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002517 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2518 (void) DrawPatternPath(image,draw_info,token,
2519 &graphic_context[n]->stroke_pattern);
2520 else
2521 {
2522 status=QueryColorDatabase(token,&graphic_context[n]->stroke,
2523 &image->exception);
2524 if (status == MagickFalse)
2525 {
2526 ImageInfo
2527 *pattern_info;
2528
2529 pattern_info=AcquireImageInfo();
2530 (void) CopyMagickString(pattern_info->filename,token,
2531 MaxTextExtent);
2532 graphic_context[n]->stroke_pattern=
2533 ReadImage(pattern_info,&image->exception);
2534 CatchException(&image->exception);
2535 pattern_info=DestroyImageInfo(pattern_info);
2536 }
2537 }
2538 break;
2539 }
2540 if (LocaleCompare("stroke-antialias",keyword) == 0)
2541 {
2542 GetMagickToken(q,&q,token);
2543 graphic_context[n]->stroke_antialias=
cristyf2f27272009-12-17 14:48:46 +00002544 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002545 break;
2546 }
2547 if (LocaleCompare("stroke-dasharray",keyword) == 0)
2548 {
2549 if (graphic_context[n]->dash_pattern != (double *) NULL)
2550 graphic_context[n]->dash_pattern=(double *)
2551 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2552 if (IsPoint(q) != MagickFalse)
2553 {
2554 const char
2555 *p;
2556
2557 p=q;
2558 GetMagickToken(p,&p,token);
2559 if (*token == ',')
2560 GetMagickToken(p,&p,token);
2561 for (x=0; IsPoint(token) != MagickFalse; x++)
2562 {
2563 GetMagickToken(p,&p,token);
2564 if (*token == ',')
2565 GetMagickToken(p,&p,token);
2566 }
2567 graphic_context[n]->dash_pattern=(double *)
2568 AcquireQuantumMemory((size_t) (2UL*x+1UL),
2569 sizeof(*graphic_context[n]->dash_pattern));
2570 if (graphic_context[n]->dash_pattern == (double *) NULL)
2571 {
2572 (void) ThrowMagickException(&image->exception,
2573 GetMagickModule(),ResourceLimitError,
2574 "MemoryAllocationFailed","`%s'",image->filename);
2575 break;
2576 }
2577 for (j=0; j < x; j++)
2578 {
2579 GetMagickToken(q,&q,token);
2580 if (*token == ',')
2581 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002582 graphic_context[n]->dash_pattern[j]=InterpretLocaleValue(
2583 token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002584 }
2585 if ((x & 0x01) != 0)
2586 for ( ; j < (2*x); j++)
2587 graphic_context[n]->dash_pattern[j]=
2588 graphic_context[n]->dash_pattern[j-x];
2589 graphic_context[n]->dash_pattern[j]=0.0;
2590 break;
2591 }
2592 GetMagickToken(q,&q,token);
2593 break;
2594 }
2595 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2596 {
2597 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002598 graphic_context[n]->dash_offset=InterpretLocaleValue(token,
2599 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002600 break;
2601 }
2602 if (LocaleCompare("stroke-linecap",keyword) == 0)
2603 {
cristybb503372010-05-27 20:51:26 +00002604 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002605 linecap;
2606
2607 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002608 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002609 if (linecap == -1)
2610 {
2611 status=MagickFalse;
2612 break;
2613 }
2614 graphic_context[n]->linecap=(LineCap) linecap;
2615 break;
2616 }
2617 if (LocaleCompare("stroke-linejoin",keyword) == 0)
2618 {
cristybb503372010-05-27 20:51:26 +00002619 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002620 linejoin;
2621
2622 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002623 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002624 if (linejoin == -1)
2625 {
2626 status=MagickFalse;
2627 break;
2628 }
2629 graphic_context[n]->linejoin=(LineJoin) linejoin;
2630 break;
2631 }
2632 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2633 {
2634 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002635 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002636 break;
2637 }
2638 if (LocaleCompare("stroke-opacity",keyword) == 0)
2639 {
2640 GetMagickToken(q,&q,token);
2641 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyce70c172010-01-07 17:15:30 +00002642 graphic_context[n]->stroke.opacity=ClampToQuantum((MagickRealType)
cristyc1acd842011-05-19 23:05:47 +00002643 QuantumRange*(1.0-factor*InterpretLocaleValue(token,
2644 (char **) NULL)));
cristy3ed852e2009-09-05 21:47:34 +00002645 break;
2646 }
2647 if (LocaleCompare("stroke-width",keyword) == 0)
2648 {
2649 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002650 graphic_context[n]->stroke_width=InterpretLocaleValue(token,
2651 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002652 break;
2653 }
2654 status=MagickFalse;
2655 break;
2656 }
2657 case 't':
2658 case 'T':
2659 {
2660 if (LocaleCompare("text",keyword) == 0)
2661 {
2662 primitive_type=TextPrimitive;
2663 break;
2664 }
2665 if (LocaleCompare("text-align",keyword) == 0)
2666 {
cristybb503372010-05-27 20:51:26 +00002667 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002668 align;
2669
2670 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002671 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002672 if (align == -1)
2673 {
2674 status=MagickFalse;
2675 break;
2676 }
2677 graphic_context[n]->align=(AlignType) align;
2678 break;
2679 }
2680 if (LocaleCompare("text-anchor",keyword) == 0)
2681 {
cristybb503372010-05-27 20:51:26 +00002682 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002683 align;
2684
2685 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002686 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002687 if (align == -1)
2688 {
2689 status=MagickFalse;
2690 break;
2691 }
2692 graphic_context[n]->align=(AlignType) align;
2693 break;
2694 }
2695 if (LocaleCompare("text-antialias",keyword) == 0)
2696 {
2697 GetMagickToken(q,&q,token);
2698 graphic_context[n]->text_antialias=
cristyf2f27272009-12-17 14:48:46 +00002699 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002700 break;
2701 }
2702 if (LocaleCompare("text-undercolor",keyword) == 0)
2703 {
2704 GetMagickToken(q,&q,token);
2705 (void) QueryColorDatabase(token,&graphic_context[n]->undercolor,
2706 &image->exception);
2707 break;
2708 }
2709 if (LocaleCompare("translate",keyword) == 0)
2710 {
2711 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002712 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002713 GetMagickToken(q,&q,token);
2714 if (*token == ',')
2715 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002716 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002717 break;
2718 }
2719 status=MagickFalse;
2720 break;
2721 }
2722 case 'v':
2723 case 'V':
2724 {
2725 if (LocaleCompare("viewbox",keyword) == 0)
2726 {
2727 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002728 graphic_context[n]->viewbox.x=(ssize_t) ceil(InterpretLocaleValue(
2729 token,(char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002730 GetMagickToken(q,&q,token);
2731 if (*token == ',')
2732 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002733 graphic_context[n]->viewbox.y=(ssize_t) ceil(InterpretLocaleValue(
2734 token,(char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002735 GetMagickToken(q,&q,token);
2736 if (*token == ',')
2737 GetMagickToken(q,&q,token);
cristybb503372010-05-27 20:51:26 +00002738 graphic_context[n]->viewbox.width=(size_t) floor(
cristyc1acd842011-05-19 23:05:47 +00002739 InterpretLocaleValue(token,(char **) NULL)+0.5);
cristy06609ee2010-03-17 20:21:27 +00002740 GetMagickToken(q,&q,token);
2741 if (*token == ',')
2742 GetMagickToken(q,&q,token);
cristybb503372010-05-27 20:51:26 +00002743 graphic_context[n]->viewbox.height=(size_t) floor(
cristyc1acd842011-05-19 23:05:47 +00002744 InterpretLocaleValue(token,(char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002745 break;
2746 }
2747 status=MagickFalse;
2748 break;
2749 }
2750 default:
2751 {
2752 status=MagickFalse;
2753 break;
2754 }
2755 }
2756 if (status == MagickFalse)
2757 break;
2758 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2759 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2760 {
cristyfbd70092010-12-01 19:31:14 +00002761 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2762 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2763 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2764 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2765 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2766 current.tx;
2767 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2768 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002769 }
2770 if (primitive_type == UndefinedPrimitive)
2771 {
2772 if (image->debug != MagickFalse)
2773 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2774 (int) (q-p),p);
2775 continue;
2776 }
2777 /*
2778 Parse the primitive attributes.
2779 */
2780 i=0;
2781 j=0;
2782 primitive_info[0].point.x=0.0;
2783 primitive_info[0].point.y=0.0;
2784 for (x=0; *q != '\0'; x++)
2785 {
2786 /*
2787 Define points.
2788 */
2789 if (IsPoint(q) == MagickFalse)
2790 break;
2791 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002792 point.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002793 GetMagickToken(q,&q,token);
2794 if (*token == ',')
2795 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002796 point.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002797 GetMagickToken(q,(const char **) NULL,token);
2798 if (*token == ',')
2799 GetMagickToken(q,&q,token);
2800 primitive_info[i].primitive=primitive_type;
2801 primitive_info[i].point=point;
2802 primitive_info[i].coordinates=0;
2803 primitive_info[i].method=FloodfillMethod;
2804 i++;
cristybb503372010-05-27 20:51:26 +00002805 if (i < (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +00002806 continue;
2807 number_points<<=1;
2808 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2809 (size_t) number_points,sizeof(*primitive_info));
2810 if (primitive_info == (PrimitiveInfo *) NULL)
2811 {
2812 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2813 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2814 break;
2815 }
2816 }
2817 primitive_info[j].primitive=primitive_type;
cristybb503372010-05-27 20:51:26 +00002818 primitive_info[j].coordinates=(size_t) x;
cristy3ed852e2009-09-05 21:47:34 +00002819 primitive_info[j].method=FloodfillMethod;
2820 primitive_info[j].text=(char *) NULL;
2821 /*
2822 Circumscribe primitive within a circle.
2823 */
2824 bounds.x1=primitive_info[j].point.x;
2825 bounds.y1=primitive_info[j].point.y;
2826 bounds.x2=primitive_info[j].point.x;
2827 bounds.y2=primitive_info[j].point.y;
cristybb503372010-05-27 20:51:26 +00002828 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 point=primitive_info[j+k].point;
2831 if (point.x < bounds.x1)
2832 bounds.x1=point.x;
2833 if (point.y < bounds.y1)
2834 bounds.y1=point.y;
2835 if (point.x > bounds.x2)
2836 bounds.x2=point.x;
2837 if (point.y > bounds.y2)
2838 bounds.y2=point.y;
2839 }
2840 /*
2841 Speculate how many points our primitive might consume.
2842 */
2843 length=primitive_info[j].coordinates;
2844 switch (primitive_type)
2845 {
2846 case RectanglePrimitive:
2847 {
2848 length*=5;
2849 break;
2850 }
2851 case RoundRectanglePrimitive:
2852 {
cristy78817ad2010-05-07 12:25:34 +00002853 length*=5+8*BezierQuantum;
cristy3ed852e2009-09-05 21:47:34 +00002854 break;
2855 }
2856 case BezierPrimitive:
2857 {
2858 if (primitive_info[j].coordinates > 107)
2859 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2860 DrawError,"TooManyBezierCoordinates","`%s'",token);
2861 length=BezierQuantum*primitive_info[j].coordinates;
2862 break;
2863 }
2864 case PathPrimitive:
2865 {
2866 char
2867 *s,
2868 *t;
2869
2870 GetMagickToken(q,&q,token);
cristy40a08ad2010-02-09 02:27:44 +00002871 length=1;
cristy3ed852e2009-09-05 21:47:34 +00002872 t=token;
2873 for (s=token; *s != '\0'; s=t)
2874 {
cristy00976d82011-02-20 20:31:28 +00002875 double
2876 value;
2877
cristyc1acd842011-05-19 23:05:47 +00002878 value=InterpretLocaleValue(s,&t);
cristy00976d82011-02-20 20:31:28 +00002879 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00002880 if (s == t)
2881 {
2882 t++;
2883 continue;
2884 }
cristyef7a6ce2011-05-14 15:01:11 +00002885 length++;
cristy3ed852e2009-09-05 21:47:34 +00002886 }
cristyef7a6ce2011-05-14 15:01:11 +00002887 length=3*length/2+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002888 break;
2889 }
2890 case CirclePrimitive:
2891 case ArcPrimitive:
2892 case EllipsePrimitive:
2893 {
2894 MagickRealType
2895 alpha,
2896 beta,
2897 radius;
2898
2899 alpha=bounds.x2-bounds.x1;
2900 beta=bounds.y2-bounds.y1;
2901 radius=hypot((double) alpha,(double) beta);
cristyf53f26f2011-05-16 00:56:05 +00002902 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002903 break;
2904 }
2905 default:
2906 break;
2907 }
cristybb503372010-05-27 20:51:26 +00002908 if ((size_t) (i+length) >= number_points)
cristy3ed852e2009-09-05 21:47:34 +00002909 {
2910 /*
2911 Resize based on speculative points required by primitive.
2912 */
cristy9ce61b92010-05-12 16:30:26 +00002913 number_points+=length+1;
cristy3ed852e2009-09-05 21:47:34 +00002914 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2915 (size_t) number_points,sizeof(*primitive_info));
2916 if (primitive_info == (PrimitiveInfo *) NULL)
2917 {
2918 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2919 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2920 image->filename);
2921 break;
2922 }
2923 }
2924 switch (primitive_type)
2925 {
2926 case PointPrimitive:
2927 default:
2928 {
2929 if (primitive_info[j].coordinates != 1)
2930 {
2931 status=MagickFalse;
2932 break;
2933 }
2934 TracePoint(primitive_info+j,primitive_info[j].point);
cristybb503372010-05-27 20:51:26 +00002935 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002936 break;
2937 }
2938 case LinePrimitive:
2939 {
2940 if (primitive_info[j].coordinates != 2)
2941 {
2942 status=MagickFalse;
2943 break;
2944 }
2945 TraceLine(primitive_info+j,primitive_info[j].point,
2946 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002947 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002948 break;
2949 }
2950 case RectanglePrimitive:
2951 {
2952 if (primitive_info[j].coordinates != 2)
2953 {
2954 status=MagickFalse;
2955 break;
2956 }
2957 TraceRectangle(primitive_info+j,primitive_info[j].point,
2958 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002959 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002960 break;
2961 }
2962 case RoundRectanglePrimitive:
2963 {
2964 if (primitive_info[j].coordinates != 3)
2965 {
2966 status=MagickFalse;
2967 break;
2968 }
2969 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
2970 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002971 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002972 break;
2973 }
2974 case ArcPrimitive:
2975 {
2976 if (primitive_info[j].coordinates != 3)
2977 {
2978 primitive_type=UndefinedPrimitive;
2979 break;
2980 }
2981 TraceArc(primitive_info+j,primitive_info[j].point,
2982 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002983 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002984 break;
2985 }
2986 case EllipsePrimitive:
2987 {
2988 if (primitive_info[j].coordinates != 3)
2989 {
2990 status=MagickFalse;
2991 break;
2992 }
2993 TraceEllipse(primitive_info+j,primitive_info[j].point,
2994 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002995 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002996 break;
2997 }
2998 case CirclePrimitive:
2999 {
3000 if (primitive_info[j].coordinates != 2)
3001 {
3002 status=MagickFalse;
3003 break;
3004 }
3005 TraceCircle(primitive_info+j,primitive_info[j].point,
3006 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00003007 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00003008 break;
3009 }
3010 case PolylinePrimitive:
3011 break;
3012 case PolygonPrimitive:
3013 {
3014 primitive_info[i]=primitive_info[j];
3015 primitive_info[i].coordinates=0;
3016 primitive_info[j].coordinates++;
3017 i++;
3018 break;
3019 }
3020 case BezierPrimitive:
3021 {
3022 if (primitive_info[j].coordinates < 3)
3023 {
3024 status=MagickFalse;
3025 break;
3026 }
3027 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
cristybb503372010-05-27 20:51:26 +00003028 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00003029 break;
3030 }
3031 case PathPrimitive:
3032 {
cristybb503372010-05-27 20:51:26 +00003033 i=(ssize_t) (j+TracePath(primitive_info+j,token));
cristy3ed852e2009-09-05 21:47:34 +00003034 break;
3035 }
3036 case ColorPrimitive:
3037 case MattePrimitive:
3038 {
cristybb503372010-05-27 20:51:26 +00003039 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003040 method;
3041
3042 if (primitive_info[j].coordinates != 1)
3043 {
3044 status=MagickFalse;
3045 break;
3046 }
3047 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00003048 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00003049 if (method == -1)
3050 {
3051 status=MagickFalse;
3052 break;
3053 }
3054 primitive_info[j].method=(PaintMethod) method;
3055 break;
3056 }
3057 case TextPrimitive:
3058 {
3059 if (primitive_info[j].coordinates != 1)
3060 {
3061 status=MagickFalse;
3062 break;
3063 }
3064 if (*token != ',')
3065 GetMagickToken(q,&q,token);
3066 primitive_info[j].text=AcquireString(token);
3067 break;
3068 }
3069 case ImagePrimitive:
3070 {
3071 if (primitive_info[j].coordinates != 2)
3072 {
3073 status=MagickFalse;
3074 break;
3075 }
3076 GetMagickToken(q,&q,token);
3077 primitive_info[j].text=AcquireString(token);
3078 break;
3079 }
3080 }
3081 if (primitive_info == (PrimitiveInfo *) NULL)
3082 break;
3083 if (image->debug != MagickFalse)
3084 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3085 if (status == MagickFalse)
3086 break;
3087 primitive_info[i].primitive=UndefinedPrimitive;
3088 if (i == 0)
3089 continue;
3090 /*
3091 Transform points.
3092 */
3093 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3094 {
3095 point=primitive_info[i].point;
3096 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3097 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3098 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3099 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3100 point=primitive_info[i].point;
3101 if (point.x < graphic_context[n]->bounds.x1)
3102 graphic_context[n]->bounds.x1=point.x;
3103 if (point.y < graphic_context[n]->bounds.y1)
3104 graphic_context[n]->bounds.y1=point.y;
3105 if (point.x > graphic_context[n]->bounds.x2)
3106 graphic_context[n]->bounds.x2=point.x;
3107 if (point.y > graphic_context[n]->bounds.y2)
3108 graphic_context[n]->bounds.y2=point.y;
3109 if (primitive_info[i].primitive == ImagePrimitive)
3110 break;
cristybb503372010-05-27 20:51:26 +00003111 if (i >= (ssize_t) number_points)
cristy9ce61b92010-05-12 16:30:26 +00003112 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00003113 }
cristy3ed852e2009-09-05 21:47:34 +00003114 if (graphic_context[n]->render != MagickFalse)
3115 {
3116 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3117 (LocaleCompare(graphic_context[n]->clip_mask,
3118 graphic_context[n-1]->clip_mask) != 0))
3119 (void) DrawClipPath(image,graphic_context[n],
3120 graphic_context[n]->clip_mask);
3121 (void) DrawPrimitive(image,graphic_context[n],primitive_info);
3122 }
3123 if (primitive_info->text != (char *) NULL)
3124 primitive_info->text=(char *) RelinquishMagickMemory(
3125 primitive_info->text);
3126 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3127 primitive_extent);
3128 if (proceed == MagickFalse)
3129 break;
3130 }
3131 if (image->debug != MagickFalse)
3132 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3133 /*
3134 Relinquish resources.
3135 */
3136 token=DestroyString(token);
3137 if (primitive_info != (PrimitiveInfo *) NULL)
3138 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3139 primitive=DestroyString(primitive);
3140 for ( ; n >= 0; n--)
3141 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3142 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3143 if (status == MagickFalse)
3144 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3145 keyword);
3146 return(status);
3147}
3148
3149/*
3150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3151% %
3152% %
3153% %
3154% D r a w G r a d i e n t I m a g e %
3155% %
3156% %
3157% %
3158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3159%
3160% DrawGradientImage() draws a linear gradient on the image.
3161%
3162% The format of the DrawGradientImage method is:
3163%
3164% MagickBooleanType DrawGradientImage(Image *image,
3165% const DrawInfo *draw_info)
3166%
3167% A description of each parameter follows:
3168%
3169% o image: the image.
3170%
3171% o _info: the draw info.
3172%
3173*/
3174
3175static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
cristybb503372010-05-27 20:51:26 +00003176 const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00003177{
3178 switch (gradient->type)
3179 {
3180 case UndefinedGradient:
3181 case LinearGradient:
3182 {
3183 MagickRealType
3184 gamma,
3185 length,
3186 offset,
3187 scale;
3188
3189 PointInfo
3190 p,
3191 q;
3192
3193 const SegmentInfo
3194 *gradient_vector;
3195
3196 gradient_vector=(&gradient->gradient_vector);
3197 p.x=gradient_vector->x2-gradient_vector->x1;
3198 p.y=gradient_vector->y2-gradient_vector->y1;
3199 q.x=(double) x-gradient_vector->x1;
3200 q.y=(double) y-gradient_vector->y1;
3201 length=sqrt(q.x*q.x+q.y*q.y);
3202 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
3203 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
3204 scale=p.x*q.x+p.y*q.y;
3205 offset=gamma*scale*length;
3206 return(offset);
3207 }
3208 case RadialGradient:
3209 {
3210 MagickRealType
3211 length,
3212 offset;
3213
3214 PointInfo
3215 v;
3216
3217 v.x=(double) x-gradient->center.x;
3218 v.y=(double) y-gradient->center.y;
3219 length=sqrt(v.x*v.x+v.y*v.y);
3220 if (gradient->spread == RepeatSpread)
3221 return(length);
3222 offset=length/gradient->radius;
3223 return(offset);
3224 }
3225 }
3226 return(0.0);
3227}
3228
3229MagickExport MagickBooleanType DrawGradientImage(Image *image,
3230 const DrawInfo *draw_info)
3231{
cristyc4c8d132010-01-07 01:58:38 +00003232 CacheView
3233 *image_view;
3234
cristy3ed852e2009-09-05 21:47:34 +00003235 const GradientInfo
3236 *gradient;
3237
3238 const SegmentInfo
3239 *gradient_vector;
3240
3241 ExceptionInfo
3242 *exception;
3243
cristy3ed852e2009-09-05 21:47:34 +00003244 MagickBooleanType
3245 status;
3246
3247 MagickPixelPacket
3248 zero;
3249
3250 MagickRealType
3251 length;
3252
3253 PointInfo
3254 point;
3255
3256 RectangleInfo
3257 bounding_box;
3258
cristy826a5472010-08-31 23:21:38 +00003259 ssize_t
3260 y;
3261
cristy3ed852e2009-09-05 21:47:34 +00003262 /*
3263 Draw linear or radial gradient on image.
3264 */
3265 assert(image != (Image *) NULL);
3266 assert(image->signature == MagickSignature);
3267 if (image->debug != MagickFalse)
3268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3269 assert(draw_info != (const DrawInfo *) NULL);
3270 gradient=(&draw_info->gradient);
3271 gradient_vector=(&gradient->gradient_vector);
3272 point.x=gradient_vector->x2-gradient_vector->x1;
3273 point.y=gradient_vector->y2-gradient_vector->y1;
3274 length=sqrt(point.x*point.x+point.y*point.y);
3275 bounding_box=gradient->bounding_box;
3276 status=MagickTrue;
3277 exception=(&image->exception);
3278 GetMagickPixelPacket(image,&zero);
3279 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003280#if defined(MAGICKCORE_OPENMP_SUPPORT)
3281 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003282#endif
cristybb503372010-05-27 20:51:26 +00003283 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00003284 {
cristy3ed852e2009-09-05 21:47:34 +00003285 MagickPixelPacket
3286 composite,
3287 pixel;
3288
3289 MagickRealType
3290 alpha,
3291 offset;
3292
3293 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003294 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00003295
cristybb503372010-05-27 20:51:26 +00003296 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003297 i,
3298 x;
3299
3300 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003301 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003302
cristy826a5472010-08-31 23:21:38 +00003303 ssize_t
3304 j;
3305
cristy3ed852e2009-09-05 21:47:34 +00003306 if (status == MagickFalse)
3307 continue;
3308 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3309 if (q == (PixelPacket *) NULL)
3310 {
3311 status=MagickFalse;
3312 continue;
3313 }
3314 indexes=GetCacheViewAuthenticIndexQueue(image_view);
3315 pixel=zero;
3316 composite=zero;
3317 offset=GetStopColorOffset(gradient,0,y);
3318 if (gradient->type != RadialGradient)
3319 offset/=length;
cristybb503372010-05-27 20:51:26 +00003320 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
cristy3ed852e2009-09-05 21:47:34 +00003321 {
3322 SetMagickPixelPacket(image,q,indexes+x,&pixel);
3323 switch (gradient->spread)
3324 {
3325 case UndefinedSpread:
3326 case PadSpread:
3327 {
cristybb503372010-05-27 20:51:26 +00003328 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3329 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003330 {
3331 offset=GetStopColorOffset(gradient,x,y);
3332 if (gradient->type != RadialGradient)
3333 offset/=length;
3334 }
cristybb503372010-05-27 20:51:26 +00003335 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003336 if (offset < gradient->stops[i].offset)
3337 break;
3338 if ((offset < 0.0) || (i == 0))
3339 composite=gradient->stops[0].color;
3340 else
cristybb503372010-05-27 20:51:26 +00003341 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
cristy3ed852e2009-09-05 21:47:34 +00003342 composite=gradient->stops[gradient->number_stops-1].color;
3343 else
3344 {
3345 j=i;
3346 i--;
3347 alpha=(offset-gradient->stops[i].offset)/
3348 (gradient->stops[j].offset-gradient->stops[i].offset);
3349 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3350 &gradient->stops[j].color,alpha,&composite);
3351 }
3352 break;
3353 }
3354 case ReflectSpread:
3355 {
cristybb503372010-05-27 20:51:26 +00003356 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3357 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003358 {
3359 offset=GetStopColorOffset(gradient,x,y);
3360 if (gradient->type != RadialGradient)
3361 offset/=length;
3362 }
3363 if (offset < 0.0)
3364 offset=(-offset);
cristybb503372010-05-27 20:51:26 +00003365 if ((ssize_t) fmod(offset,2.0) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003366 offset=fmod(offset,1.0);
3367 else
3368 offset=1.0-fmod(offset,1.0);
cristybb503372010-05-27 20:51:26 +00003369 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003370 if (offset < gradient->stops[i].offset)
3371 break;
3372 if (i == 0)
3373 composite=gradient->stops[0].color;
3374 else
cristybb503372010-05-27 20:51:26 +00003375 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003376 composite=gradient->stops[gradient->number_stops-1].color;
3377 else
3378 {
3379 j=i;
3380 i--;
3381 alpha=(offset-gradient->stops[i].offset)/
3382 (gradient->stops[j].offset-gradient->stops[i].offset);
3383 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3384 &gradient->stops[j].color,alpha,&composite);
3385 }
3386 break;
3387 }
3388 case RepeatSpread:
3389 {
3390 MagickBooleanType
3391 antialias;
3392
3393 MagickRealType
3394 repeat;
3395
3396 antialias=MagickFalse;
3397 repeat=0.0;
cristybb503372010-05-27 20:51:26 +00003398 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3399 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003400 {
3401 offset=GetStopColorOffset(gradient,x,y);
3402 if (gradient->type == LinearGradient)
3403 {
3404 repeat=fmod(offset,length);
3405 if (repeat < 0.0)
3406 repeat=length-fmod(-repeat,length);
3407 else
3408 repeat=fmod(offset,length);
3409 antialias=(repeat < length) && ((repeat+1.0) > length) ?
3410 MagickTrue : MagickFalse;
3411 offset=repeat/length;
3412 }
3413 else
3414 {
3415 repeat=fmod(offset,gradient->radius);
3416 if (repeat < 0.0)
3417 repeat=gradient->radius-fmod(-repeat,gradient->radius);
3418 else
3419 repeat=fmod(offset,gradient->radius);
3420 antialias=repeat+1.0 > gradient->radius ?
3421 MagickTrue : MagickFalse;
3422 offset=repeat/gradient->radius;
3423 }
3424 }
cristybb503372010-05-27 20:51:26 +00003425 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003426 if (offset < gradient->stops[i].offset)
3427 break;
3428 if (i == 0)
3429 composite=gradient->stops[0].color;
3430 else
cristybb503372010-05-27 20:51:26 +00003431 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003432 composite=gradient->stops[gradient->number_stops-1].color;
3433 else
3434 {
3435 j=i;
3436 i--;
3437 alpha=(offset-gradient->stops[i].offset)/
3438 (gradient->stops[j].offset-gradient->stops[i].offset);
3439 if (antialias != MagickFalse)
3440 {
3441 if (gradient->type == LinearGradient)
3442 alpha=length-repeat;
3443 else
3444 alpha=gradient->radius-repeat;
3445 i=0;
cristybb503372010-05-27 20:51:26 +00003446 j=(ssize_t) gradient->number_stops-1L;
cristy3ed852e2009-09-05 21:47:34 +00003447 }
3448 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
3449 &gradient->stops[j].color,alpha,&composite);
3450 }
3451 break;
3452 }
3453 }
3454 MagickPixelCompositeOver(&composite,composite.opacity,&pixel,
3455 pixel.opacity,&pixel);
3456 SetPixelPacket(image,&pixel,q,indexes+x);
3457 q++;
3458 }
3459 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3460 status=MagickFalse;
3461 }
3462 image_view=DestroyCacheView(image_view);
3463 return(status);
3464}
3465
3466/*
3467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3468% %
3469% %
3470% %
3471% D r a w P a t t e r n P a t h %
3472% %
3473% %
3474% %
3475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3476%
3477% DrawPatternPath() draws a pattern.
3478%
3479% The format of the DrawPatternPath method is:
3480%
3481% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
3482% const char *name,Image **pattern)
3483%
3484% A description of each parameter follows:
3485%
3486% o image: the image.
3487%
3488% o draw_info: the draw info.
3489%
3490% o name: the pattern name.
3491%
3492% o image: the image.
3493%
3494*/
3495MagickExport MagickBooleanType DrawPatternPath(Image *image,
3496 const DrawInfo *draw_info,const char *name,Image **pattern)
3497{
3498 char
3499 property[MaxTextExtent];
3500
3501 const char
3502 *geometry,
3503 *path;
3504
3505 DrawInfo
3506 *clone_info;
3507
3508 ImageInfo
3509 *image_info;
3510
3511 MagickBooleanType
3512 status;
3513
3514 assert(image != (Image *) NULL);
3515 assert(image->signature == MagickSignature);
3516 if (image->debug != MagickFalse)
3517 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3518 assert(draw_info != (const DrawInfo *) NULL);
3519 assert(name != (const char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00003520 (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00003521 path=GetImageArtifact(image,property);
3522 if (path == (const char *) NULL)
3523 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00003524 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
cristy3ed852e2009-09-05 21:47:34 +00003525 geometry=GetImageArtifact(image,property);
3526 if (geometry == (const char *) NULL)
3527 return(MagickFalse);
3528 if ((*pattern) != (Image *) NULL)
3529 *pattern=DestroyImage(*pattern);
3530 image_info=AcquireImageInfo();
3531 image_info->size=AcquireString(geometry);
3532 *pattern=AcquireImage(image_info);
3533 image_info=DestroyImageInfo(image_info);
3534 (void) QueryColorDatabase("#00000000",&(*pattern)->background_color,
3535 &image->exception);
3536 (void) SetImageBackgroundColor(*pattern);
3537 if (image->debug != MagickFalse)
3538 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3539 "begin pattern-path %s %s",name,geometry);
3540 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3541 clone_info->fill_pattern=NewImageList();
3542 clone_info->stroke_pattern=NewImageList();
3543 (void) CloneString(&clone_info->primitive,path);
3544 status=DrawImage(*pattern,clone_info);
3545 clone_info=DestroyDrawInfo(clone_info);
3546 if (image->debug != MagickFalse)
3547 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3548 return(status);
3549}
3550
3551/*
3552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3553% %
3554% %
3555% %
3556+ D r a w P o l y g o n P r i m i t i v e %
3557% %
3558% %
3559% %
3560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3561%
3562% DrawPolygonPrimitive() draws a polygon on the image.
3563%
3564% The format of the DrawPolygonPrimitive method is:
3565%
3566% MagickBooleanType DrawPolygonPrimitive(Image *image,
3567% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3568%
3569% A description of each parameter follows:
3570%
3571% o image: the image.
3572%
3573% o draw_info: the draw info.
3574%
3575% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3576%
3577*/
3578
3579static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
3580{
cristybb503372010-05-27 20:51:26 +00003581 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003582 i;
3583
3584 assert(polygon_info != (PolygonInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00003585 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00003586 if (polygon_info[i] != (PolygonInfo *) NULL)
3587 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
cristyb41ee102010-10-04 16:46:15 +00003588 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
cristy3ed852e2009-09-05 21:47:34 +00003589 return(polygon_info);
3590}
3591
3592static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
3593 const PrimitiveInfo *primitive_info)
3594{
3595 PathInfo
cristyfa112112010-01-04 17:48:07 +00003596 *restrict path_info;
cristy3ed852e2009-09-05 21:47:34 +00003597
cristy3ed852e2009-09-05 21:47:34 +00003598 PolygonInfo
3599 **polygon_info;
3600
cristy826a5472010-08-31 23:21:38 +00003601 register ssize_t
3602 i;
3603
cristybb503372010-05-27 20:51:26 +00003604 size_t
cristy3ed852e2009-09-05 21:47:34 +00003605 number_threads;
3606
3607 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00003608 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00003609 sizeof(*polygon_info));
3610 if (polygon_info == (PolygonInfo **) NULL)
3611 return((PolygonInfo **) NULL);
3612 (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()*
3613 sizeof(*polygon_info));
3614 path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
3615 if (path_info == (PathInfo *) NULL)
3616 return(DestroyPolygonThreadSet(polygon_info));
cristybb503372010-05-27 20:51:26 +00003617 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003618 {
3619 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
3620 if (polygon_info[i] == (PolygonInfo *) NULL)
3621 return(DestroyPolygonThreadSet(polygon_info));
3622 }
3623 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
3624 return(polygon_info);
3625}
3626
3627static MagickRealType GetPixelOpacity(PolygonInfo *polygon_info,
3628 const MagickRealType mid,const MagickBooleanType fill,
cristy77f38fb2010-04-22 15:51:47 +00003629 const FillRule fill_rule,const double x,const double y,
cristy3ed852e2009-09-05 21:47:34 +00003630 MagickRealType *stroke_opacity)
3631{
cristy3ed852e2009-09-05 21:47:34 +00003632 MagickRealType
cristyb32b90a2009-09-07 21:45:48 +00003633 alpha,
3634 beta,
cristy3ed852e2009-09-05 21:47:34 +00003635 distance,
cristy3ed852e2009-09-05 21:47:34 +00003636 subpath_opacity;
3637
3638 PointInfo
cristyb32b90a2009-09-07 21:45:48 +00003639 delta;
cristy3ed852e2009-09-05 21:47:34 +00003640
3641 register EdgeInfo
3642 *p;
3643
cristyb32b90a2009-09-07 21:45:48 +00003644 register const PointInfo
3645 *q;
3646
cristybb503372010-05-27 20:51:26 +00003647 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003648 i;
3649
cristycee97112010-05-28 00:44:52 +00003650 ssize_t
3651 j,
3652 winding_number;
3653
cristy3ed852e2009-09-05 21:47:34 +00003654 /*
3655 Compute fill & stroke opacity for this (x,y) point.
3656 */
3657 *stroke_opacity=0.0;
3658 subpath_opacity=0.0;
cristy3ed852e2009-09-05 21:47:34 +00003659 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003660 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristy3ed852e2009-09-05 21:47:34 +00003661 {
cristyb32b90a2009-09-07 21:45:48 +00003662 if (y <= (p->bounds.y1-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003663 break;
cristyb32b90a2009-09-07 21:45:48 +00003664 if (y > (p->bounds.y2+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003665 {
cristybb503372010-05-27 20:51:26 +00003666 (void) DestroyEdge(polygon_info,(size_t) j);
cristy3ed852e2009-09-05 21:47:34 +00003667 continue;
3668 }
cristyb32b90a2009-09-07 21:45:48 +00003669 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003670 continue;
cristybb503372010-05-27 20:51:26 +00003671 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3672 for ( ; i < (ssize_t) p->number_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00003673 {
cristyb32b90a2009-09-07 21:45:48 +00003674 if (y <= (p->points[i-1].y-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003675 break;
cristyb32b90a2009-09-07 21:45:48 +00003676 if (y > (p->points[i].y+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003677 continue;
cristyb32b90a2009-09-07 21:45:48 +00003678 if (p->scanline != y)
cristy3ed852e2009-09-05 21:47:34 +00003679 {
cristyb32b90a2009-09-07 21:45:48 +00003680 p->scanline=y;
cristybb503372010-05-27 20:51:26 +00003681 p->highwater=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00003682 }
3683 /*
3684 Compute distance between a point and an edge.
3685 */
cristyb32b90a2009-09-07 21:45:48 +00003686 q=p->points+i-1;
3687 delta.x=(q+1)->x-q->x;
3688 delta.y=(q+1)->y-q->y;
3689 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
cristy3ed852e2009-09-05 21:47:34 +00003690 if (beta < 0.0)
3691 {
cristyb32b90a2009-09-07 21:45:48 +00003692 delta.x=x-q->x;
3693 delta.y=y-q->y;
cristy3ed852e2009-09-05 21:47:34 +00003694 distance=delta.x*delta.x+delta.y*delta.y;
3695 }
3696 else
3697 {
3698 alpha=delta.x*delta.x+delta.y*delta.y;
3699 if (beta > alpha)
3700 {
cristyb32b90a2009-09-07 21:45:48 +00003701 delta.x=x-(q+1)->x;
3702 delta.y=y-(q+1)->y;
cristy3ed852e2009-09-05 21:47:34 +00003703 distance=delta.x*delta.x+delta.y*delta.y;
3704 }
3705 else
3706 {
cristyb32b90a2009-09-07 21:45:48 +00003707 alpha=1.0/alpha;
3708 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
3709 distance=alpha*beta*beta;
cristy3ed852e2009-09-05 21:47:34 +00003710 }
3711 }
3712 /*
3713 Compute stroke & subpath opacity.
3714 */
3715 beta=0.0;
3716 if (p->ghostline == MagickFalse)
3717 {
cristyb32b90a2009-09-07 21:45:48 +00003718 alpha=mid+0.5;
cristy3ed852e2009-09-05 21:47:34 +00003719 if ((*stroke_opacity < 1.0) &&
3720 (distance <= ((alpha+0.25)*(alpha+0.25))))
3721 {
3722 alpha=mid-0.5;
3723 if (distance <= ((alpha+0.25)*(alpha+0.25)))
3724 *stroke_opacity=1.0;
3725 else
3726 {
3727 beta=1.0;
3728 if (distance != 1.0)
3729 beta=sqrt((double) distance);
cristyb32b90a2009-09-07 21:45:48 +00003730 alpha=beta-mid-0.5;
cristy3ed852e2009-09-05 21:47:34 +00003731 if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
3732 *stroke_opacity=(alpha-0.25)*(alpha-0.25);
3733 }
3734 }
3735 }
3736 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_opacity >= 1.0))
3737 continue;
3738 if (distance <= 0.0)
3739 {
3740 subpath_opacity=1.0;
3741 continue;
3742 }
3743 if (distance > 1.0)
3744 continue;
3745 if (beta == 0.0)
3746 {
3747 beta=1.0;
3748 if (distance != 1.0)
cristyb32b90a2009-09-07 21:45:48 +00003749 beta=sqrt(distance);
cristy3ed852e2009-09-05 21:47:34 +00003750 }
3751 alpha=beta-1.0;
cristyb32b90a2009-09-07 21:45:48 +00003752 if (subpath_opacity < (alpha*alpha))
cristy3ed852e2009-09-05 21:47:34 +00003753 subpath_opacity=alpha*alpha;
3754 }
cristy3ed852e2009-09-05 21:47:34 +00003755 }
3756 /*
3757 Compute fill opacity.
3758 */
3759 if (fill == MagickFalse)
3760 return(0.0);
3761 if (subpath_opacity >= 1.0)
3762 return(1.0);
cristyb32b90a2009-09-07 21:45:48 +00003763 /*
3764 Determine winding number.
3765 */
3766 winding_number=0;
3767 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003768 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristyb32b90a2009-09-07 21:45:48 +00003769 {
3770 if (y <= p->bounds.y1)
3771 break;
3772 if ((y > p->bounds.y2) || (x <= p->bounds.x1))
3773 continue;
3774 if (x > p->bounds.x2)
3775 {
3776 winding_number+=p->direction ? 1 : -1;
3777 continue;
3778 }
cristybb503372010-05-27 20:51:26 +00003779 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3780 for ( ; i < (ssize_t) p->number_points; i++)
cristyb32b90a2009-09-07 21:45:48 +00003781 if (y <= p->points[i].y)
3782 break;
3783 q=p->points+i-1;
3784 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
3785 winding_number+=p->direction ? 1 : -1;
3786 }
cristy3ed852e2009-09-05 21:47:34 +00003787 if (fill_rule != NonZeroRule)
3788 {
3789 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
3790 return(1.0);
3791 }
3792 else
3793 if (MagickAbsoluteValue(winding_number) != 0)
3794 return(1.0);
3795 return(subpath_opacity);
3796}
3797
3798static MagickBooleanType DrawPolygonPrimitive(Image *image,
3799 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3800{
cristyfa112112010-01-04 17:48:07 +00003801 CacheView
3802 *image_view;
3803
cristy3ed852e2009-09-05 21:47:34 +00003804 ExceptionInfo
3805 *exception;
3806
cristy3ed852e2009-09-05 21:47:34 +00003807 MagickBooleanType
3808 fill,
3809 status;
3810
3811 MagickRealType
3812 mid;
3813
3814 PolygonInfo
cristyfa112112010-01-04 17:48:07 +00003815 **restrict polygon_info;
cristy3ed852e2009-09-05 21:47:34 +00003816
3817 register EdgeInfo
3818 *p;
3819
cristybb503372010-05-27 20:51:26 +00003820 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003821 i;
3822
3823 SegmentInfo
3824 bounds;
3825
cristy826a5472010-08-31 23:21:38 +00003826 ssize_t
3827 start,
3828 stop,
3829 y;
3830
cristy3ed852e2009-09-05 21:47:34 +00003831 /*
3832 Compute bounding box.
3833 */
3834 assert(image != (Image *) NULL);
3835 assert(image->signature == MagickSignature);
3836 if (image->debug != MagickFalse)
3837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3838 assert(draw_info != (DrawInfo *) NULL);
3839 assert(draw_info->signature == MagickSignature);
3840 assert(primitive_info != (PrimitiveInfo *) NULL);
3841 if (primitive_info->coordinates == 0)
3842 return(MagickTrue);
3843 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3844 if (polygon_info == (PolygonInfo **) NULL)
3845 return(MagickFalse);
3846 if (0)
3847 DrawBoundingRectangles(image,draw_info,polygon_info[0]);
3848 if (image->debug != MagickFalse)
3849 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3850 fill=(primitive_info->method == FillToBorderMethod) ||
3851 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3852 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3853 bounds=polygon_info[0]->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00003854 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00003855 {
3856 p=polygon_info[0]->edges+i;
3857 if (p->bounds.x1 < bounds.x1)
3858 bounds.x1=p->bounds.x1;
3859 if (p->bounds.y1 < bounds.y1)
3860 bounds.y1=p->bounds.y1;
3861 if (p->bounds.x2 > bounds.x2)
3862 bounds.x2=p->bounds.x2;
3863 if (p->bounds.y2 > bounds.y2)
3864 bounds.y2=p->bounds.y2;
3865 }
3866 bounds.x1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003867 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003868 image->columns ? (double) image->columns-1.0 : bounds.x1;
3869 bounds.y1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003870 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003871 image->rows ? (double) image->rows-1.0 : bounds.y1;
3872 bounds.x2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003873 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003874 image->columns ? (double) image->columns-1.0 : bounds.x2;
3875 bounds.y2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003876 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003877 image->rows ? (double) image->rows-1.0 : bounds.y2;
3878 status=MagickTrue;
3879 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00003880 start=(ssize_t) ceil(bounds.x1-0.5);
3881 stop=(ssize_t) floor(bounds.x2+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003882 image_view=AcquireCacheView(image);
3883 if (primitive_info->coordinates == 1)
3884 {
3885 /*
3886 Draw point.
3887 */
cristyb5d5f722009-11-04 03:03:49 +00003888#if defined(MAGICKCORE_OPENMP_SUPPORT)
3889 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003890#endif
cristybb503372010-05-27 20:51:26 +00003891 for (y=(ssize_t) ceil(bounds.y1-0.5); y <= (ssize_t) floor(bounds.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00003892 {
3893 MagickBooleanType
3894 sync;
3895
cristybb503372010-05-27 20:51:26 +00003896 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003897 x;
3898
3899 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003900 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003901
3902 if (status == MagickFalse)
3903 continue;
3904 x=start;
cristybb503372010-05-27 20:51:26 +00003905 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),
cristy3ed852e2009-09-05 21:47:34 +00003906 1,exception);
3907 if (q == (PixelPacket *) NULL)
3908 {
3909 status=MagickFalse;
3910 continue;
3911 }
3912 for ( ; x <= stop; x++)
3913 {
cristybb503372010-05-27 20:51:26 +00003914 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3915 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003916 (void) GetStrokeColor(draw_info,x,y,q);
3917 q++;
3918 }
3919 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3920 if (sync == MagickFalse)
3921 status=MagickFalse;
3922 }
3923 image_view=DestroyCacheView(image_view);
3924 polygon_info=DestroyPolygonThreadSet(polygon_info);
3925 if (image->debug != MagickFalse)
3926 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3927 " end draw-polygon");
3928 return(status);
3929 }
3930 /*
3931 Draw polygon or line.
3932 */
3933 if (image->matte == MagickFalse)
3934 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
cristyb5d5f722009-11-04 03:03:49 +00003935#if defined(MAGICKCORE_OPENMP_SUPPORT)
3936 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003937#endif
cristybb503372010-05-27 20:51:26 +00003938 for (y=(ssize_t) ceil(bounds.y1-0.5); y <= (ssize_t) floor(bounds.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00003939 {
cristy5c9e6f22010-09-17 17:31:01 +00003940 const int
3941 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003942
cristy3ed852e2009-09-05 21:47:34 +00003943 MagickRealType
3944 fill_opacity,
3945 stroke_opacity;
3946
3947 PixelPacket
3948 fill_color,
3949 stroke_color;
3950
cristy3ed852e2009-09-05 21:47:34 +00003951 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003952 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003953
cristy826a5472010-08-31 23:21:38 +00003954 register ssize_t
3955 x;
3956
cristy3ed852e2009-09-05 21:47:34 +00003957 if (status == MagickFalse)
3958 continue;
cristybb503372010-05-27 20:51:26 +00003959 q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-
cristy3ed852e2009-09-05 21:47:34 +00003960 start+1),1,exception);
3961 if (q == (PixelPacket *) NULL)
3962 {
3963 status=MagickFalse;
3964 continue;
3965 }
cristy3ed852e2009-09-05 21:47:34 +00003966 for (x=start; x <= stop; x++)
3967 {
3968 /*
3969 Fill and/or stroke.
3970 */
3971 fill_opacity=GetPixelOpacity(polygon_info[id],mid,fill,
cristy77f38fb2010-04-22 15:51:47 +00003972 draw_info->fill_rule,(double) x,(double) y,&stroke_opacity);
cristy3ed852e2009-09-05 21:47:34 +00003973 if (draw_info->stroke_antialias == MagickFalse)
3974 {
3975 fill_opacity=fill_opacity > 0.25 ? 1.0 : 0.0;
3976 stroke_opacity=stroke_opacity > 0.25 ? 1.0 : 0.0;
3977 }
3978 (void) GetFillColor(draw_info,x,y,&fill_color);
3979 fill_opacity=(MagickRealType) (QuantumRange-fill_opacity*(QuantumRange-
3980 fill_color.opacity));
3981 MagickCompositeOver(&fill_color,fill_opacity,q,(MagickRealType)
3982 q->opacity,q);
3983 (void) GetStrokeColor(draw_info,x,y,&stroke_color);
3984 stroke_opacity=(MagickRealType) (QuantumRange-stroke_opacity*
3985 (QuantumRange-stroke_color.opacity));
3986 MagickCompositeOver(&stroke_color,stroke_opacity,q,(MagickRealType)
3987 q->opacity,q);
3988 q++;
3989 }
3990 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3991 status=MagickFalse;
3992 }
3993 image_view=DestroyCacheView(image_view);
3994 polygon_info=DestroyPolygonThreadSet(polygon_info);
3995 if (image->debug != MagickFalse)
3996 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
3997 return(status);
3998}
3999
4000/*
4001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4002% %
4003% %
4004% %
4005% D r a w P r i m i t i v e %
4006% %
4007% %
4008% %
4009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4010%
4011% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
4012%
4013% The format of the DrawPrimitive method is:
4014%
4015% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
4016% PrimitiveInfo *primitive_info)
4017%
4018% A description of each parameter follows:
4019%
4020% o image: the image.
4021%
4022% o draw_info: the draw info.
4023%
4024% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4025%
4026*/
4027
4028static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4029{
4030 const char
4031 *methods[] =
4032 {
4033 "point",
4034 "replace",
4035 "floodfill",
4036 "filltoborder",
4037 "reset",
4038 "?"
4039 };
4040
cristy3ed852e2009-09-05 21:47:34 +00004041 PointInfo
4042 p,
4043 q,
4044 point;
4045
cristybb503372010-05-27 20:51:26 +00004046 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004047 i,
4048 x;
4049
cristy826a5472010-08-31 23:21:38 +00004050 ssize_t
4051 coordinates,
4052 y;
4053
cristybb503372010-05-27 20:51:26 +00004054 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4055 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004056 switch (primitive_info->primitive)
4057 {
4058 case PointPrimitive:
4059 {
4060 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004061 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004062 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004063 return;
4064 }
4065 case ColorPrimitive:
4066 {
4067 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004068 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004069 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004070 return;
4071 }
4072 case MattePrimitive:
4073 {
4074 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004075 "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004076 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004077 return;
4078 }
4079 case TextPrimitive:
4080 {
4081 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004082 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004083 return;
4084 }
4085 case ImagePrimitive:
4086 {
4087 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004088 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004089 return;
4090 }
4091 default:
4092 break;
4093 }
4094 coordinates=0;
4095 p=primitive_info[0].point;
4096 q.x=(-1.0);
4097 q.y=(-1.0);
4098 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4099 {
4100 point=primitive_info[i].point;
4101 if (coordinates <= 0)
4102 {
cristybb503372010-05-27 20:51:26 +00004103 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004104 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004105 " begin open (%.20g)",(double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004106 p=point;
4107 }
4108 point=primitive_info[i].point;
4109 if ((fabs(q.x-point.x) > MagickEpsilon) ||
4110 (fabs(q.y-point.y) > MagickEpsilon))
cristy8cd5b312010-01-07 01:10:24 +00004111 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004112 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004113 else
4114 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004115 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004116 q=point;
4117 coordinates--;
4118 if (coordinates > 0)
4119 continue;
4120 if ((fabs(p.x-point.x) > MagickEpsilon) ||
4121 (fabs(p.y-point.y) > MagickEpsilon))
cristye8c25f92010-06-03 00:53:06 +00004122 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4123 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004124 else
cristye8c25f92010-06-03 00:53:06 +00004125 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4126 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004127 }
4128}
4129
4130MagickExport MagickBooleanType DrawPrimitive(Image *image,
4131 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4132{
cristyc4c8d132010-01-07 01:58:38 +00004133 CacheView
4134 *image_view;
4135
cristy3ed852e2009-09-05 21:47:34 +00004136 ExceptionInfo
4137 *exception;
4138
cristy3ed852e2009-09-05 21:47:34 +00004139 MagickStatusType
4140 status;
4141
cristybb503372010-05-27 20:51:26 +00004142 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004143 i,
4144 x;
4145
cristy826a5472010-08-31 23:21:38 +00004146 ssize_t
4147 y;
4148
cristy3ed852e2009-09-05 21:47:34 +00004149 if (image->debug != MagickFalse)
4150 {
4151 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4152 " begin draw-primitive");
4153 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004154 " affine: %g %g %g %g %g %g",draw_info->affine.sx,
cristy3ed852e2009-09-05 21:47:34 +00004155 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4156 draw_info->affine.tx,draw_info->affine.ty);
4157 }
4158 status=MagickTrue;
4159 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00004160 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4161 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004162 image_view=AcquireCacheView(image);
4163 switch (primitive_info->primitive)
4164 {
4165 case PointPrimitive:
4166 {
4167 PixelPacket
4168 fill_color;
4169
4170 PixelPacket
4171 *q;
4172
cristybb503372010-05-27 20:51:26 +00004173 if ((y < 0) || (y >= (ssize_t) image->rows))
cristyb32b90a2009-09-07 21:45:48 +00004174 break;
cristybb503372010-05-27 20:51:26 +00004175 if ((x < 0) || (x >= (ssize_t) image->columns))
cristyb32b90a2009-09-07 21:45:48 +00004176 break;
cristy3ed852e2009-09-05 21:47:34 +00004177 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4178 if (q == (PixelPacket *) NULL)
4179 break;
4180 (void) GetFillColor(draw_info,x,y,&fill_color);
4181 MagickCompositeOver(&fill_color,(MagickRealType) fill_color.opacity,q,
4182 (MagickRealType) q->opacity,q);
4183 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4184 break;
4185 }
4186 case ColorPrimitive:
4187 {
4188 switch (primitive_info->method)
4189 {
4190 case PointMethod:
4191 default:
4192 {
4193 PixelPacket
4194 *q;
4195
4196 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4197 if (q == (PixelPacket *) NULL)
4198 break;
4199 (void) GetFillColor(draw_info,x,y,q);
4200 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4201 break;
4202 }
4203 case ReplaceMethod:
4204 {
4205 MagickBooleanType
4206 sync;
4207
4208 PixelPacket
4209 target;
4210
4211 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
cristybb503372010-05-27 20:51:26 +00004212 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004213 {
4214 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004215 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004216
4217 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4218 exception);
4219 if (q == (PixelPacket *) NULL)
4220 break;
cristybb503372010-05-27 20:51:26 +00004221 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004222 {
4223 if (IsColorSimilar(image,q,&target) == MagickFalse)
4224 {
4225 q++;
4226 continue;
4227 }
4228 (void) GetFillColor(draw_info,x,y,q);
4229 q++;
4230 }
4231 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4232 if (sync == MagickFalse)
4233 break;
4234 }
4235 break;
4236 }
4237 case FloodfillMethod:
4238 case FillToBorderMethod:
4239 {
4240 MagickPixelPacket
4241 target;
4242
4243 (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4244 if (primitive_info->method == FillToBorderMethod)
4245 {
4246 target.red=(MagickRealType) draw_info->border_color.red;
4247 target.green=(MagickRealType) draw_info->border_color.green;
4248 target.blue=(MagickRealType) draw_info->border_color.blue;
4249 }
4250 (void) FloodfillPaintImage(image,DefaultChannels,draw_info,&target,x,
4251 y,primitive_info->method == FloodfillMethod ? MagickFalse :
4252 MagickTrue);
4253 break;
4254 }
4255 case ResetMethod:
4256 {
4257 MagickBooleanType
4258 sync;
4259
cristybb503372010-05-27 20:51:26 +00004260 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004261 {
cristy3ed852e2009-09-05 21:47:34 +00004262 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004263 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004264
cristy826a5472010-08-31 23:21:38 +00004265 register ssize_t
4266 x;
4267
cristy3ed852e2009-09-05 21:47:34 +00004268 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4269 exception);
4270 if (q == (PixelPacket *) NULL)
4271 break;
cristybb503372010-05-27 20:51:26 +00004272 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004273 {
4274 (void) GetFillColor(draw_info,x,y,q);
4275 q++;
4276 }
4277 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4278 if (sync == MagickFalse)
4279 break;
4280 }
4281 break;
4282 }
4283 }
4284 break;
4285 }
4286 case MattePrimitive:
4287 {
4288 if (image->matte == MagickFalse)
4289 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
4290 switch (primitive_info->method)
4291 {
4292 case PointMethod:
4293 default:
4294 {
4295 PixelPacket
4296 pixel;
4297
4298 PixelPacket
4299 *q;
4300
4301 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
4302 if (q == (PixelPacket *) NULL)
4303 break;
4304 (void) GetFillColor(draw_info,x,y,&pixel);
cristya2d08742011-04-22 19:59:52 +00004305 SetOpacityPixelComponent(q,pixel.opacity);
cristy3ed852e2009-09-05 21:47:34 +00004306 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4307 break;
4308 }
4309 case ReplaceMethod:
4310 {
4311 MagickBooleanType
4312 sync;
4313
4314 PixelPacket
4315 pixel,
4316 target;
4317
4318 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
cristybb503372010-05-27 20:51:26 +00004319 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004320 {
cristy3ed852e2009-09-05 21:47:34 +00004321 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004322 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004323
cristy826a5472010-08-31 23:21:38 +00004324 register ssize_t
4325 x;
4326
cristy3ed852e2009-09-05 21:47:34 +00004327 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4328 exception);
4329 if (q == (PixelPacket *) NULL)
4330 break;
cristybb503372010-05-27 20:51:26 +00004331 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004332 {
4333 if (IsColorSimilar(image,q,&target) == MagickFalse)
4334 {
4335 q++;
4336 continue;
4337 }
4338 (void) GetFillColor(draw_info,x,y,&pixel);
cristya2d08742011-04-22 19:59:52 +00004339 SetOpacityPixelComponent(q,pixel.opacity);
cristy3ed852e2009-09-05 21:47:34 +00004340 q++;
4341 }
4342 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4343 if (sync == MagickFalse)
4344 break;
4345 }
4346 break;
4347 }
4348 case FloodfillMethod:
4349 case FillToBorderMethod:
4350 {
4351 MagickPixelPacket
4352 target;
4353
4354 (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4355 if (primitive_info->method == FillToBorderMethod)
4356 {
4357 target.red=(MagickRealType) draw_info->border_color.red;
4358 target.green=(MagickRealType) draw_info->border_color.green;
4359 target.blue=(MagickRealType) draw_info->border_color.blue;
4360 }
4361 (void) FloodfillPaintImage(image,OpacityChannel,draw_info,&target,x,y,
4362 primitive_info->method == FloodfillMethod ? MagickFalse :
4363 MagickTrue);
4364 break;
4365 }
4366 case ResetMethod:
4367 {
4368 MagickBooleanType
4369 sync;
4370
4371 PixelPacket
4372 pixel;
4373
cristybb503372010-05-27 20:51:26 +00004374 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004375 {
cristy3ed852e2009-09-05 21:47:34 +00004376 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004377 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004378
cristy826a5472010-08-31 23:21:38 +00004379 register ssize_t
4380 x;
4381
cristy3ed852e2009-09-05 21:47:34 +00004382 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4383 exception);
4384 if (q == (PixelPacket *) NULL)
4385 break;
cristybb503372010-05-27 20:51:26 +00004386 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004387 {
4388 (void) GetFillColor(draw_info,x,y,&pixel);
cristya2d08742011-04-22 19:59:52 +00004389 SetOpacityPixelComponent(q,pixel.opacity);
cristy3ed852e2009-09-05 21:47:34 +00004390 q++;
4391 }
4392 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4393 if (sync == MagickFalse)
4394 break;
4395 }
4396 break;
4397 }
4398 }
4399 break;
4400 }
4401 case TextPrimitive:
4402 {
4403 char
4404 geometry[MaxTextExtent];
4405
4406 DrawInfo
4407 *clone_info;
4408
4409 if (primitive_info->text == (char *) NULL)
4410 break;
4411 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4412 (void) CloneString(&clone_info->text,primitive_info->text);
cristyb51dff52011-05-19 16:55:47 +00004413 (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
cristy3ed852e2009-09-05 21:47:34 +00004414 primitive_info->point.x,primitive_info->point.y);
4415 (void) CloneString(&clone_info->geometry,geometry);
4416 status=AnnotateImage(image,clone_info);
4417 clone_info=DestroyDrawInfo(clone_info);
4418 break;
4419 }
4420 case ImagePrimitive:
4421 {
4422 AffineMatrix
4423 affine;
4424
4425 char
4426 composite_geometry[MaxTextExtent];
4427
4428 Image
4429 *composite_image;
4430
4431 ImageInfo
4432 *clone_info;
4433
cristy826a5472010-08-31 23:21:38 +00004434 RectangleInfo
4435 geometry;
4436
cristybb503372010-05-27 20:51:26 +00004437 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004438 x1,
4439 y1;
4440
cristy3ed852e2009-09-05 21:47:34 +00004441 if (primitive_info->text == (char *) NULL)
4442 break;
4443 clone_info=AcquireImageInfo();
4444 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4445 composite_image=ReadInlineImage(clone_info,primitive_info->text,
4446 &image->exception);
4447 else
4448 {
4449 (void) CopyMagickString(clone_info->filename,primitive_info->text,
4450 MaxTextExtent);
4451 composite_image=ReadImage(clone_info,&image->exception);
4452 }
4453 clone_info=DestroyImageInfo(clone_info);
4454 if (composite_image == (Image *) NULL)
4455 break;
4456 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4457 NULL,(void *) NULL);
cristybb503372010-05-27 20:51:26 +00004458 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4459 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4460 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4461 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
cristy3ed852e2009-09-05 21:47:34 +00004462 {
4463 char
4464 geometry[MaxTextExtent];
4465
4466 /*
4467 Resize image.
4468 */
cristyb51dff52011-05-19 16:55:47 +00004469 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
cristy3ed852e2009-09-05 21:47:34 +00004470 primitive_info[1].point.x,primitive_info[1].point.y);
4471 composite_image->filter=image->filter;
4472 (void) TransformImage(&composite_image,(char *) NULL,geometry);
4473 }
4474 if (composite_image->matte == MagickFalse)
4475 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4476 if (draw_info->opacity != OpaqueOpacity)
4477 (void) SetImageOpacity(composite_image,draw_info->opacity);
4478 SetGeometry(image,&geometry);
4479 image->gravity=draw_info->gravity;
4480 geometry.x=x;
4481 geometry.y=y;
cristyb51dff52011-05-19 16:55:47 +00004482 (void) FormatLocaleString(composite_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004483 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
cristye8c25f92010-06-03 00:53:06 +00004484 composite_image->rows,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00004485 (void) ParseGravityGeometry(image,composite_geometry,&geometry,
4486 &image->exception);
4487 affine=draw_info->affine;
4488 affine.tx=(double) geometry.x;
4489 affine.ty=(double) geometry.y;
4490 composite_image->interpolate=image->interpolate;
cristyd5f3fc32011-04-26 14:44:36 +00004491 if (draw_info->compose == OverCompositeOp)
4492 (void) DrawAffineImage(image,composite_image,&affine);
4493 else
4494 (void) CompositeImage(image,draw_info->compose,composite_image,
4495 geometry.x,geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00004496 composite_image=DestroyImage(composite_image);
4497 break;
4498 }
4499 default:
4500 {
4501 MagickRealType
4502 mid,
4503 scale;
4504
4505 DrawInfo
4506 *clone_info;
4507
4508 if (IsEventLogging() != MagickFalse)
4509 LogPrimitiveInfo(primitive_info);
4510 scale=ExpandAffine(&draw_info->affine);
4511 if ((draw_info->dash_pattern != (double *) NULL) &&
4512 (draw_info->dash_pattern[0] != 0.0) &&
4513 ((scale*draw_info->stroke_width) > MagickEpsilon) &&
4514 (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
4515 {
4516 /*
4517 Draw dash polygon.
4518 */
4519 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4520 clone_info->stroke_width=0.0;
4521 clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4522 status=DrawPolygonPrimitive(image,clone_info,primitive_info);
4523 clone_info=DestroyDrawInfo(clone_info);
4524 (void) DrawDashPolygon(draw_info,primitive_info,image);
4525 break;
4526 }
4527 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4528 if ((mid > 1.0) &&
4529 (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
4530 {
4531 MagickBooleanType
4532 closed_path;
4533
4534 /*
4535 Draw strokes while respecting line cap/join attributes.
4536 */
4537 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4538 closed_path=
4539 (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4540 (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4541 MagickTrue : MagickFalse;
cristybb503372010-05-27 20:51:26 +00004542 i=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004543 if ((((draw_info->linecap == RoundCap) ||
4544 (closed_path != MagickFalse)) &&
4545 (draw_info->linejoin == RoundJoin)) ||
4546 (primitive_info[i].primitive != UndefinedPrimitive))
4547 {
4548 (void) DrawPolygonPrimitive(image,draw_info,primitive_info);
4549 break;
4550 }
4551 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4552 clone_info->stroke_width=0.0;
4553 clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4554 status=DrawPolygonPrimitive(image,clone_info,primitive_info);
4555 clone_info=DestroyDrawInfo(clone_info);
4556 status|=DrawStrokePolygon(image,draw_info,primitive_info);
4557 break;
4558 }
4559 status=DrawPolygonPrimitive(image,draw_info,primitive_info);
4560 break;
4561 }
4562 }
4563 image_view=DestroyCacheView(image_view);
4564 if (image->debug != MagickFalse)
4565 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4566 return(status != 0 ? MagickTrue : MagickFalse);
4567}
4568
4569/*
4570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4571% %
4572% %
4573% %
4574+ D r a w S t r o k e P o l y g o n %
4575% %
4576% %
4577% %
4578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4579%
4580% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4581% the image while respecting the line cap and join attributes.
4582%
4583% The format of the DrawStrokePolygon method is:
4584%
4585% MagickBooleanType DrawStrokePolygon(Image *image,
4586% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4587%
4588% A description of each parameter follows:
4589%
4590% o image: the image.
4591%
4592% o draw_info: the draw info.
4593%
4594% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4595%
4596%
4597*/
4598
4599static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
4600 const PrimitiveInfo *primitive_info)
4601{
4602 PrimitiveInfo
4603 linecap[5];
4604
cristybb503372010-05-27 20:51:26 +00004605 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004606 i;
4607
4608 for (i=0; i < 4; i++)
4609 linecap[i]=(*primitive_info);
4610 linecap[0].coordinates=4;
4611 linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4612 linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4613 linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4614 linecap[3].point.y+=(double) (10.0*MagickEpsilon);
4615 linecap[4].primitive=UndefinedPrimitive;
4616 (void) DrawPolygonPrimitive(image,draw_info,linecap);
4617}
4618
4619static MagickBooleanType DrawStrokePolygon(Image *image,
4620 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4621{
4622 DrawInfo
4623 *clone_info;
4624
4625 MagickBooleanType
4626 closed_path,
4627 status;
4628
4629 PrimitiveInfo
4630 *stroke_polygon;
4631
4632 register const PrimitiveInfo
4633 *p,
4634 *q;
4635
4636 /*
4637 Draw stroked polygon.
4638 */
4639 if (image->debug != MagickFalse)
4640 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4641 " begin draw-stroke-polygon");
4642 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4643 clone_info->fill=draw_info->stroke;
4644 clone_info->stroke.opacity=(Quantum) TransparentOpacity;
4645 clone_info->stroke_width=0.0;
4646 clone_info->fill_rule=NonZeroRule;
4647 status=MagickTrue;
4648 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4649 {
4650 stroke_polygon=TraceStrokePolygon(draw_info,p);
4651 status=DrawPolygonPrimitive(image,clone_info,stroke_polygon);
4652 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4653 q=p+p->coordinates-1;
4654 closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4655 MagickTrue : MagickFalse;
4656 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4657 {
4658 DrawRoundLinecap(image,draw_info,p);
4659 DrawRoundLinecap(image,draw_info,q);
4660 }
4661 }
4662 clone_info=DestroyDrawInfo(clone_info);
4663 if (image->debug != MagickFalse)
4664 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4665 " end draw-stroke-polygon");
4666 return(status);
4667}
4668
4669/*
4670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4671% %
4672% %
4673% %
4674% G e t A f f i n e M a t r i x %
4675% %
4676% %
4677% %
4678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4679%
4680% GetAffineMatrix() returns an AffineMatrix initialized to the identity
4681% matrix.
4682%
4683% The format of the GetAffineMatrix method is:
4684%
4685% void GetAffineMatrix(AffineMatrix *affine_matrix)
4686%
4687% A description of each parameter follows:
4688%
4689% o affine_matrix: the affine matrix.
4690%
4691*/
4692MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
4693{
4694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4695 assert(affine_matrix != (AffineMatrix *) NULL);
4696 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4697 affine_matrix->sx=1.0;
4698 affine_matrix->sy=1.0;
4699}
4700
4701/*
4702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4703% %
4704% %
4705% %
4706+ G e t D r a w I n f o %
4707% %
4708% %
4709% %
4710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4711%
4712% GetDrawInfo() initializes draw_info to default values.
4713%
4714% The format of the GetDrawInfo method is:
4715%
4716% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4717%
4718% A description of each parameter follows:
4719%
4720% o image_info: the image info..
4721%
4722% o draw_info: the draw info.
4723%
4724*/
4725MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4726{
4727 const char
4728 *option;
4729
4730 ExceptionInfo
4731 *exception;
4732
4733 ImageInfo
4734 *clone_info;
4735
4736 /*
4737 Initialize draw attributes.
4738 */
4739 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4740 assert(draw_info != (DrawInfo *) NULL);
4741 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
4742 clone_info=CloneImageInfo(image_info);
4743 GetAffineMatrix(&draw_info->affine);
4744 exception=AcquireExceptionInfo();
4745 (void) QueryColorDatabase("#000F",&draw_info->fill,exception);
4746 (void) QueryColorDatabase("#FFF0",&draw_info->stroke,exception);
4747 draw_info->stroke_antialias=clone_info->antialias;
4748 draw_info->stroke_width=1.0;
4749 draw_info->opacity=OpaqueOpacity;
4750 draw_info->fill_rule=EvenOddRule;
4751 draw_info->linecap=ButtCap;
4752 draw_info->linejoin=MiterJoin;
4753 draw_info->miterlimit=10;
4754 draw_info->decorate=NoDecoration;
4755 if (clone_info->font != (char *) NULL)
4756 draw_info->font=AcquireString(clone_info->font);
4757 if (clone_info->density != (char *) NULL)
4758 draw_info->density=AcquireString(clone_info->density);
4759 draw_info->text_antialias=clone_info->antialias;
4760 draw_info->pointsize=12.0;
4761 if (clone_info->pointsize != 0.0)
4762 draw_info->pointsize=clone_info->pointsize;
4763 draw_info->undercolor.opacity=(Quantum) TransparentOpacity;
4764 draw_info->border_color=clone_info->border_color;
4765 draw_info->compose=OverCompositeOp;
4766 if (clone_info->server_name != (char *) NULL)
4767 draw_info->server_name=AcquireString(clone_info->server_name);
4768 draw_info->render=MagickTrue;
4769 draw_info->debug=IsEventLogging();
4770 option=GetImageOption(clone_info,"encoding");
4771 if (option != (const char *) NULL)
4772 (void) CloneString(&draw_info->encoding,option);
4773 option=GetImageOption(clone_info,"kerning");
4774 if (option != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00004775 draw_info->kerning=InterpretLocaleValue(option,(char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00004776 option=GetImageOption(clone_info,"interline-spacing");
4777 if (option != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00004778 draw_info->interline_spacing=InterpretLocaleValue(option,(char **) NULL);
cristy6ac8b332010-04-22 02:24:11 +00004779 draw_info->direction=UndefinedDirection;
cristy3ed852e2009-09-05 21:47:34 +00004780 option=GetImageOption(clone_info,"interword-spacing");
4781 if (option != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00004782 draw_info->interword_spacing=InterpretLocaleValue(option,(char **) NULL);
cristyc9b12952010-03-28 01:12:28 +00004783 option=GetImageOption(clone_info,"direction");
4784 if (option != (const char *) NULL)
cristy042ee782011-04-22 18:48:30 +00004785 draw_info->direction=(DirectionType) ParseCommandOption(
cristyc9b12952010-03-28 01:12:28 +00004786 MagickDirectionOptions,MagickFalse,option);
cristy3ed852e2009-09-05 21:47:34 +00004787 option=GetImageOption(clone_info,"fill");
4788 if (option != (const char *) NULL)
4789 (void) QueryColorDatabase(option,&draw_info->fill,exception);
4790 option=GetImageOption(clone_info,"stroke");
4791 if (option != (const char *) NULL)
4792 (void) QueryColorDatabase(option,&draw_info->stroke,exception);
4793 option=GetImageOption(clone_info,"strokewidth");
4794 if (option != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00004795 draw_info->stroke_width=InterpretLocaleValue(option,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004796 option=GetImageOption(clone_info,"undercolor");
4797 if (option != (const char *) NULL)
4798 (void) QueryColorDatabase(option,&draw_info->undercolor,exception);
4799 option=GetImageOption(clone_info,"gravity");
4800 if (option != (const char *) NULL)
cristy042ee782011-04-22 18:48:30 +00004801 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
cristy3ed852e2009-09-05 21:47:34 +00004802 MagickFalse,option);
4803 exception=DestroyExceptionInfo(exception);
4804 draw_info->signature=MagickSignature;
4805 clone_info=DestroyImageInfo(clone_info);
4806}
4807
4808/*
4809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4810% %
4811% %
4812% %
4813+ P e r m u t a t e %
4814% %
4815% %
4816% %
4817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4818%
4819% Permutate() returns the permuation of the (n,k).
4820%
4821% The format of the Permutate method is:
4822%
cristybb503372010-05-27 20:51:26 +00004823% void Permutate(ssize_t n,ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004824%
4825% A description of each parameter follows:
4826%
4827% o n:
4828%
4829% o k:
4830%
4831%
4832*/
cristybb503372010-05-27 20:51:26 +00004833static inline MagickRealType Permutate(const ssize_t n,const ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004834{
4835 MagickRealType
4836 r;
4837
cristybb503372010-05-27 20:51:26 +00004838 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004839 i;
4840
4841 r=1.0;
4842 for (i=k+1; i <= n; i++)
4843 r*=i;
4844 for (i=1; i <= (n-k); i++)
4845 r/=i;
4846 return(r);
4847}
4848
4849/*
4850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4851% %
4852% %
4853% %
4854+ T r a c e P r i m i t i v e %
4855% %
4856% %
4857% %
4858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4859%
4860% TracePrimitive is a collection of methods for generating graphic
4861% primitives such as arcs, ellipses, paths, etc.
4862%
4863*/
4864
4865static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4866 const PointInfo end,const PointInfo degrees)
4867{
4868 PointInfo
4869 center,
4870 radii;
4871
4872 center.x=0.5*(end.x+start.x);
4873 center.y=0.5*(end.y+start.y);
4874 radii.x=fabs(center.x-start.x);
4875 radii.y=fabs(center.y-start.y);
4876 TraceEllipse(primitive_info,center,radii,degrees);
4877}
4878
4879static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
4880 const PointInfo end,const PointInfo arc,const MagickRealType angle,
4881 const MagickBooleanType large_arc,const MagickBooleanType sweep)
4882{
4883 MagickRealType
4884 alpha,
4885 beta,
4886 delta,
4887 factor,
4888 gamma,
4889 theta;
4890
4891 PointInfo
4892 center,
4893 points[3],
4894 radii;
4895
4896 register MagickRealType
4897 cosine,
4898 sine;
4899
4900 register PrimitiveInfo
4901 *p;
4902
cristybb503372010-05-27 20:51:26 +00004903 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004904 i;
4905
cristybb503372010-05-27 20:51:26 +00004906 size_t
cristy3ed852e2009-09-05 21:47:34 +00004907 arc_segments;
4908
4909 if ((start.x == end.x) && (start.y == end.y))
4910 {
4911 TracePoint(primitive_info,end);
4912 return;
4913 }
4914 radii.x=fabs(arc.x);
4915 radii.y=fabs(arc.y);
4916 if ((radii.x == 0.0) || (radii.y == 0.0))
4917 {
4918 TraceLine(primitive_info,start,end);
4919 return;
4920 }
4921 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
4922 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
4923 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
4924 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
4925 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
4926 (radii.y*radii.y);
4927 if (delta < MagickEpsilon)
4928 {
4929 TraceLine(primitive_info,start,end);
4930 return;
4931 }
4932 if (delta > 1.0)
4933 {
4934 radii.x*=sqrt((double) delta);
4935 radii.y*=sqrt((double) delta);
4936 }
4937 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
4938 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
4939 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
4940 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
4941 alpha=points[1].x-points[0].x;
4942 beta=points[1].y-points[0].y;
4943 factor=1.0/(alpha*alpha+beta*beta)-0.25;
4944 if (factor <= 0.0)
4945 factor=0.0;
4946 else
4947 {
4948 factor=sqrt((double) factor);
4949 if (sweep == large_arc)
4950 factor=(-factor);
4951 }
4952 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
4953 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
4954 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
4955 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
4956 if ((theta < 0.0) && (sweep != MagickFalse))
4957 theta+=(MagickRealType) (2.0*MagickPI);
4958 else
4959 if ((theta > 0.0) && (sweep == MagickFalse))
4960 theta-=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00004961 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
cristy20be8a02010-08-17 00:23:28 +00004962 MagickEpsilon))));
cristy3ed852e2009-09-05 21:47:34 +00004963 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00004964 for (i=0; i < (ssize_t) arc_segments; i++)
cristy3ed852e2009-09-05 21:47:34 +00004965 {
4966 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
4967 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
4968 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
4969 sin(fmod((double) beta,DegreesToRadians(360.0)));
4970 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
4971 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
4972 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
4973 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
4974 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
4975 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
4976 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
4977 theta/arc_segments),DegreesToRadians(360.0))));
4978 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
4979 theta/arc_segments),DegreesToRadians(360.0))));
4980 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
4981 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
4982 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
4983 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
4984 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
4985 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
4986 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
4987 points[0].y);
4988 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
4989 points[0].y);
4990 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
4991 points[1].y);
4992 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
4993 points[1].y);
4994 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
4995 points[2].y);
4996 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
4997 points[2].y);
cristybb503372010-05-27 20:51:26 +00004998 if (i == (ssize_t) (arc_segments-1))
cristy3ed852e2009-09-05 21:47:34 +00004999 (p+3)->point=end;
5000 TraceBezier(p,4);
5001 p+=p->coordinates;
5002 }
cristybb503372010-05-27 20:51:26 +00005003 primitive_info->coordinates=(size_t) (p-primitive_info);
5004 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005005 {
5006 p->primitive=primitive_info->primitive;
5007 p--;
5008 }
5009}
5010
5011static void TraceBezier(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005012 const size_t number_coordinates)
cristy3ed852e2009-09-05 21:47:34 +00005013{
5014 MagickRealType
5015 alpha,
5016 *coefficients,
5017 weight;
5018
5019 PointInfo
5020 end,
5021 point,
5022 *points;
5023
cristy826a5472010-08-31 23:21:38 +00005024 register PrimitiveInfo
5025 *p;
5026
cristybb503372010-05-27 20:51:26 +00005027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005028 i,
5029 j;
5030
cristybb503372010-05-27 20:51:26 +00005031 size_t
cristy3ed852e2009-09-05 21:47:34 +00005032 control_points,
5033 quantum;
5034
5035 /*
5036 Allocate coeficients.
5037 */
5038 quantum=number_coordinates;
cristybb503372010-05-27 20:51:26 +00005039 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005040 {
cristybb503372010-05-27 20:51:26 +00005041 for (j=i+1; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005042 {
5043 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
5044 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005045 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005046 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
5047 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005048 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005049 }
5050 }
cristybb503372010-05-27 20:51:26 +00005051 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
cristy3ed852e2009-09-05 21:47:34 +00005052 (double) BezierQuantum);
5053 control_points=quantum*number_coordinates;
5054 coefficients=(MagickRealType *) AcquireQuantumMemory((size_t)
5055 number_coordinates,sizeof(*coefficients));
5056 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5057 sizeof(*points));
5058 if ((coefficients == (MagickRealType *) NULL) ||
5059 (points == (PointInfo *) NULL))
5060 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5061 /*
5062 Compute bezier points.
5063 */
5064 end=primitive_info[number_coordinates-1].point;
cristybb503372010-05-27 20:51:26 +00005065 for (i=0; i < (ssize_t) number_coordinates; i++)
5066 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
cristy3ed852e2009-09-05 21:47:34 +00005067 weight=0.0;
cristybb503372010-05-27 20:51:26 +00005068 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005069 {
5070 p=primitive_info;
5071 point.x=0.0;
5072 point.y=0.0;
5073 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
cristybb503372010-05-27 20:51:26 +00005074 for (j=0; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005075 {
5076 point.x+=alpha*coefficients[j]*p->point.x;
5077 point.y+=alpha*coefficients[j]*p->point.y;
5078 alpha*=weight/(1.0-weight);
5079 p++;
5080 }
5081 points[i]=point;
5082 weight+=1.0/control_points;
5083 }
5084 /*
5085 Bezier curves are just short segmented polys.
5086 */
5087 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005088 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005089 {
5090 TracePoint(p,points[i]);
5091 p+=p->coordinates;
5092 }
5093 TracePoint(p,end);
5094 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005095 primitive_info->coordinates=(size_t) (p-primitive_info);
5096 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005097 {
5098 p->primitive=primitive_info->primitive;
5099 p--;
5100 }
5101 points=(PointInfo *) RelinquishMagickMemory(points);
5102 coefficients=(MagickRealType *) RelinquishMagickMemory(coefficients);
5103}
5104
5105static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5106 const PointInfo end)
5107{
5108 MagickRealType
5109 alpha,
5110 beta,
5111 radius;
5112
5113 PointInfo
5114 offset,
5115 degrees;
5116
5117 alpha=end.x-start.x;
5118 beta=end.y-start.y;
5119 radius=hypot((double) alpha,(double) beta);
5120 offset.x=(double) radius;
5121 offset.y=(double) radius;
5122 degrees.x=0.0;
5123 degrees.y=360.0;
5124 TraceEllipse(primitive_info,start,offset,degrees);
5125}
5126
5127static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5128 const PointInfo stop,const PointInfo degrees)
5129{
5130 MagickRealType
5131 delta,
5132 step,
5133 y;
5134
5135 PointInfo
5136 angle,
5137 point;
5138
5139 register PrimitiveInfo
5140 *p;
5141
cristybb503372010-05-27 20:51:26 +00005142 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005143 i;
5144
5145 /*
5146 Ellipses are just short segmented polys.
5147 */
5148 if ((stop.x == 0.0) && (stop.y == 0.0))
5149 {
5150 TracePoint(primitive_info,start);
5151 return;
5152 }
5153 delta=2.0/MagickMax(stop.x,stop.y);
5154 step=(MagickRealType) (MagickPI/8.0);
cristyd4e3ffa2011-01-20 13:47:55 +00005155 if ((delta >= 0.0) && (delta < (MagickRealType) (MagickPI/8.0)))
5156 step=(MagickRealType) (MagickPI/(4*(MagickPI/delta/2+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00005157 angle.x=DegreesToRadians(degrees.x);
5158 y=degrees.y;
5159 while (y < degrees.x)
5160 y+=360.0;
5161 angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
5162 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5163 {
5164 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5165 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5166 TracePoint(p,point);
5167 p+=p->coordinates;
5168 }
5169 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5170 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5171 TracePoint(p,point);
5172 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005173 primitive_info->coordinates=(size_t) (p-primitive_info);
5174 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005175 {
5176 p->primitive=primitive_info->primitive;
5177 p--;
5178 }
5179}
5180
5181static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5182 const PointInfo end)
5183{
5184 TracePoint(primitive_info,start);
5185 if ((fabs(start.x-end.x) <= MagickEpsilon) &&
5186 (fabs(start.y-end.y) <= MagickEpsilon))
5187 {
5188 primitive_info->primitive=PointPrimitive;
5189 primitive_info->coordinates=1;
5190 return;
5191 }
5192 TracePoint(primitive_info+1,end);
5193 (primitive_info+1)->primitive=primitive_info->primitive;
5194 primitive_info->coordinates=2;
5195}
5196
cristybb503372010-05-27 20:51:26 +00005197static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00005198{
5199 char
5200 token[MaxTextExtent];
5201
5202 const char
5203 *p;
5204
5205 int
5206 attribute,
5207 last_attribute;
5208
5209 MagickRealType
5210 x,
5211 y;
5212
5213 PointInfo
5214 end,
5215 points[4],
5216 point,
5217 start;
5218
5219 PrimitiveType
5220 primitive_type;
5221
5222 register PrimitiveInfo
5223 *q;
5224
cristybb503372010-05-27 20:51:26 +00005225 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005226 i;
5227
cristybb503372010-05-27 20:51:26 +00005228 size_t
cristy3ed852e2009-09-05 21:47:34 +00005229 number_coordinates,
5230 z_count;
5231
5232 attribute=0;
5233 point.x=0.0;
5234 point.y=0.0;
5235 start.x=0.0;
5236 start.y=0.0;
5237 number_coordinates=0;
5238 z_count=0;
5239 primitive_type=primitive_info->primitive;
5240 q=primitive_info;
5241 for (p=path; *p != '\0'; )
5242 {
5243 while (isspace((int) ((unsigned char) *p)) != 0)
5244 p++;
5245 if (*p == '\0')
5246 break;
5247 last_attribute=attribute;
5248 attribute=(int) (*p++);
5249 switch (attribute)
5250 {
5251 case 'a':
5252 case 'A':
5253 {
5254 MagickBooleanType
5255 large_arc,
5256 sweep;
5257
5258 MagickRealType
5259 angle;
5260
5261 PointInfo
5262 arc;
5263
5264 /*
5265 Compute arc points.
5266 */
5267 do
5268 {
5269 GetMagickToken(p,&p,token);
5270 if (*token == ',')
5271 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005272 arc.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005273 GetMagickToken(p,&p,token);
5274 if (*token == ',')
5275 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005276 arc.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005277 GetMagickToken(p,&p,token);
5278 if (*token == ',')
5279 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005280 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005281 GetMagickToken(p,&p,token);
5282 if (*token == ',')
5283 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005284 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005285 GetMagickToken(p,&p,token);
5286 if (*token == ',')
5287 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005288 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005289 GetMagickToken(p,&p,token);
5290 if (*token == ',')
5291 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005292 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005293 GetMagickToken(p,&p,token);
5294 if (*token == ',')
5295 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005296 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005297 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5298 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5299 TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5300 q+=q->coordinates;
5301 point=end;
5302 } while (IsPoint(p) != MagickFalse);
5303 break;
5304 }
5305 case 'c':
5306 case 'C':
5307 {
5308 /*
5309 Compute bezier points.
5310 */
5311 do
5312 {
5313 points[0]=point;
5314 for (i=1; i < 4; i++)
5315 {
5316 GetMagickToken(p,&p,token);
5317 if (*token == ',')
5318 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005319 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005320 GetMagickToken(p,&p,token);
5321 if (*token == ',')
5322 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005323 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005324 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5325 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5326 points[i]=end;
5327 }
5328 for (i=0; i < 4; i++)
5329 (q+i)->point=points[i];
5330 TraceBezier(q,4);
5331 q+=q->coordinates;
5332 point=end;
5333 } while (IsPoint(p) != MagickFalse);
5334 break;
5335 }
5336 case 'H':
5337 case 'h':
5338 {
5339 do
5340 {
5341 GetMagickToken(p,&p,token);
5342 if (*token == ',')
5343 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005344 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005345 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
5346 TracePoint(q,point);
5347 q+=q->coordinates;
5348 } while (IsPoint(p) != MagickFalse);
5349 break;
5350 }
5351 case 'l':
5352 case 'L':
5353 {
5354 do
5355 {
5356 GetMagickToken(p,&p,token);
5357 if (*token == ',')
5358 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005359 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005360 GetMagickToken(p,&p,token);
5361 if (*token == ',')
5362 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005363 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005364 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
5365 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
5366 TracePoint(q,point);
5367 q+=q->coordinates;
5368 } while (IsPoint(p) != MagickFalse);
5369 break;
5370 }
5371 case 'M':
5372 case 'm':
5373 {
5374 if (q != primitive_info)
5375 {
cristybb503372010-05-27 20:51:26 +00005376 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005377 number_coordinates+=primitive_info->coordinates;
5378 primitive_info=q;
5379 }
5380 i=0;
5381 do
5382 {
5383 GetMagickToken(p,&p,token);
5384 if (*token == ',')
5385 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005386 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005387 GetMagickToken(p,&p,token);
5388 if (*token == ',')
5389 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005390 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005391 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
5392 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
5393 if (i == 0)
5394 start=point;
5395 i++;
5396 TracePoint(q,point);
5397 q+=q->coordinates;
cristy826a5472010-08-31 23:21:38 +00005398 if ((i != 0) && (attribute == (int) 'M'))
cristy3ed852e2009-09-05 21:47:34 +00005399 {
5400 TracePoint(q,point);
5401 q+=q->coordinates;
5402 }
5403 } while (IsPoint(p) != MagickFalse);
5404 break;
5405 }
5406 case 'q':
5407 case 'Q':
5408 {
5409 /*
5410 Compute bezier points.
5411 */
5412 do
5413 {
5414 points[0]=point;
5415 for (i=1; i < 3; i++)
5416 {
5417 GetMagickToken(p,&p,token);
5418 if (*token == ',')
5419 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005420 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005421 GetMagickToken(p,&p,token);
5422 if (*token == ',')
5423 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005424 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005425 if (*p == ',')
5426 p++;
5427 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
5428 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
5429 points[i]=end;
5430 }
5431 for (i=0; i < 3; i++)
5432 (q+i)->point=points[i];
5433 TraceBezier(q,3);
5434 q+=q->coordinates;
5435 point=end;
5436 } while (IsPoint(p) != MagickFalse);
5437 break;
5438 }
5439 case 's':
5440 case 'S':
5441 {
5442 /*
5443 Compute bezier points.
5444 */
5445 do
5446 {
5447 points[0]=points[3];
5448 points[1].x=2.0*points[3].x-points[2].x;
5449 points[1].y=2.0*points[3].y-points[2].y;
5450 for (i=2; i < 4; i++)
5451 {
5452 GetMagickToken(p,&p,token);
5453 if (*token == ',')
5454 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005455 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005456 GetMagickToken(p,&p,token);
5457 if (*token == ',')
5458 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005459 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005460 if (*p == ',')
5461 p++;
5462 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
5463 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
5464 points[i]=end;
5465 }
5466 if (strchr("CcSs",last_attribute) == (char *) NULL)
5467 {
5468 points[0]=points[2];
5469 points[1]=points[3];
5470 }
5471 for (i=0; i < 4; i++)
5472 (q+i)->point=points[i];
5473 TraceBezier(q,4);
5474 q+=q->coordinates;
5475 point=end;
5476 } while (IsPoint(p) != MagickFalse);
5477 break;
5478 }
5479 case 't':
5480 case 'T':
5481 {
5482 /*
5483 Compute bezier points.
5484 */
5485 do
5486 {
5487 points[0]=points[2];
5488 points[1].x=2.0*points[2].x-points[1].x;
5489 points[1].y=2.0*points[2].y-points[1].y;
5490 for (i=2; i < 3; i++)
5491 {
5492 GetMagickToken(p,&p,token);
5493 if (*token == ',')
5494 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005495 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005496 GetMagickToken(p,&p,token);
5497 if (*token == ',')
5498 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005499 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005500 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
5501 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
5502 points[i]=end;
5503 }
5504 if (strchr("QqTt",last_attribute) == (char *) NULL)
5505 {
5506 points[0]=points[2];
5507 points[1]=points[3];
5508 }
5509 for (i=0; i < 3; i++)
5510 (q+i)->point=points[i];
5511 TraceBezier(q,3);
5512 q+=q->coordinates;
5513 point=end;
5514 } while (IsPoint(p) != MagickFalse);
5515 break;
5516 }
5517 case 'v':
5518 case 'V':
5519 {
5520 do
5521 {
5522 GetMagickToken(p,&p,token);
5523 if (*token == ',')
5524 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005525 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005526 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
5527 TracePoint(q,point);
5528 q+=q->coordinates;
5529 } while (IsPoint(p) != MagickFalse);
5530 break;
5531 }
5532 case 'z':
5533 case 'Z':
5534 {
5535 point=start;
5536 TracePoint(q,point);
5537 q+=q->coordinates;
cristybb503372010-05-27 20:51:26 +00005538 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005539 number_coordinates+=primitive_info->coordinates;
5540 primitive_info=q;
5541 z_count++;
5542 break;
5543 }
5544 default:
5545 {
5546 if (isalpha((int) ((unsigned char) attribute)) != 0)
cristy1e604812011-05-19 18:07:50 +00005547 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n",
5548 attribute);
cristy3ed852e2009-09-05 21:47:34 +00005549 break;
5550 }
5551 }
5552 }
cristybb503372010-05-27 20:51:26 +00005553 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005554 number_coordinates+=primitive_info->coordinates;
cristybb503372010-05-27 20:51:26 +00005555 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005556 {
5557 q--;
5558 q->primitive=primitive_type;
5559 if (z_count > 1)
5560 q->method=FillToBorderMethod;
5561 }
5562 q=primitive_info;
5563 return(number_coordinates);
5564}
5565
5566static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
5567 const PointInfo end)
5568{
5569 PointInfo
5570 point;
5571
5572 register PrimitiveInfo
5573 *p;
5574
cristybb503372010-05-27 20:51:26 +00005575 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005576 i;
5577
5578 p=primitive_info;
5579 TracePoint(p,start);
5580 p+=p->coordinates;
5581 point.x=start.x;
5582 point.y=end.y;
5583 TracePoint(p,point);
5584 p+=p->coordinates;
5585 TracePoint(p,end);
5586 p+=p->coordinates;
5587 point.x=end.x;
5588 point.y=start.y;
5589 TracePoint(p,point);
5590 p+=p->coordinates;
5591 TracePoint(p,start);
5592 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005593 primitive_info->coordinates=(size_t) (p-primitive_info);
5594 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005595 {
5596 p->primitive=primitive_info->primitive;
5597 p--;
5598 }
5599}
5600
5601static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
5602 const PointInfo start,const PointInfo end,PointInfo arc)
5603{
5604 PointInfo
5605 degrees,
5606 offset,
5607 point;
5608
5609 register PrimitiveInfo
5610 *p;
5611
cristybb503372010-05-27 20:51:26 +00005612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005613 i;
5614
5615 p=primitive_info;
5616 offset.x=fabs(end.x-start.x);
5617 offset.y=fabs(end.y-start.y);
5618 if (arc.x > (0.5*offset.x))
5619 arc.x=0.5*offset.x;
5620 if (arc.y > (0.5*offset.y))
5621 arc.y=0.5*offset.y;
5622 point.x=start.x+offset.x-arc.x;
5623 point.y=start.y+arc.y;
5624 degrees.x=270.0;
5625 degrees.y=360.0;
5626 TraceEllipse(p,point,arc,degrees);
5627 p+=p->coordinates;
5628 point.x=start.x+offset.x-arc.x;
5629 point.y=start.y+offset.y-arc.y;
5630 degrees.x=0.0;
5631 degrees.y=90.0;
5632 TraceEllipse(p,point,arc,degrees);
5633 p+=p->coordinates;
5634 point.x=start.x+arc.x;
5635 point.y=start.y+offset.y-arc.y;
5636 degrees.x=90.0;
5637 degrees.y=180.0;
5638 TraceEllipse(p,point,arc,degrees);
5639 p+=p->coordinates;
5640 point.x=start.x+arc.x;
5641 point.y=start.y+arc.y;
5642 degrees.x=180.0;
5643 degrees.y=270.0;
5644 TraceEllipse(p,point,arc,degrees);
5645 p+=p->coordinates;
5646 TracePoint(p,primitive_info->point);
5647 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005648 primitive_info->coordinates=(size_t) (p-primitive_info);
5649 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005650 {
5651 p->primitive=primitive_info->primitive;
5652 p--;
5653 }
5654}
5655
5656static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005657 const size_t number_vertices,const MagickRealType offset)
cristy3ed852e2009-09-05 21:47:34 +00005658{
5659 MagickRealType
5660 distance;
5661
cristy3ed852e2009-09-05 21:47:34 +00005662 register MagickRealType
5663 dx,
5664 dy;
5665
cristybb503372010-05-27 20:51:26 +00005666 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005667 i;
5668
cristy826a5472010-08-31 23:21:38 +00005669 ssize_t
5670 j;
5671
cristy3ed852e2009-09-05 21:47:34 +00005672 dx=0.0;
5673 dy=0.0;
cristybb503372010-05-27 20:51:26 +00005674 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005675 {
5676 dx=primitive_info[0].point.x-primitive_info[i].point.x;
5677 dy=primitive_info[0].point.y-primitive_info[i].point.y;
5678 if ((fabs((double) dx) >= MagickEpsilon) ||
5679 (fabs((double) dy) >= MagickEpsilon))
5680 break;
5681 }
cristybb503372010-05-27 20:51:26 +00005682 if (i == (ssize_t) number_vertices)
5683 i=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005684 distance=hypot((double) dx,(double) dy);
5685 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
5686 dx*(distance+offset)/distance);
5687 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
5688 dy*(distance+offset)/distance);
cristybb503372010-05-27 20:51:26 +00005689 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00005690 {
5691 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
5692 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
5693 if ((fabs((double) dx) >= MagickEpsilon) ||
5694 (fabs((double) dy) >= MagickEpsilon))
5695 break;
5696 }
5697 distance=hypot((double) dx,(double) dy);
5698 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
5699 dx*(distance+offset)/distance);
5700 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
5701 dy*(distance+offset)/distance);
5702}
5703
5704static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
5705 const PrimitiveInfo *primitive_info)
5706{
5707 typedef struct _LineSegment
5708 {
5709 double
5710 p,
5711 q;
5712 } LineSegment;
5713
5714 LineSegment
5715 dx,
5716 dy,
5717 inverse_slope,
5718 slope,
5719 theta;
5720
cristy3ed852e2009-09-05 21:47:34 +00005721 MagickBooleanType
5722 closed_path;
5723
5724 MagickRealType
5725 delta_theta,
5726 dot_product,
5727 mid,
5728 miterlimit;
5729
5730 PointInfo
5731 box_p[5],
5732 box_q[5],
5733 center,
5734 offset,
5735 *path_p,
5736 *path_q;
5737
5738 PrimitiveInfo
5739 *polygon_primitive,
5740 *stroke_polygon;
5741
cristybb503372010-05-27 20:51:26 +00005742 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005743 i;
5744
cristybb503372010-05-27 20:51:26 +00005745 size_t
cristy3ed852e2009-09-05 21:47:34 +00005746 arc_segments,
5747 max_strokes,
5748 number_vertices;
5749
cristy826a5472010-08-31 23:21:38 +00005750 ssize_t
5751 j,
5752 n,
5753 p,
5754 q;
5755
cristy3ed852e2009-09-05 21:47:34 +00005756 /*
5757 Allocate paths.
5758 */
5759 number_vertices=primitive_info->coordinates;
5760 max_strokes=2*number_vertices+6*BezierQuantum+360;
5761 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5762 sizeof(*path_p));
5763 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5764 sizeof(*path_q));
5765 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
5766 number_vertices+2UL,sizeof(*polygon_primitive));
5767 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
5768 (polygon_primitive == (PrimitiveInfo *) NULL))
5769 return((PrimitiveInfo *) NULL);
5770 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
5771 number_vertices*sizeof(*polygon_primitive));
5772 closed_path=
5773 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
5774 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
5775 MagickTrue : MagickFalse;
5776 if ((draw_info->linejoin == RoundJoin) ||
5777 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
5778 {
5779 polygon_primitive[number_vertices]=primitive_info[1];
5780 number_vertices++;
5781 }
5782 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
5783 /*
5784 Compute the slope for the first line segment, p.
5785 */
5786 dx.p=0.0;
5787 dy.p=0.0;
cristybb503372010-05-27 20:51:26 +00005788 for (n=1; n < (ssize_t) number_vertices; n++)
cristy3ed852e2009-09-05 21:47:34 +00005789 {
5790 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
5791 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
5792 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
5793 break;
5794 }
cristybb503372010-05-27 20:51:26 +00005795 if (n == (ssize_t) number_vertices)
5796 n=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005797 slope.p=0.0;
5798 inverse_slope.p=0.0;
5799 if (fabs(dx.p) <= MagickEpsilon)
5800 {
5801 if (dx.p >= 0.0)
5802 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5803 else
5804 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5805 }
5806 else
5807 if (fabs(dy.p) <= MagickEpsilon)
5808 {
5809 if (dy.p >= 0.0)
5810 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5811 else
5812 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5813 }
5814 else
5815 {
5816 slope.p=dy.p/dx.p;
5817 inverse_slope.p=(-1.0/slope.p);
5818 }
5819 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5820 miterlimit=(MagickRealType) (draw_info->miterlimit*draw_info->miterlimit*
5821 mid*mid);
5822 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
5823 TraceSquareLinecap(polygon_primitive,number_vertices,mid);
5824 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
5825 offset.y=(double) (offset.x*inverse_slope.p);
5826 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
5827 {
5828 box_p[0].x=polygon_primitive[0].point.x-offset.x;
5829 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
5830 box_p[1].x=polygon_primitive[n].point.x-offset.x;
5831 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
5832 box_q[0].x=polygon_primitive[0].point.x+offset.x;
5833 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
5834 box_q[1].x=polygon_primitive[n].point.x+offset.x;
5835 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
5836 }
5837 else
5838 {
5839 box_p[0].x=polygon_primitive[0].point.x+offset.x;
5840 box_p[0].y=polygon_primitive[0].point.y+offset.y;
5841 box_p[1].x=polygon_primitive[n].point.x+offset.x;
5842 box_p[1].y=polygon_primitive[n].point.y+offset.y;
5843 box_q[0].x=polygon_primitive[0].point.x-offset.x;
5844 box_q[0].y=polygon_primitive[0].point.y-offset.y;
5845 box_q[1].x=polygon_primitive[n].point.x-offset.x;
5846 box_q[1].y=polygon_primitive[n].point.y-offset.y;
5847 }
5848 /*
5849 Create strokes for the line join attribute: bevel, miter, round.
5850 */
5851 p=0;
5852 q=0;
5853 path_q[p++]=box_q[0];
5854 path_p[q++]=box_p[0];
cristybb503372010-05-27 20:51:26 +00005855 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005856 {
5857 /*
5858 Compute the slope for this line segment, q.
5859 */
5860 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
5861 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
5862 dot_product=dx.q*dx.q+dy.q*dy.q;
5863 if (dot_product < 0.25)
5864 continue;
5865 slope.q=0.0;
5866 inverse_slope.q=0.0;
5867 if (fabs(dx.q) < MagickEpsilon)
5868 {
5869 if (dx.q >= 0.0)
5870 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5871 else
5872 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5873 }
5874 else
5875 if (fabs(dy.q) <= MagickEpsilon)
5876 {
5877 if (dy.q >= 0.0)
5878 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5879 else
5880 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5881 }
5882 else
5883 {
5884 slope.q=dy.q/dx.q;
5885 inverse_slope.q=(-1.0/slope.q);
5886 }
5887 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
5888 offset.y=(double) (offset.x*inverse_slope.q);
5889 dot_product=dy.q*offset.x-dx.q*offset.y;
5890 if (dot_product > 0.0)
5891 {
5892 box_p[2].x=polygon_primitive[n].point.x-offset.x;
5893 box_p[2].y=polygon_primitive[n].point.y-offset.y;
5894 box_p[3].x=polygon_primitive[i].point.x-offset.x;
5895 box_p[3].y=polygon_primitive[i].point.y-offset.y;
5896 box_q[2].x=polygon_primitive[n].point.x+offset.x;
5897 box_q[2].y=polygon_primitive[n].point.y+offset.y;
5898 box_q[3].x=polygon_primitive[i].point.x+offset.x;
5899 box_q[3].y=polygon_primitive[i].point.y+offset.y;
5900 }
5901 else
5902 {
5903 box_p[2].x=polygon_primitive[n].point.x+offset.x;
5904 box_p[2].y=polygon_primitive[n].point.y+offset.y;
5905 box_p[3].x=polygon_primitive[i].point.x+offset.x;
5906 box_p[3].y=polygon_primitive[i].point.y+offset.y;
5907 box_q[2].x=polygon_primitive[n].point.x-offset.x;
5908 box_q[2].y=polygon_primitive[n].point.y-offset.y;
5909 box_q[3].x=polygon_primitive[i].point.x-offset.x;
5910 box_q[3].y=polygon_primitive[i].point.y-offset.y;
5911 }
5912 if (fabs((double) (slope.p-slope.q)) <= MagickEpsilon)
5913 {
5914 box_p[4]=box_p[1];
5915 box_q[4]=box_q[1];
5916 }
5917 else
5918 {
5919 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
5920 box_p[3].y)/(slope.p-slope.q));
5921 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
5922 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
5923 box_q[3].y)/(slope.p-slope.q));
5924 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
5925 }
cristybb503372010-05-27 20:51:26 +00005926 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00005927 {
5928 max_strokes+=6*BezierQuantum+360;
5929 path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
5930 sizeof(*path_p));
5931 path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
5932 sizeof(*path_q));
5933 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
5934 {
5935 polygon_primitive=(PrimitiveInfo *)
5936 RelinquishMagickMemory(polygon_primitive);
5937 return((PrimitiveInfo *) NULL);
5938 }
5939 }
5940 dot_product=dx.q*dy.p-dx.p*dy.q;
5941 if (dot_product <= 0.0)
5942 switch (draw_info->linejoin)
5943 {
5944 case BevelJoin:
5945 {
5946 path_q[q++]=box_q[1];
5947 path_q[q++]=box_q[2];
5948 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5949 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5950 if (dot_product <= miterlimit)
5951 path_p[p++]=box_p[4];
5952 else
5953 {
5954 path_p[p++]=box_p[1];
5955 path_p[p++]=box_p[2];
5956 }
5957 break;
5958 }
5959 case MiterJoin:
5960 {
5961 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5962 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5963 if (dot_product <= miterlimit)
5964 {
5965 path_q[q++]=box_q[4];
5966 path_p[p++]=box_p[4];
5967 }
5968 else
5969 {
5970 path_q[q++]=box_q[1];
5971 path_q[q++]=box_q[2];
5972 path_p[p++]=box_p[1];
5973 path_p[p++]=box_p[2];
5974 }
5975 break;
5976 }
5977 case RoundJoin:
5978 {
5979 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5980 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5981 if (dot_product <= miterlimit)
5982 path_p[p++]=box_p[4];
5983 else
5984 {
5985 path_p[p++]=box_p[1];
5986 path_p[p++]=box_p[2];
5987 }
5988 center=polygon_primitive[n].point;
5989 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
5990 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
5991 if (theta.q < theta.p)
5992 theta.q+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00005993 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
cristy3ed852e2009-09-05 21:47:34 +00005994 (2.0*sqrt((double) (1.0/mid)))));
5995 path_q[q].x=box_q[1].x;
5996 path_q[q].y=box_q[1].y;
5997 q++;
cristybb503372010-05-27 20:51:26 +00005998 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00005999 {
6000 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6001 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
6002 (theta.p+delta_theta),DegreesToRadians(360.0))));
6003 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
6004 (theta.p+delta_theta),DegreesToRadians(360.0))));
6005 q++;
6006 }
6007 path_q[q++]=box_q[2];
6008 break;
6009 }
6010 default:
6011 break;
6012 }
6013 else
6014 switch (draw_info->linejoin)
6015 {
6016 case BevelJoin:
6017 {
6018 path_p[p++]=box_p[1];
6019 path_p[p++]=box_p[2];
6020 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6021 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6022 if (dot_product <= miterlimit)
6023 path_q[q++]=box_q[4];
6024 else
6025 {
6026 path_q[q++]=box_q[1];
6027 path_q[q++]=box_q[2];
6028 }
6029 break;
6030 }
6031 case MiterJoin:
6032 {
6033 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6034 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6035 if (dot_product <= miterlimit)
6036 {
6037 path_q[q++]=box_q[4];
6038 path_p[p++]=box_p[4];
6039 }
6040 else
6041 {
6042 path_q[q++]=box_q[1];
6043 path_q[q++]=box_q[2];
6044 path_p[p++]=box_p[1];
6045 path_p[p++]=box_p[2];
6046 }
6047 break;
6048 }
6049 case RoundJoin:
6050 {
6051 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6052 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6053 if (dot_product <= miterlimit)
6054 path_q[q++]=box_q[4];
6055 else
6056 {
6057 path_q[q++]=box_q[1];
6058 path_q[q++]=box_q[2];
6059 }
6060 center=polygon_primitive[n].point;
6061 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
6062 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
6063 if (theta.p < theta.q)
6064 theta.p+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006065 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
cristy3ed852e2009-09-05 21:47:34 +00006066 (2.0*sqrt((double) (1.0/mid)))));
6067 path_p[p++]=box_p[1];
cristybb503372010-05-27 20:51:26 +00006068 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006069 {
6070 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6071 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
6072 (theta.p+delta_theta),DegreesToRadians(360.0))));
6073 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
6074 (theta.p+delta_theta),DegreesToRadians(360.0))));
6075 p++;
6076 }
6077 path_p[p++]=box_p[2];
6078 break;
6079 }
6080 default:
6081 break;
6082 }
6083 slope.p=slope.q;
6084 inverse_slope.p=inverse_slope.q;
6085 box_p[0]=box_p[2];
6086 box_p[1]=box_p[3];
6087 box_q[0]=box_q[2];
6088 box_q[1]=box_q[3];
6089 dx.p=dx.q;
6090 dy.p=dy.q;
6091 n=i;
6092 }
6093 path_p[p++]=box_p[1];
6094 path_q[q++]=box_q[1];
6095 /*
6096 Trace stroked polygon.
6097 */
6098 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
6099 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
6100 if (stroke_polygon != (PrimitiveInfo *) NULL)
6101 {
cristybb503372010-05-27 20:51:26 +00006102 for (i=0; i < (ssize_t) p; i++)
cristy3ed852e2009-09-05 21:47:34 +00006103 {
6104 stroke_polygon[i]=polygon_primitive[0];
6105 stroke_polygon[i].point=path_p[i];
6106 }
6107 if (closed_path != MagickFalse)
6108 {
6109 stroke_polygon[i]=polygon_primitive[0];
6110 stroke_polygon[i].point=stroke_polygon[0].point;
6111 i++;
6112 }
cristybb503372010-05-27 20:51:26 +00006113 for ( ; i < (ssize_t) (p+q+closed_path); i++)
cristy3ed852e2009-09-05 21:47:34 +00006114 {
6115 stroke_polygon[i]=polygon_primitive[0];
6116 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
6117 }
6118 if (closed_path != MagickFalse)
6119 {
6120 stroke_polygon[i]=polygon_primitive[0];
6121 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
6122 i++;
6123 }
6124 stroke_polygon[i]=polygon_primitive[0];
6125 stroke_polygon[i].point=stroke_polygon[0].point;
6126 i++;
6127 stroke_polygon[i].primitive=UndefinedPrimitive;
cristybb503372010-05-27 20:51:26 +00006128 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
cristy3ed852e2009-09-05 21:47:34 +00006129 }
6130 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
6131 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
6132 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
6133 return(stroke_polygon);
6134}