blob: 2b234b2a4d1e46690fc9b70eb4ecbf2c90628e94 [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*/
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/studio.h"
49#include "MagickCore/annotate.h"
50#include "MagickCore/artifact.h"
51#include "MagickCore/blob.h"
52#include "MagickCore/cache.h"
53#include "MagickCore/cache-view.h"
54#include "MagickCore/color.h"
55#include "MagickCore/composite.h"
56#include "MagickCore/composite-private.h"
57#include "MagickCore/constitute.h"
58#include "MagickCore/draw.h"
59#include "MagickCore/draw-private.h"
60#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/gem.h"
64#include "MagickCore/geometry.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/paint.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/property.h"
74#include "MagickCore/resample.h"
75#include "MagickCore/resample-private.h"
76#include "MagickCore/string_.h"
77#include "MagickCore/string-private.h"
78#include "MagickCore/thread-private.h"
79#include "MagickCore/token.h"
80#include "MagickCore/transform.h"
81#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000082
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%
anthony36fe1752011-09-29 11:28:37 +0000218% CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
219% is specified, a new draw_info structure is created initialized to
220% default values, according to the given image_info.
cristy3ed852e2009-09-05 21:47:34 +0000221%
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");
anthony4301f052011-10-04 23:48:29 +0000243
cristy3ed852e2009-09-05 21:47:34 +0000244 GetDrawInfo(image_info,clone_info);
245 if (draw_info == (DrawInfo *) NULL)
246 return(clone_info);
anthony4301f052011-10-04 23:48:29 +0000247
cristy3ed852e2009-09-05 21:47:34 +0000248 if (clone_info->primitive != (char *) NULL)
249 (void) CloneString(&clone_info->primitive,draw_info->primitive);
250 if (draw_info->geometry != (char *) NULL)
251 (void) CloneString(&clone_info->geometry,draw_info->geometry);
252 clone_info->viewbox=draw_info->viewbox;
253 clone_info->affine=draw_info->affine;
254 clone_info->gravity=draw_info->gravity;
255 clone_info->fill=draw_info->fill;
256 clone_info->stroke=draw_info->stroke;
257 clone_info->stroke_width=draw_info->stroke_width;
258 if (draw_info->fill_pattern != (Image *) NULL)
259 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
260 &draw_info->fill_pattern->exception);
cristy3ed852e2009-09-05 21:47:34 +0000261 if (draw_info->stroke_pattern != (Image *) NULL)
262 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
263 MagickTrue,&draw_info->stroke_pattern->exception);
264 clone_info->stroke_antialias=draw_info->stroke_antialias;
265 clone_info->text_antialias=draw_info->text_antialias;
266 clone_info->fill_rule=draw_info->fill_rule;
267 clone_info->linecap=draw_info->linecap;
268 clone_info->linejoin=draw_info->linejoin;
269 clone_info->miterlimit=draw_info->miterlimit;
270 clone_info->dash_offset=draw_info->dash_offset;
271 clone_info->decorate=draw_info->decorate;
272 clone_info->compose=draw_info->compose;
273 if (draw_info->text != (char *) NULL)
274 (void) CloneString(&clone_info->text,draw_info->text);
275 if (draw_info->font != (char *) NULL)
276 (void) CloneString(&clone_info->font,draw_info->font);
277 if (draw_info->metrics != (char *) NULL)
278 (void) CloneString(&clone_info->metrics,draw_info->metrics);
279 if (draw_info->family != (char *) NULL)
280 (void) CloneString(&clone_info->family,draw_info->family);
281 clone_info->style=draw_info->style;
282 clone_info->stretch=draw_info->stretch;
283 clone_info->weight=draw_info->weight;
284 if (draw_info->encoding != (char *) NULL)
285 (void) CloneString(&clone_info->encoding,draw_info->encoding);
286 clone_info->pointsize=draw_info->pointsize;
287 clone_info->kerning=draw_info->kerning;
cristyb32b90a2009-09-07 21:45:48 +0000288 clone_info->interline_spacing=draw_info->interline_spacing;
cristy3ed852e2009-09-05 21:47:34 +0000289 clone_info->interword_spacing=draw_info->interword_spacing;
cristyc9b12952010-03-28 01:12:28 +0000290 clone_info->direction=draw_info->direction;
cristy3ed852e2009-09-05 21:47:34 +0000291 if (draw_info->density != (char *) NULL)
292 (void) CloneString(&clone_info->density,draw_info->density);
293 clone_info->align=draw_info->align;
294 clone_info->undercolor=draw_info->undercolor;
295 clone_info->border_color=draw_info->border_color;
296 if (draw_info->server_name != (char *) NULL)
297 (void) CloneString(&clone_info->server_name,draw_info->server_name);
298 if (draw_info->dash_pattern != (double *) NULL)
299 {
cristybb503372010-05-27 20:51:26 +0000300 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000301 x;
302
303 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
304 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
305 sizeof(*clone_info->dash_pattern));
306 if (clone_info->dash_pattern == (double *) NULL)
307 ThrowFatalException(ResourceLimitFatalError,
308 "UnableToAllocateDashPattern");
309 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
310 (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
311 }
312 clone_info->gradient=draw_info->gradient;
313 if (draw_info->gradient.stops != (StopInfo *) NULL)
314 {
cristybb503372010-05-27 20:51:26 +0000315 size_t
cristy3ed852e2009-09-05 21:47:34 +0000316 number_stops;
317
318 number_stops=clone_info->gradient.number_stops;
319 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
320 number_stops,sizeof(*clone_info->gradient.stops));
321 if (clone_info->gradient.stops == (StopInfo *) NULL)
322 ThrowFatalException(ResourceLimitFatalError,
323 "UnableToAllocateDashPattern");
324 (void) CopyMagickMemory(clone_info->gradient.stops,
325 draw_info->gradient.stops,(size_t) number_stops*
326 sizeof(*clone_info->gradient.stops));
327 }
328 if (draw_info->clip_mask != (char *) NULL)
329 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
330 clone_info->bounds=draw_info->bounds;
331 clone_info->clip_units=draw_info->clip_units;
332 clone_info->render=draw_info->render;
cristy4c08aed2011-07-01 19:47:50 +0000333 clone_info->alpha=draw_info->alpha;
cristy3ed852e2009-09-05 21:47:34 +0000334 clone_info->element_reference=draw_info->element_reference;
335 clone_info->debug=IsEventLogging();
336 return(clone_info);
337}
338
339/*
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341% %
342% %
343% %
344+ C o n v e r t P a t h T o P o l y g o n %
345% %
346% %
347% %
348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349%
350% ConvertPathToPolygon() converts a path to the more efficient sorted
351% rendering form.
352%
353% The format of the ConvertPathToPolygon method is:
354%
355% PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
356% const PathInfo *path_info)
357%
358% A description of each parameter follows:
359%
360% o Method ConvertPathToPolygon returns the path in a more efficient sorted
361% rendering form of type PolygonInfo.
362%
363% o draw_info: Specifies a pointer to an DrawInfo structure.
364%
365% o path_info: Specifies a pointer to an PathInfo structure.
366%
367%
368*/
369
370#if defined(__cplusplus) || defined(c_plusplus)
371extern "C" {
372#endif
373
374static int CompareEdges(const void *x,const void *y)
375{
376 register const EdgeInfo
377 *p,
378 *q;
379
380 /*
381 Compare two edges.
382 */
383 p=(const EdgeInfo *) x;
384 q=(const EdgeInfo *) y;
385 if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
386 return(1);
387 if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
388 return(-1);
389 if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
390 return(1);
391 if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
392 return(-1);
393 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
394 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
395 return(1);
396 return(-1);
397}
398
399#if defined(__cplusplus) || defined(c_plusplus)
400}
401#endif
402
403static void LogPolygonInfo(const PolygonInfo *polygon_info)
404{
405 register EdgeInfo
406 *p;
407
cristybb503372010-05-27 20:51:26 +0000408 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000409 i,
410 j;
411
412 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
413 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +0000414 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000415 {
cristye8c25f92010-06-03 00:53:06 +0000416 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
417 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000418 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
419 p->direction != MagickFalse ? "down" : "up");
420 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
421 p->ghostline != MagickFalse ? "transparent" : "opaque");
422 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000423 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000424 p->bounds.x2,p->bounds.y2);
cristybb503372010-05-27 20:51:26 +0000425 for (j=0; j < (ssize_t) p->number_points; j++)
cristy14388de2011-05-15 14:57:16 +0000426 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g",
cristy3ed852e2009-09-05 21:47:34 +0000427 p->points[j].x,p->points[j].y);
428 p++;
429 }
430 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
431}
432
cristybb503372010-05-27 20:51:26 +0000433static void ReversePoints(PointInfo *points,const size_t number_points)
cristy3ed852e2009-09-05 21:47:34 +0000434{
435 PointInfo
436 point;
437
cristybb503372010-05-27 20:51:26 +0000438 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000439 i;
440
cristybb503372010-05-27 20:51:26 +0000441 for (i=0; i < (ssize_t) (number_points >> 1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000442 {
443 point=points[i];
444 points[i]=points[number_points-(i+1)];
445 points[number_points-(i+1)]=point;
446 }
447}
448
449static PolygonInfo *ConvertPathToPolygon(
450 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
451{
cristycee97112010-05-28 00:44:52 +0000452 long
cristy3ed852e2009-09-05 21:47:34 +0000453 direction,
454 next_direction;
455
456 PointInfo
457 point,
458 *points;
459
460 PolygonInfo
461 *polygon_info;
462
463 SegmentInfo
464 bounds;
465
cristybb503372010-05-27 20:51:26 +0000466 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000467 i,
468 n;
469
470 MagickBooleanType
471 ghostline;
472
cristybb503372010-05-27 20:51:26 +0000473 size_t
cristy3ed852e2009-09-05 21:47:34 +0000474 edge,
475 number_edges,
476 number_points;
477
478 /*
479 Convert a path to the more efficient sorted rendering form.
480 */
cristy73bd4a52010-10-05 11:24:23 +0000481 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +0000482 if (polygon_info == (PolygonInfo *) NULL)
483 return((PolygonInfo *) NULL);
484 number_edges=16;
485 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
486 sizeof(*polygon_info->edges));
487 if (polygon_info->edges == (EdgeInfo *) NULL)
488 return((PolygonInfo *) NULL);
489 direction=0;
490 edge=0;
491 ghostline=MagickFalse;
492 n=0;
493 number_points=0;
494 points=(PointInfo *) NULL;
495 (void) ResetMagickMemory(&point,0,sizeof(point));
496 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
497 for (i=0; path_info[i].code != EndCode; i++)
498 {
499 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
500 (path_info[i].code == GhostlineCode))
501 {
502 /*
503 Move to.
504 */
505 if ((points != (PointInfo *) NULL) && (n >= 2))
506 {
507 if (edge == number_edges)
508 {
509 number_edges<<=1;
510 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
511 polygon_info->edges,(size_t) number_edges,
512 sizeof(*polygon_info->edges));
513 if (polygon_info->edges == (EdgeInfo *) NULL)
514 return((PolygonInfo *) NULL);
515 }
cristybb503372010-05-27 20:51:26 +0000516 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000517 polygon_info->edges[edge].scanline=(-1.0);
518 polygon_info->edges[edge].highwater=0;
519 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000520 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000521 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000522 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000523 polygon_info->edges[edge].points=points;
524 polygon_info->edges[edge].bounds=bounds;
525 polygon_info->edges[edge].bounds.y1=points[0].y;
526 polygon_info->edges[edge].bounds.y2=points[n-1].y;
527 points=(PointInfo *) NULL;
528 ghostline=MagickFalse;
529 edge++;
530 }
531 if (points == (PointInfo *) NULL)
532 {
533 number_points=16;
534 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
535 sizeof(*points));
536 if (points == (PointInfo *) NULL)
537 return((PolygonInfo *) NULL);
538 }
539 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
540 point=path_info[i].point;
541 points[0]=point;
542 bounds.x1=point.x;
543 bounds.x2=point.x;
544 direction=0;
545 n=1;
546 continue;
547 }
548 /*
549 Line to.
550 */
551 next_direction=((path_info[i].point.y > point.y) ||
552 ((path_info[i].point.y == point.y) &&
553 (path_info[i].point.x > point.x))) ? 1 : -1;
554 if ((direction != 0) && (direction != next_direction))
555 {
556 /*
557 New edge.
558 */
559 point=points[n-1];
560 if (edge == number_edges)
561 {
562 number_edges<<=1;
563 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
564 polygon_info->edges,(size_t) number_edges,
565 sizeof(*polygon_info->edges));
566 if (polygon_info->edges == (EdgeInfo *) NULL)
567 return((PolygonInfo *) NULL);
568 }
cristybb503372010-05-27 20:51:26 +0000569 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000570 polygon_info->edges[edge].scanline=(-1.0);
571 polygon_info->edges[edge].highwater=0;
572 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000573 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000574 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000575 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000576 polygon_info->edges[edge].points=points;
577 polygon_info->edges[edge].bounds=bounds;
578 polygon_info->edges[edge].bounds.y1=points[0].y;
579 polygon_info->edges[edge].bounds.y2=points[n-1].y;
580 number_points=16;
581 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
582 sizeof(*points));
583 if (points == (PointInfo *) NULL)
584 return((PolygonInfo *) NULL);
585 n=1;
586 ghostline=MagickFalse;
587 points[0]=point;
588 bounds.x1=point.x;
589 bounds.x2=point.x;
590 edge++;
591 }
592 direction=next_direction;
593 if (points == (PointInfo *) NULL)
594 continue;
cristybb503372010-05-27 20:51:26 +0000595 if (n == (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +0000596 {
597 number_points<<=1;
598 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
599 sizeof(*points));
600 if (points == (PointInfo *) NULL)
601 return((PolygonInfo *) NULL);
602 }
603 point=path_info[i].point;
604 points[n]=point;
605 if (point.x < bounds.x1)
606 bounds.x1=point.x;
607 if (point.x > bounds.x2)
608 bounds.x2=point.x;
609 n++;
610 }
611 if (points != (PointInfo *) NULL)
612 {
613 if (n < 2)
614 points=(PointInfo *) RelinquishMagickMemory(points);
615 else
616 {
617 if (edge == number_edges)
618 {
619 number_edges<<=1;
620 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
621 polygon_info->edges,(size_t) number_edges,
622 sizeof(*polygon_info->edges));
623 if (polygon_info->edges == (EdgeInfo *) NULL)
624 return((PolygonInfo *) NULL);
625 }
cristybb503372010-05-27 20:51:26 +0000626 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000627 polygon_info->edges[edge].scanline=(-1.0);
628 polygon_info->edges[edge].highwater=0;
629 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000630 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000631 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000632 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000633 polygon_info->edges[edge].points=points;
634 polygon_info->edges[edge].bounds=bounds;
635 polygon_info->edges[edge].bounds.y1=points[0].y;
636 polygon_info->edges[edge].bounds.y2=points[n-1].y;
637 ghostline=MagickFalse;
638 edge++;
639 }
640 }
641 polygon_info->number_edges=edge;
642 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
643 sizeof(*polygon_info->edges),CompareEdges);
644 if (IsEventLogging() != MagickFalse)
645 LogPolygonInfo(polygon_info);
646 return(polygon_info);
647}
648
649/*
650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651% %
652% %
653% %
654+ C o n v e r t P r i m i t i v e T o P a t h %
655% %
656% %
657% %
658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659%
660% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
661% path structure.
662%
663% The format of the ConvertPrimitiveToPath method is:
664%
665% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
666% const PrimitiveInfo *primitive_info)
667%
668% A description of each parameter follows:
669%
670% o Method ConvertPrimitiveToPath returns a vector path structure of type
671% PathInfo.
672%
673% o draw_info: a structure of type DrawInfo.
674%
675% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
676%
677%
678*/
679
680static void LogPathInfo(const PathInfo *path_info)
681{
682 register const PathInfo
683 *p;
684
685 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
686 for (p=path_info; p->code != EndCode; p++)
687 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000688 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
cristy3ed852e2009-09-05 21:47:34 +0000689 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
690 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
691 "?");
692 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
693}
694
695static PathInfo *ConvertPrimitiveToPath(
696 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
697{
cristy3ed852e2009-09-05 21:47:34 +0000698 PathInfo
699 *path_info;
700
701 PathInfoCode
702 code;
703
704 PointInfo
705 p,
706 q;
707
cristybb503372010-05-27 20:51:26 +0000708 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000709 i,
710 n;
711
cristy826a5472010-08-31 23:21:38 +0000712 ssize_t
713 coordinates,
714 start;
715
cristy3ed852e2009-09-05 21:47:34 +0000716 /*
717 Converts a PrimitiveInfo structure into a vector path structure.
718 */
719 switch (primitive_info->primitive)
720 {
721 case PointPrimitive:
722 case ColorPrimitive:
723 case MattePrimitive:
724 case TextPrimitive:
725 case ImagePrimitive:
726 return((PathInfo *) NULL);
727 default:
728 break;
729 }
730 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
731 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
732 sizeof(*path_info));
733 if (path_info == (PathInfo *) NULL)
734 return((PathInfo *) NULL);
735 coordinates=0;
736 n=0;
737 p.x=(-1.0);
738 p.y=(-1.0);
739 q.x=(-1.0);
740 q.y=(-1.0);
741 start=0;
742 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
743 {
744 code=LineToCode;
745 if (coordinates <= 0)
746 {
cristybb503372010-05-27 20:51:26 +0000747 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +0000748 p=primitive_info[i].point;
749 start=n;
750 code=MoveToCode;
751 }
752 coordinates--;
753 /*
754 Eliminate duplicate points.
755 */
756 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
757 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
758 {
759 path_info[n].code=code;
760 path_info[n].point=primitive_info[i].point;
761 q=primitive_info[i].point;
762 n++;
763 }
764 if (coordinates > 0)
765 continue;
766 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
767 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
768 continue;
769 /*
770 Mark the p point as open if it does not match the q.
771 */
772 path_info[start].code=OpenCode;
773 path_info[n].code=GhostlineCode;
774 path_info[n].point=primitive_info[i].point;
775 n++;
776 path_info[n].code=LineToCode;
777 path_info[n].point=p;
778 n++;
779 }
780 path_info[n].code=EndCode;
781 path_info[n].point.x=0.0;
782 path_info[n].point.y=0.0;
783 if (IsEventLogging() != MagickFalse)
784 LogPathInfo(path_info);
785 return(path_info);
786}
787
788/*
789%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790% %
791% %
792% %
793% D e s t r o y D r a w I n f o %
794% %
795% %
796% %
797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798%
799% DestroyDrawInfo() deallocates memory associated with an DrawInfo
800% structure.
801%
802% The format of the DestroyDrawInfo method is:
803%
804% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
805%
806% A description of each parameter follows:
807%
808% o draw_info: the draw info.
809%
810*/
811MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
812{
813 if (draw_info->debug != MagickFalse)
814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
815 assert(draw_info != (DrawInfo *) NULL);
816 assert(draw_info->signature == MagickSignature);
817 if (draw_info->primitive != (char *) NULL)
818 draw_info->primitive=DestroyString(draw_info->primitive);
819 if (draw_info->text != (char *) NULL)
820 draw_info->text=DestroyString(draw_info->text);
821 if (draw_info->geometry != (char *) NULL)
822 draw_info->geometry=DestroyString(draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000823 if (draw_info->fill_pattern != (Image *) NULL)
824 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
825 if (draw_info->stroke_pattern != (Image *) NULL)
826 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
827 if (draw_info->font != (char *) NULL)
828 draw_info->font=DestroyString(draw_info->font);
829 if (draw_info->metrics != (char *) NULL)
830 draw_info->metrics=DestroyString(draw_info->metrics);
831 if (draw_info->family != (char *) NULL)
832 draw_info->family=DestroyString(draw_info->family);
833 if (draw_info->encoding != (char *) NULL)
834 draw_info->encoding=DestroyString(draw_info->encoding);
835 if (draw_info->density != (char *) NULL)
836 draw_info->density=DestroyString(draw_info->density);
837 if (draw_info->server_name != (char *) NULL)
838 draw_info->server_name=(char *)
839 RelinquishMagickMemory(draw_info->server_name);
840 if (draw_info->dash_pattern != (double *) NULL)
841 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
842 draw_info->dash_pattern);
843 if (draw_info->gradient.stops != (StopInfo *) NULL)
844 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
845 draw_info->gradient.stops);
846 if (draw_info->clip_mask != (char *) NULL)
847 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
848 draw_info->signature=(~MagickSignature);
849 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
850 return(draw_info);
851}
852
853/*
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855% %
856% %
857% %
858+ D e s t r o y E d g e %
859% %
860% %
861% %
862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863%
864% DestroyEdge() destroys the specified polygon edge.
865%
866% The format of the DestroyEdge method is:
867%
cristybb503372010-05-27 20:51:26 +0000868% ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
cristy3ed852e2009-09-05 21:47:34 +0000869%
870% A description of each parameter follows:
871%
872% o polygon_info: Specifies a pointer to an PolygonInfo structure.
873%
874% o edge: the polygon edge number to destroy.
875%
876*/
cristybb503372010-05-27 20:51:26 +0000877static size_t DestroyEdge(PolygonInfo *polygon_info,
878 const size_t edge)
cristy3ed852e2009-09-05 21:47:34 +0000879{
880 assert(edge < polygon_info->number_edges);
881 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
882 polygon_info->edges[edge].points);
883 polygon_info->number_edges--;
884 if (edge < polygon_info->number_edges)
885 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
886 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
887 return(polygon_info->number_edges);
888}
889
890/*
891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892% %
893% %
894% %
895+ D e s t r o y P o l y g o n I n f o %
896% %
897% %
898% %
899%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900%
901% DestroyPolygonInfo() destroys the PolygonInfo data structure.
902%
903% The format of the DestroyPolygonInfo method is:
904%
905% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
906%
907% A description of each parameter follows:
908%
909% o polygon_info: Specifies a pointer to an PolygonInfo structure.
910%
911*/
912static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
913{
cristybb503372010-05-27 20:51:26 +0000914 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000915 i;
916
cristybb503372010-05-27 20:51:26 +0000917 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000918 polygon_info->edges[i].points=(PointInfo *)
919 RelinquishMagickMemory(polygon_info->edges[i].points);
920 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
921 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
922}
923
924/*
925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
926% %
927% %
928% %
929% D r a w A f f i n e I m a g e %
930% %
931% %
932% %
933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
934%
935% DrawAffineImage() composites the source over the destination image as
936% dictated by the affine transform.
937%
938% The format of the DrawAffineImage method is:
939%
940% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
941% const AffineMatrix *affine)
942%
943% A description of each parameter follows:
944%
945% o image: the image.
946%
947% o source: the source image.
948%
949% o affine: the affine transform.
950%
951*/
952static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
953 const double y,const SegmentInfo *edge)
954{
955 double
956 intercept,
957 z;
958
959 register double
960 x;
961
962 SegmentInfo
963 inverse_edge;
964
965 /*
966 Determine left and right edges.
967 */
968 inverse_edge.x1=edge->x1;
969 inverse_edge.y1=edge->y1;
970 inverse_edge.x2=edge->x2;
971 inverse_edge.y2=edge->y2;
972 z=affine->ry*y+affine->tx;
973 if (affine->sx > MagickEpsilon)
974 {
975 intercept=(-z/affine->sx);
976 x=intercept+MagickEpsilon;
977 if (x > inverse_edge.x1)
978 inverse_edge.x1=x;
979 intercept=(-z+(double) image->columns)/affine->sx;
980 x=intercept-MagickEpsilon;
981 if (x < inverse_edge.x2)
982 inverse_edge.x2=x;
983 }
984 else
985 if (affine->sx < -MagickEpsilon)
986 {
987 intercept=(-z+(double) image->columns)/affine->sx;
988 x=intercept+MagickEpsilon;
989 if (x > inverse_edge.x1)
990 inverse_edge.x1=x;
991 intercept=(-z/affine->sx);
992 x=intercept-MagickEpsilon;
993 if (x < inverse_edge.x2)
994 inverse_edge.x2=x;
995 }
996 else
cristybb503372010-05-27 20:51:26 +0000997 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
cristy3ed852e2009-09-05 21:47:34 +0000998 {
999 inverse_edge.x2=edge->x1;
1000 return(inverse_edge);
1001 }
1002 /*
1003 Determine top and bottom edges.
1004 */
1005 z=affine->sy*y+affine->ty;
1006 if (affine->rx > MagickEpsilon)
1007 {
1008 intercept=(-z/affine->rx);
1009 x=intercept+MagickEpsilon;
1010 if (x > inverse_edge.x1)
1011 inverse_edge.x1=x;
1012 intercept=(-z+(double) image->rows)/affine->rx;
1013 x=intercept-MagickEpsilon;
1014 if (x < inverse_edge.x2)
1015 inverse_edge.x2=x;
1016 }
1017 else
1018 if (affine->rx < -MagickEpsilon)
1019 {
1020 intercept=(-z+(double) image->rows)/affine->rx;
1021 x=intercept+MagickEpsilon;
1022 if (x > inverse_edge.x1)
1023 inverse_edge.x1=x;
1024 intercept=(-z/affine->rx);
1025 x=intercept-MagickEpsilon;
1026 if (x < inverse_edge.x2)
1027 inverse_edge.x2=x;
1028 }
1029 else
cristybb503372010-05-27 20:51:26 +00001030 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001031 {
1032 inverse_edge.x2=edge->x2;
1033 return(inverse_edge);
1034 }
1035 return(inverse_edge);
1036}
1037
1038static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1039{
1040 AffineMatrix
1041 inverse_affine;
1042
1043 double
1044 determinant;
1045
1046 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
1047 inverse_affine.sx=determinant*affine->sy;
1048 inverse_affine.rx=determinant*(-affine->rx);
1049 inverse_affine.ry=determinant*(-affine->ry);
1050 inverse_affine.sy=determinant*affine->sx;
1051 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1052 inverse_affine.ry;
1053 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1054 inverse_affine.sy;
1055 return(inverse_affine);
1056}
1057
cristybb503372010-05-27 20:51:26 +00001058static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +00001059{
1060 if (x < 0)
1061 return(-x);
1062 return(x);
1063}
1064
1065static inline double MagickMax(const double x,const double y)
1066{
1067 if (x > y)
1068 return(x);
1069 return(y);
1070}
1071
1072static inline double MagickMin(const double x,const double y)
1073{
1074 if (x < y)
1075 return(x);
1076 return(y);
1077}
1078
1079MagickExport MagickBooleanType DrawAffineImage(Image *image,
1080 const Image *source,const AffineMatrix *affine)
1081{
1082 AffineMatrix
1083 inverse_affine;
1084
cristyfa112112010-01-04 17:48:07 +00001085 CacheView
1086 *image_view,
1087 *source_view;
1088
cristy3ed852e2009-09-05 21:47:34 +00001089 ExceptionInfo
1090 *exception;
1091
cristy3ed852e2009-09-05 21:47:34 +00001092 MagickBooleanType
1093 status;
1094
cristy4c08aed2011-07-01 19:47:50 +00001095 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001096 zero;
1097
1098 PointInfo
1099 extent[4],
1100 min,
1101 max,
1102 point;
1103
cristybb503372010-05-27 20:51:26 +00001104 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001105 i;
1106
cristy3ed852e2009-09-05 21:47:34 +00001107 SegmentInfo
1108 edge;
1109
cristy826a5472010-08-31 23:21:38 +00001110 ssize_t
1111 y;
1112
cristy3ed852e2009-09-05 21:47:34 +00001113 /*
1114 Determine bounding box.
1115 */
1116 assert(image != (Image *) NULL);
1117 assert(image->signature == MagickSignature);
1118 if (image->debug != MagickFalse)
1119 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1120 assert(source != (const Image *) NULL);
1121 assert(source->signature == MagickSignature);
1122 assert(affine != (AffineMatrix *) NULL);
1123 extent[0].x=0.0;
1124 extent[0].y=0.0;
1125 extent[1].x=(double) source->columns-1.0;
1126 extent[1].y=0.0;
1127 extent[2].x=(double) source->columns-1.0;
1128 extent[2].y=(double) source->rows-1.0;
1129 extent[3].x=0.0;
1130 extent[3].y=(double) source->rows-1.0;
1131 for (i=0; i < 4; i++)
1132 {
1133 point=extent[i];
1134 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1135 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1136 }
1137 min=extent[0];
1138 max=extent[0];
1139 for (i=1; i < 4; i++)
1140 {
1141 if (min.x > extent[i].x)
1142 min.x=extent[i].x;
1143 if (min.y > extent[i].y)
1144 min.y=extent[i].y;
1145 if (max.x < extent[i].x)
1146 max.x=extent[i].x;
1147 if (max.y < extent[i].y)
1148 max.y=extent[i].y;
1149 }
1150 /*
1151 Affine transform image.
1152 */
cristy874276b2011-08-05 19:29:22 +00001153 exception=(&image->exception);
cristy574cc262011-08-05 01:23:58 +00001154 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001155 return(MagickFalse);
1156 status=MagickTrue;
1157 edge.x1=MagickMax(min.x,0.0);
1158 edge.y1=MagickMax(min.y,0.0);
1159 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1160 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1161 inverse_affine=InverseAffineMatrix(affine);
cristy4c08aed2011-07-01 19:47:50 +00001162 GetPixelInfo(image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00001163 image_view=AcquireCacheView(image);
1164 source_view=AcquireCacheView(source);
cristyb5d5f722009-11-04 03:03:49 +00001165#if defined(MAGICKCORE_OPENMP_SUPPORT)
1166 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001167#endif
cristybb503372010-05-27 20:51:26 +00001168 for (y=(ssize_t) ceil(edge.y1-0.5); y <= (ssize_t) floor(edge.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00001169 {
cristy4c08aed2011-07-01 19:47:50 +00001170 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001171 composite,
1172 pixel;
1173
1174 PointInfo
1175 point;
1176
cristybb503372010-05-27 20:51:26 +00001177 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001178 x;
1179
cristy4c08aed2011-07-01 19:47:50 +00001180 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001181 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001182
1183 SegmentInfo
1184 inverse_edge;
1185
cristy826a5472010-08-31 23:21:38 +00001186 ssize_t
1187 x_offset;
1188
cristy3ed852e2009-09-05 21:47:34 +00001189 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1190 if (inverse_edge.x2 < inverse_edge.x1)
1191 continue;
cristy6ebe97c2010-07-03 01:17:28 +00001192 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
1193 0.5),y,(size_t) ((ssize_t) floor(inverse_edge.x2+0.5)-(ssize_t) floor(
cristy06609ee2010-03-17 20:21:27 +00001194 inverse_edge.x1+0.5)+1),1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001195 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001196 continue;
cristy3ed852e2009-09-05 21:47:34 +00001197 pixel=zero;
1198 composite=zero;
1199 x_offset=0;
cristybb503372010-05-27 20:51:26 +00001200 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 +00001201 {
1202 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1203 inverse_affine.tx;
1204 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1205 inverse_affine.ty;
cristy4c08aed2011-07-01 19:47:50 +00001206 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1207 point.x,point.y,&pixel,exception);
1208 SetPixelInfo(image,q,&composite);
1209 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1210 &composite);
1211 SetPixelPixelInfo(image,&composite,q);
cristy3ed852e2009-09-05 21:47:34 +00001212 x_offset++;
cristyed231572011-07-14 02:18:59 +00001213 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001214 }
1215 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1216 status=MagickFalse;
1217 }
cristy3ed852e2009-09-05 21:47:34 +00001218 source_view=DestroyCacheView(source_view);
1219 image_view=DestroyCacheView(image_view);
1220 return(status);
1221}
1222
1223/*
1224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225% %
1226% %
1227% %
1228+ D r a w B o u n d i n g R e c t a n g l e s %
1229% %
1230% %
1231% %
1232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1233%
1234% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1235% is only useful for developers debugging the rendering algorithm.
1236%
1237% The format of the DrawBoundingRectangles method is:
1238%
1239% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1240% PolygonInfo *polygon_info)
1241%
1242% A description of each parameter follows:
1243%
1244% o image: the image.
1245%
1246% o draw_info: the draw info.
1247%
1248% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1249%
1250*/
1251static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1252 const PolygonInfo *polygon_info)
1253{
1254 DrawInfo
1255 *clone_info;
1256
cristy3ed852e2009-09-05 21:47:34 +00001257 MagickRealType
1258 mid;
1259
1260 PointInfo
1261 end,
1262 resolution,
1263 start;
1264
1265 PrimitiveInfo
1266 primitive_info[6];
1267
cristybb503372010-05-27 20:51:26 +00001268 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001269 i;
1270
1271 SegmentInfo
1272 bounds;
1273
cristy826a5472010-08-31 23:21:38 +00001274 ssize_t
1275 coordinates;
1276
cristy3ed852e2009-09-05 21:47:34 +00001277 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
cristy9950d572011-10-01 18:22:35 +00001278 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill,
1279 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001280 resolution.x=DefaultResolution;
1281 resolution.y=DefaultResolution;
1282 if (clone_info->density != (char *) NULL)
1283 {
1284 GeometryInfo
1285 geometry_info;
1286
1287 MagickStatusType
1288 flags;
1289
1290 flags=ParseGeometry(clone_info->density,&geometry_info);
1291 resolution.x=geometry_info.rho;
1292 resolution.y=geometry_info.sigma;
1293 if ((flags & SigmaValue) == MagickFalse)
1294 resolution.y=resolution.x;
1295 }
1296 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1297 clone_info->stroke_width/2.0;
1298 bounds.x1=0.0;
1299 bounds.y1=0.0;
1300 bounds.x2=0.0;
1301 bounds.y2=0.0;
1302 if (polygon_info != (PolygonInfo *) NULL)
1303 {
1304 bounds=polygon_info->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00001305 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001306 {
1307 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1308 bounds.x1=polygon_info->edges[i].bounds.x1;
1309 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1310 bounds.y1=polygon_info->edges[i].bounds.y1;
1311 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1312 bounds.x2=polygon_info->edges[i].bounds.x2;
1313 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1314 bounds.y2=polygon_info->edges[i].bounds.y2;
1315 }
1316 bounds.x1-=mid;
1317 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1318 image->columns ? (double) image->columns-1 : bounds.x1;
1319 bounds.y1-=mid;
1320 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1321 image->rows ? (double) image->rows-1 : bounds.y1;
1322 bounds.x2+=mid;
1323 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1324 image->columns ? (double) image->columns-1 : bounds.x2;
1325 bounds.y2+=mid;
1326 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1327 image->rows ? (double) image->rows-1 : bounds.y2;
cristybb503372010-05-27 20:51:26 +00001328 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001329 {
1330 if (polygon_info->edges[i].direction != 0)
cristy9950d572011-10-01 18:22:35 +00001331 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke,
cristy3ed852e2009-09-05 21:47:34 +00001332 &image->exception);
1333 else
cristy9950d572011-10-01 18:22:35 +00001334 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke,
cristy3ed852e2009-09-05 21:47:34 +00001335 &image->exception);
1336 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1337 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1338 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1339 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1340 primitive_info[0].primitive=RectanglePrimitive;
1341 TraceRectangle(primitive_info,start,end);
1342 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001343 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001344 primitive_info[coordinates].primitive=UndefinedPrimitive;
1345 (void) DrawPrimitive(image,clone_info,primitive_info);
1346 }
1347 }
cristy9950d572011-10-01 18:22:35 +00001348 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke,
1349 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001350 start.x=(double) (bounds.x1-mid);
1351 start.y=(double) (bounds.y1-mid);
1352 end.x=(double) (bounds.x2+mid);
1353 end.y=(double) (bounds.y2+mid);
1354 primitive_info[0].primitive=RectanglePrimitive;
1355 TraceRectangle(primitive_info,start,end);
1356 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001357 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001358 primitive_info[coordinates].primitive=UndefinedPrimitive;
1359 (void) DrawPrimitive(image,clone_info,primitive_info);
1360 clone_info=DestroyDrawInfo(clone_info);
1361}
1362
1363/*
1364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1365% %
1366% %
1367% %
1368% D r a w C l i p P a t h %
1369% %
1370% %
1371% %
1372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1373%
1374% DrawClipPath() draws the clip path on the image mask.
1375%
1376% The format of the DrawClipPath method is:
1377%
1378% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00001379% const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001380%
1381% A description of each parameter follows:
1382%
1383% o image: the image.
1384%
1385% o draw_info: the draw info.
1386%
1387% o name: the name of the clip path.
1388%
cristy018f07f2011-09-04 21:15:19 +00001389% o exception: return any errors or warnings in this structure.
1390%
cristy3ed852e2009-09-05 21:47:34 +00001391*/
1392MagickExport MagickBooleanType DrawClipPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00001393 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001394{
1395 char
1396 clip_mask[MaxTextExtent];
1397
1398 const char
1399 *value;
1400
1401 DrawInfo
1402 *clone_info;
1403
1404 MagickStatusType
1405 status;
1406
1407 assert(image != (Image *) NULL);
1408 assert(image->signature == MagickSignature);
1409 if (image->debug != MagickFalse)
1410 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1411 assert(draw_info != (const DrawInfo *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001412 (void) FormatLocaleString(clip_mask,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00001413 value=GetImageArtifact(image,clip_mask);
1414 if (value == (const char *) NULL)
1415 return(MagickFalse);
1416 if (image->clip_mask == (Image *) NULL)
1417 {
1418 Image
1419 *clip_mask;
1420
1421 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
1422 &image->exception);
1423 if (clip_mask == (Image *) NULL)
1424 return(MagickFalse);
cristy018f07f2011-09-04 21:15:19 +00001425 (void) SetImageClipMask(image,clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001426 clip_mask=DestroyImage(clip_mask);
1427 }
cristy9950d572011-10-01 18:22:35 +00001428 (void) QueryColorCompliance("#00000000",AllCompliance,
1429 &image->clip_mask->background_color,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001430 image->clip_mask->background_color.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001431 (void) SetImageBackgroundColor(image->clip_mask);
1432 if (image->debug != MagickFalse)
1433 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1434 draw_info->clip_mask);
1435 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1436 (void) CloneString(&clone_info->primitive,value);
cristy9950d572011-10-01 18:22:35 +00001437 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1438 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001439 clone_info->clip_mask=(char *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001440 status=DrawImage(image->clip_mask,clone_info,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00001441 status|=NegateImage(image->clip_mask,MagickFalse,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001442 clone_info=DestroyDrawInfo(clone_info);
1443 if (image->debug != MagickFalse)
1444 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1445 return(status != 0 ? MagickTrue : MagickFalse);
1446}
1447
1448/*
1449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450% %
1451% %
1452% %
1453+ D r a w D a s h P o l y g o n %
1454% %
1455% %
1456% %
1457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458%
1459% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1460% image while respecting the dash offset and dash pattern attributes.
1461%
1462% The format of the DrawDashPolygon method is:
1463%
1464% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1465% const PrimitiveInfo *primitive_info,Image *image)
1466%
1467% A description of each parameter follows:
1468%
1469% o draw_info: the draw info.
1470%
1471% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1472%
1473% o image: the image.
1474%
1475%
1476*/
1477static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1478 const PrimitiveInfo *primitive_info,Image *image)
1479{
1480 DrawInfo
1481 *clone_info;
1482
cristy3ed852e2009-09-05 21:47:34 +00001483 MagickRealType
1484 length,
1485 maximum_length,
1486 offset,
1487 scale,
1488 total_length;
1489
1490 MagickStatusType
1491 status;
1492
1493 PrimitiveInfo
1494 *dash_polygon;
1495
cristybb503372010-05-27 20:51:26 +00001496 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001497 i;
1498
1499 register MagickRealType
1500 dx,
1501 dy;
1502
cristybb503372010-05-27 20:51:26 +00001503 size_t
cristy3ed852e2009-09-05 21:47:34 +00001504 number_vertices;
1505
cristy826a5472010-08-31 23:21:38 +00001506 ssize_t
1507 j,
1508 n;
1509
cristy3ed852e2009-09-05 21:47:34 +00001510 assert(draw_info != (const DrawInfo *) NULL);
1511 if (image->debug != MagickFalse)
1512 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1513 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1514 clone_info->miterlimit=0;
1515 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
cristybb503372010-05-27 20:51:26 +00001516 number_vertices=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001517 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1518 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
1519 if (dash_polygon == (PrimitiveInfo *) NULL)
1520 return(MagickFalse);
1521 dash_polygon[0]=primitive_info[0];
1522 scale=ExpandAffine(&draw_info->affine);
1523 length=scale*(draw_info->dash_pattern[0]-0.5);
1524 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
1525 j=1;
1526 for (n=0; offset > 0.0; j=0)
1527 {
1528 if (draw_info->dash_pattern[n] <= 0.0)
1529 break;
1530 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1531 if (offset > length)
1532 {
1533 offset-=length;
1534 n++;
1535 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1536 continue;
1537 }
1538 if (offset < length)
1539 {
1540 length-=offset;
1541 offset=0.0;
1542 break;
1543 }
1544 offset=0.0;
1545 n++;
1546 }
1547 status=MagickTrue;
1548 maximum_length=0.0;
1549 total_length=0.0;
cristybb503372010-05-27 20:51:26 +00001550 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00001551 {
1552 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1553 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1554 maximum_length=hypot((double) dx,dy);
1555 if (length == 0.0)
1556 {
1557 n++;
1558 if (draw_info->dash_pattern[n] == 0.0)
1559 n=0;
1560 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1561 }
1562 for (total_length=0.0; (total_length+length) < maximum_length; )
1563 {
1564 total_length+=length;
1565 if ((n & 0x01) != 0)
1566 {
1567 dash_polygon[0]=primitive_info[0];
1568 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1569 total_length/maximum_length);
1570 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1571 total_length/maximum_length);
1572 j=1;
1573 }
1574 else
1575 {
cristybb503372010-05-27 20:51:26 +00001576 if ((j+1) > (ssize_t) (2*number_vertices))
cristy3ed852e2009-09-05 21:47:34 +00001577 break;
1578 dash_polygon[j]=primitive_info[i-1];
1579 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1580 total_length/maximum_length);
1581 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1582 total_length/maximum_length);
1583 dash_polygon[j].coordinates=1;
1584 j++;
cristybb503372010-05-27 20:51:26 +00001585 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001586 dash_polygon[j].primitive=UndefinedPrimitive;
1587 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
1588 }
1589 n++;
1590 if (draw_info->dash_pattern[n] == 0.0)
1591 n=0;
1592 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1593 }
1594 length-=(maximum_length-total_length);
1595 if ((n & 0x01) != 0)
1596 continue;
1597 dash_polygon[j]=primitive_info[i];
1598 dash_polygon[j].coordinates=1;
1599 j++;
1600 }
1601 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
1602 {
1603 dash_polygon[j]=primitive_info[i-1];
1604 dash_polygon[j].point.x+=MagickEpsilon;
1605 dash_polygon[j].point.y+=MagickEpsilon;
1606 dash_polygon[j].coordinates=1;
1607 j++;
cristybb503372010-05-27 20:51:26 +00001608 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001609 dash_polygon[j].primitive=UndefinedPrimitive;
1610 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
1611 }
1612 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1613 clone_info=DestroyDrawInfo(clone_info);
1614 if (image->debug != MagickFalse)
1615 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1616 return(status != 0 ? MagickTrue : MagickFalse);
1617}
1618
1619/*
1620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621% %
1622% %
1623% %
1624% D r a w I m a g e %
1625% %
1626% %
1627% %
1628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629%
1630% DrawImage() draws a graphic primitive on your image. The primitive
1631% may be represented as a string or filename. Precede the filename with an
1632% "at" sign (@) and the contents of the file are drawn on the image. You
1633% can affect how text is drawn by setting one or more members of the draw
1634% info structure.
1635%
1636% The format of the DrawImage method is:
1637%
cristy018f07f2011-09-04 21:15:19 +00001638% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1639% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001640%
1641% A description of each parameter follows:
1642%
1643% o image: the image.
1644%
1645% o draw_info: the draw info.
1646%
cristy018f07f2011-09-04 21:15:19 +00001647% o exception: return any errors or warnings in this structure.
1648%
cristy3ed852e2009-09-05 21:47:34 +00001649*/
1650
1651static inline MagickBooleanType IsPoint(const char *point)
1652{
1653 char
1654 *p;
1655
1656 double
1657 value;
1658
cristyc1acd842011-05-19 23:05:47 +00001659 value=InterpretLocaleValue(point,&p);
cristy3ed852e2009-09-05 21:47:34 +00001660 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1661}
1662
1663static inline void TracePoint(PrimitiveInfo *primitive_info,
1664 const PointInfo point)
1665{
1666 primitive_info->coordinates=1;
1667 primitive_info->point=point;
1668}
1669
cristy018f07f2011-09-04 21:15:19 +00001670MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1671 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001672{
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
cristy101ab702011-10-13 13:06:32 +00001706 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001707 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);
cristy9950d572011-10-01 18:22:35 +00001790 (void) QueryColorCompliance("#000000",AllCompliance,&start_color,
1791 &image->exception);
cristy574cc262011-08-05 01:23:58 +00001792 if (SetImageStorageClass(image,DirectClass,&image->exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001793 return(MagickFalse);
1794 status=MagickTrue;
1795 for (q=primitive; *q != '\0'; )
1796 {
1797 /*
1798 Interpret graphic primitive.
1799 */
1800 GetMagickToken(q,&q,keyword);
1801 if (*keyword == '\0')
1802 break;
1803 if (*keyword == '#')
1804 {
1805 /*
1806 Comment.
1807 */
1808 while ((*q != '\n') && (*q != '\0'))
1809 q++;
1810 continue;
1811 }
1812 p=q-strlen(keyword)-1;
1813 primitive_type=UndefinedPrimitive;
1814 current=graphic_context[n]->affine;
1815 GetAffineMatrix(&affine);
1816 switch (*keyword)
1817 {
1818 case ';':
1819 break;
1820 case 'a':
1821 case 'A':
1822 {
1823 if (LocaleCompare("affine",keyword) == 0)
1824 {
1825 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001826 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001827 GetMagickToken(q,&q,token);
1828 if (*token == ',')
1829 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001830 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001831 GetMagickToken(q,&q,token);
1832 if (*token == ',')
1833 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001834 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001835 GetMagickToken(q,&q,token);
1836 if (*token == ',')
1837 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001838 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001839 GetMagickToken(q,&q,token);
1840 if (*token == ',')
1841 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001842 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001843 GetMagickToken(q,&q,token);
1844 if (*token == ',')
1845 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00001846 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001847 break;
1848 }
1849 if (LocaleCompare("arc",keyword) == 0)
1850 {
1851 primitive_type=ArcPrimitive;
1852 break;
1853 }
1854 status=MagickFalse;
1855 break;
1856 }
1857 case 'b':
1858 case 'B':
1859 {
1860 if (LocaleCompare("bezier",keyword) == 0)
1861 {
1862 primitive_type=BezierPrimitive;
1863 break;
1864 }
1865 if (LocaleCompare("border-color",keyword) == 0)
1866 {
1867 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00001868 (void) QueryColorCompliance(token,AllCompliance,
1869 &graphic_context[n]->border_color,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001870 break;
1871 }
1872 status=MagickFalse;
1873 break;
1874 }
1875 case 'c':
1876 case 'C':
1877 {
1878 if (LocaleCompare("clip-path",keyword) == 0)
1879 {
1880 /*
1881 Create clip mask.
1882 */
1883 GetMagickToken(q,&q,token);
1884 (void) CloneString(&graphic_context[n]->clip_mask,token);
1885 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00001886 graphic_context[n]->clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001887 break;
1888 }
1889 if (LocaleCompare("clip-rule",keyword) == 0)
1890 {
cristybb503372010-05-27 20:51:26 +00001891 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001892 fill_rule;
1893
1894 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001895 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001896 token);
1897 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00001898 status=MagickFalse;
1899 else
1900 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00001901 break;
1902 }
1903 if (LocaleCompare("clip-units",keyword) == 0)
1904 {
cristybb503372010-05-27 20:51:26 +00001905 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001906 clip_units;
1907
1908 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001909 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001910 token);
1911 if (clip_units == -1)
1912 {
1913 status=MagickFalse;
1914 break;
1915 }
1916 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1917 if (clip_units == ObjectBoundingBox)
1918 {
1919 GetAffineMatrix(&current);
1920 affine.sx=draw_info->bounds.x2;
1921 affine.sy=draw_info->bounds.y2;
1922 affine.tx=draw_info->bounds.x1;
1923 affine.ty=draw_info->bounds.y1;
1924 break;
1925 }
1926 break;
1927 }
1928 if (LocaleCompare("circle",keyword) == 0)
1929 {
1930 primitive_type=CirclePrimitive;
1931 break;
1932 }
1933 if (LocaleCompare("color",keyword) == 0)
1934 {
1935 primitive_type=ColorPrimitive;
1936 break;
1937 }
1938 status=MagickFalse;
1939 break;
1940 }
1941 case 'd':
1942 case 'D':
1943 {
1944 if (LocaleCompare("decorate",keyword) == 0)
1945 {
cristybb503372010-05-27 20:51:26 +00001946 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001947 decorate;
1948
1949 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001950 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001951 token);
1952 if (decorate == -1)
anthony2a021472011-10-08 11:29:29 +00001953 status=MagickFalse;
1954 else
1955 graphic_context[n]->decorate=(DecorationType) decorate;
cristy3ed852e2009-09-05 21:47:34 +00001956 break;
1957 }
1958 status=MagickFalse;
1959 break;
1960 }
1961 case 'e':
1962 case 'E':
1963 {
1964 if (LocaleCompare("ellipse",keyword) == 0)
1965 {
1966 primitive_type=EllipsePrimitive;
1967 break;
1968 }
1969 if (LocaleCompare("encoding",keyword) == 0)
1970 {
1971 GetMagickToken(q,&q,token);
1972 (void) CloneString(&graphic_context[n]->encoding,token);
1973 break;
1974 }
1975 status=MagickFalse;
1976 break;
1977 }
1978 case 'f':
1979 case 'F':
1980 {
1981 if (LocaleCompare("fill",keyword) == 0)
1982 {
1983 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00001984 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00001985 if (GetImageArtifact(image,pattern) != (const char *) NULL)
1986 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00001987 &graphic_context[n]->fill_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00001988 else
1989 {
cristy9950d572011-10-01 18:22:35 +00001990 status=QueryColorCompliance(token,AllCompliance,
1991 &graphic_context[n]->fill,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00001992 if (status == MagickFalse)
1993 {
1994 ImageInfo
1995 *pattern_info;
1996
1997 pattern_info=AcquireImageInfo();
1998 (void) CopyMagickString(pattern_info->filename,token,
1999 MaxTextExtent);
2000 graphic_context[n]->fill_pattern=
2001 ReadImage(pattern_info,&image->exception);
2002 CatchException(&image->exception);
2003 pattern_info=DestroyImageInfo(pattern_info);
2004 }
2005 }
2006 break;
2007 }
2008 if (LocaleCompare("fill-opacity",keyword) == 0)
2009 {
2010 GetMagickToken(q,&q,token);
2011 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002012 graphic_context[n]->fill.alpha=(MagickRealType) QuantumRange*
2013 factor*InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002014 break;
2015 }
2016 if (LocaleCompare("fill-rule",keyword) == 0)
2017 {
cristybb503372010-05-27 20:51:26 +00002018 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002019 fill_rule;
2020
2021 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002022 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002023 token);
2024 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00002025 status=MagickFalse;
2026 else
2027 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00002028 break;
2029 }
2030 if (LocaleCompare("font",keyword) == 0)
2031 {
2032 GetMagickToken(q,&q,token);
2033 (void) CloneString(&graphic_context[n]->font,token);
2034 if (LocaleCompare("none",token) == 0)
2035 graphic_context[n]->font=(char *)
2036 RelinquishMagickMemory(graphic_context[n]->font);
2037 break;
2038 }
2039 if (LocaleCompare("font-family",keyword) == 0)
2040 {
2041 GetMagickToken(q,&q,token);
2042 (void) CloneString(&graphic_context[n]->family,token);
2043 break;
2044 }
2045 if (LocaleCompare("font-size",keyword) == 0)
2046 {
2047 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002048 graphic_context[n]->pointsize=InterpretLocaleValue(token,
2049 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002050 break;
2051 }
2052 if (LocaleCompare("font-stretch",keyword) == 0)
2053 {
cristybb503372010-05-27 20:51:26 +00002054 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002055 stretch;
2056
2057 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002058 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002059 if (stretch == -1)
anthony2a021472011-10-08 11:29:29 +00002060 status=MagickFalse;
2061 else
2062 graphic_context[n]->stretch=(StretchType) stretch;
cristy3ed852e2009-09-05 21:47:34 +00002063 break;
2064 }
2065 if (LocaleCompare("font-style",keyword) == 0)
2066 {
cristybb503372010-05-27 20:51:26 +00002067 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002068 style;
2069
2070 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002071 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002072 if (style == -1)
anthony2a021472011-10-08 11:29:29 +00002073 status=MagickFalse;
2074 else
2075 graphic_context[n]->style=(StyleType) style;
cristy3ed852e2009-09-05 21:47:34 +00002076 break;
2077 }
2078 if (LocaleCompare("font-weight",keyword) == 0)
2079 {
2080 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002081 graphic_context[n]->weight=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002082 if (LocaleCompare(token,"all") == 0)
2083 graphic_context[n]->weight=0;
2084 if (LocaleCompare(token,"bold") == 0)
2085 graphic_context[n]->weight=700;
2086 if (LocaleCompare(token,"bolder") == 0)
2087 if (graphic_context[n]->weight <= 800)
2088 graphic_context[n]->weight+=100;
2089 if (LocaleCompare(token,"lighter") == 0)
2090 if (graphic_context[n]->weight >= 100)
2091 graphic_context[n]->weight-=100;
2092 if (LocaleCompare(token,"normal") == 0)
2093 graphic_context[n]->weight=400;
2094 break;
2095 }
2096 status=MagickFalse;
2097 break;
2098 }
2099 case 'g':
2100 case 'G':
2101 {
2102 if (LocaleCompare("gradient-units",keyword) == 0)
2103 {
2104 GetMagickToken(q,&q,token);
2105 break;
2106 }
2107 if (LocaleCompare("gravity",keyword) == 0)
2108 {
cristybb503372010-05-27 20:51:26 +00002109 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002110 gravity;
2111
2112 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002113 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002114 if (gravity == -1)
anthony2a021472011-10-08 11:29:29 +00002115 status=MagickFalse;
2116 else
2117 graphic_context[n]->gravity=(GravityType) gravity;
cristy3ed852e2009-09-05 21:47:34 +00002118 break;
2119 }
2120 status=MagickFalse;
2121 break;
2122 }
2123 case 'i':
2124 case 'I':
2125 {
2126 if (LocaleCompare("image",keyword) == 0)
2127 {
cristybb503372010-05-27 20:51:26 +00002128 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002129 compose;
2130
2131 primitive_type=ImagePrimitive;
2132 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002133 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002134 if (compose == -1)
anthony2a021472011-10-08 11:29:29 +00002135 status=MagickFalse;
2136 else
2137 graphic_context[n]->compose=(CompositeOperator) compose;
cristy3ed852e2009-09-05 21:47:34 +00002138 break;
2139 }
cristyb32b90a2009-09-07 21:45:48 +00002140 if (LocaleCompare("interline-spacing",keyword) == 0)
2141 {
2142 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002143 graphic_context[n]->interline_spacing=InterpretLocaleValue(token,
2144 (char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00002145 break;
2146 }
cristy3ed852e2009-09-05 21:47:34 +00002147 if (LocaleCompare("interword-spacing",keyword) == 0)
2148 {
2149 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002150 graphic_context[n]->interword_spacing=InterpretLocaleValue(token,
2151 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002152 break;
2153 }
2154 status=MagickFalse;
2155 break;
2156 }
2157 case 'k':
2158 case 'K':
2159 {
2160 if (LocaleCompare("kerning",keyword) == 0)
2161 {
2162 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002163 graphic_context[n]->kerning=InterpretLocaleValue(token,
2164 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002165 break;
2166 }
2167 status=MagickFalse;
2168 break;
2169 }
2170 case 'l':
2171 case 'L':
2172 {
2173 if (LocaleCompare("line",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002174 primitive_type=LinePrimitive;
2175 else
2176 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002177 break;
2178 }
2179 case 'm':
2180 case 'M':
2181 {
2182 if (LocaleCompare("matte",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002183 primitive_type=MattePrimitive;
2184 else
2185 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002186 break;
2187 }
2188 case 'o':
2189 case 'O':
2190 {
2191 if (LocaleCompare("offset",keyword) == 0)
2192 {
2193 GetMagickToken(q,&q,token);
2194 break;
2195 }
2196 if (LocaleCompare("opacity",keyword) == 0)
2197 {
2198 GetMagickToken(q,&q,token);
2199 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristy4c08aed2011-07-01 19:47:50 +00002200 graphic_context[n]->alpha=ClampToQuantum((MagickRealType)
2201 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->alpha)*
cristyc1acd842011-05-19 23:05:47 +00002202 factor*InterpretLocaleValue(token,(char **) NULL))));
cristyda1f9c12011-10-02 21:39:49 +00002203 graphic_context[n]->fill.alpha=(double) graphic_context[n]->alpha;
2204 graphic_context[n]->stroke.alpha=(double) graphic_context[n]->alpha;
cristy3ed852e2009-09-05 21:47:34 +00002205 break;
2206 }
2207 status=MagickFalse;
2208 break;
2209 }
2210 case 'p':
2211 case 'P':
2212 {
2213 if (LocaleCompare("path",keyword) == 0)
2214 {
2215 primitive_type=PathPrimitive;
2216 break;
2217 }
2218 if (LocaleCompare("point",keyword) == 0)
2219 {
2220 primitive_type=PointPrimitive;
2221 break;
2222 }
2223 if (LocaleCompare("polyline",keyword) == 0)
2224 {
2225 primitive_type=PolylinePrimitive;
2226 break;
2227 }
2228 if (LocaleCompare("polygon",keyword) == 0)
2229 {
2230 primitive_type=PolygonPrimitive;
2231 break;
2232 }
2233 if (LocaleCompare("pop",keyword) == 0)
2234 {
2235 GetMagickToken(q,&q,token);
2236 if (LocaleCompare("clip-path",token) == 0)
2237 break;
2238 if (LocaleCompare("defs",token) == 0)
2239 break;
2240 if (LocaleCompare("gradient",token) == 0)
2241 break;
2242 if (LocaleCompare("graphic-context",token) == 0)
2243 {
2244 if (n <= 0)
2245 {
2246 (void) ThrowMagickException(&image->exception,
2247 GetMagickModule(),DrawError,
2248 "UnbalancedGraphicContextPushPop","`%s'",token);
2249 n=0;
2250 break;
2251 }
2252 if (graphic_context[n]->clip_mask != (char *) NULL)
2253 if (LocaleCompare(graphic_context[n]->clip_mask,
2254 graphic_context[n-1]->clip_mask) != 0)
cristy018f07f2011-09-04 21:15:19 +00002255 (void) SetImageClipMask(image,(Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002256 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2257 n--;
2258 break;
2259 }
2260 if (LocaleCompare("pattern",token) == 0)
2261 break;
2262 status=MagickFalse;
2263 break;
2264 }
2265 if (LocaleCompare("push",keyword) == 0)
2266 {
2267 GetMagickToken(q,&q,token);
2268 if (LocaleCompare("clip-path",token) == 0)
2269 {
2270 char
2271 name[MaxTextExtent];
2272
2273 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002274 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002275 for (p=q; *q != '\0'; )
2276 {
2277 GetMagickToken(q,&q,token);
2278 if (LocaleCompare(token,"pop") != 0)
2279 continue;
2280 GetMagickToken(q,(const char **) NULL,token);
2281 if (LocaleCompare(token,"clip-path") != 0)
2282 continue;
2283 break;
2284 }
2285 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2286 (void) SetImageArtifact(image,name,token);
2287 GetMagickToken(q,&q,token);
2288 break;
2289 }
2290 if (LocaleCompare("gradient",token) == 0)
2291 {
2292 char
2293 key[2*MaxTextExtent],
2294 name[MaxTextExtent],
2295 type[MaxTextExtent];
2296
cristy3ed852e2009-09-05 21:47:34 +00002297 SegmentInfo
2298 segment;
2299
2300 GetMagickToken(q,&q,token);
2301 (void) CopyMagickString(name,token,MaxTextExtent);
2302 GetMagickToken(q,&q,token);
2303 (void) CopyMagickString(type,token,MaxTextExtent);
2304 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002305 segment.x1=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002306 GetMagickToken(q,&q,token);
2307 if (*token == ',')
2308 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002309 segment.y1=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002310 GetMagickToken(q,&q,token);
2311 if (*token == ',')
2312 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002313 segment.x2=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002314 GetMagickToken(q,&q,token);
2315 if (*token == ',')
2316 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002317 segment.y2=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002318 if (LocaleCompare(type,"radial") == 0)
2319 {
2320 GetMagickToken(q,&q,token);
2321 if (*token == ',')
2322 GetMagickToken(q,&q,token);
cristy3ed852e2009-09-05 21:47:34 +00002323 }
2324 for (p=q; *q != '\0'; )
2325 {
2326 GetMagickToken(q,&q,token);
2327 if (LocaleCompare(token,"pop") != 0)
2328 continue;
2329 GetMagickToken(q,(const char **) NULL,token);
2330 if (LocaleCompare(token,"gradient") != 0)
2331 continue;
2332 break;
2333 }
2334 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2335 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2336 graphic_context[n]->affine.ry*segment.y1+
2337 graphic_context[n]->affine.tx;
2338 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2339 graphic_context[n]->affine.sy*segment.y1+
2340 graphic_context[n]->affine.ty;
2341 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2342 graphic_context[n]->affine.ry*segment.y2+
2343 graphic_context[n]->affine.tx;
2344 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2345 graphic_context[n]->affine.sy*segment.y2+
2346 graphic_context[n]->affine.ty;
cristyb51dff52011-05-19 16:55:47 +00002347 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002348 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002349 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2350 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002351 "%gx%g%+.15g%+.15g",
cristy3ed852e2009-09-05 21:47:34 +00002352 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2353 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2354 bounds.x1,bounds.y1);
2355 (void) SetImageArtifact(image,key,geometry);
2356 GetMagickToken(q,&q,token);
2357 break;
2358 }
2359 if (LocaleCompare("pattern",token) == 0)
2360 {
2361 RectangleInfo
2362 bounds;
2363
2364 GetMagickToken(q,&q,token);
2365 (void) CopyMagickString(name,token,MaxTextExtent);
2366 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002367 bounds.x=(ssize_t) ceil(InterpretLocaleValue(token,
2368 (char **) NULL)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002369 GetMagickToken(q,&q,token);
2370 if (*token == ',')
2371 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002372 bounds.y=(ssize_t) ceil(InterpretLocaleValue(token,
2373 (char **) NULL)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002374 GetMagickToken(q,&q,token);
2375 if (*token == ',')
2376 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002377 bounds.width=(size_t) floor(InterpretLocaleValue(token,
2378 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002379 GetMagickToken(q,&q,token);
2380 if (*token == ',')
2381 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002382 bounds.height=(size_t) floor(InterpretLocaleValue(token,
2383 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002384 for (p=q; *q != '\0'; )
2385 {
2386 GetMagickToken(q,&q,token);
2387 if (LocaleCompare(token,"pop") != 0)
2388 continue;
2389 GetMagickToken(q,(const char **) NULL,token);
2390 if (LocaleCompare(token,"pattern") != 0)
2391 continue;
2392 break;
2393 }
2394 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
cristyb51dff52011-05-19 16:55:47 +00002395 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002396 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002397 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2398 (void) FormatLocaleString(geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002399 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002400 bounds.height,(double) bounds.x,(double) bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00002401 (void) SetImageArtifact(image,key,geometry);
2402 GetMagickToken(q,&q,token);
2403 break;
2404 }
2405 if (LocaleCompare("graphic-context",token) == 0)
2406 {
2407 n++;
2408 graphic_context=(DrawInfo **) ResizeQuantumMemory(
2409 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2410 if (graphic_context == (DrawInfo **) NULL)
2411 {
2412 (void) ThrowMagickException(&image->exception,
2413 GetMagickModule(),ResourceLimitError,
2414 "MemoryAllocationFailed","`%s'",image->filename);
2415 break;
2416 }
2417 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2418 graphic_context[n-1]);
2419 break;
2420 }
2421 if (LocaleCompare("defs",token) == 0)
2422 break;
2423 status=MagickFalse;
2424 break;
2425 }
2426 status=MagickFalse;
2427 break;
2428 }
2429 case 'r':
2430 case 'R':
2431 {
2432 if (LocaleCompare("rectangle",keyword) == 0)
2433 {
2434 primitive_type=RectanglePrimitive;
2435 break;
2436 }
2437 if (LocaleCompare("rotate",keyword) == 0)
2438 {
2439 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002440 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002441 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2442 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2443 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2444 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2445 break;
2446 }
2447 if (LocaleCompare("roundRectangle",keyword) == 0)
2448 {
2449 primitive_type=RoundRectanglePrimitive;
2450 break;
2451 }
2452 status=MagickFalse;
2453 break;
2454 }
2455 case 's':
2456 case 'S':
2457 {
2458 if (LocaleCompare("scale",keyword) == 0)
2459 {
2460 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002461 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002462 GetMagickToken(q,&q,token);
2463 if (*token == ',')
2464 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002465 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002466 break;
2467 }
2468 if (LocaleCompare("skewX",keyword) == 0)
2469 {
2470 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002471 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002472 affine.ry=sin(DegreesToRadians(angle));
2473 break;
2474 }
2475 if (LocaleCompare("skewY",keyword) == 0)
2476 {
2477 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002478 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002479 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2480 break;
2481 }
2482 if (LocaleCompare("stop-color",keyword) == 0)
2483 {
cristy101ab702011-10-13 13:06:32 +00002484 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002485 stop_color;
2486
2487 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002488 (void) QueryColorCompliance(token,AllCompliance,&stop_color,
2489 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00002490 (void) GradientImage(image,LinearGradient,ReflectSpread,
cristy189e84c2011-08-27 18:08:53 +00002491 &start_color,&stop_color,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00002492 start_color=stop_color;
2493 GetMagickToken(q,&q,token);
2494 break;
2495 }
2496 if (LocaleCompare("stroke",keyword) == 0)
2497 {
2498 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002499 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002500 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2501 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00002502 &graphic_context[n]->stroke_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002503 else
2504 {
cristy9950d572011-10-01 18:22:35 +00002505 status=QueryColorCompliance(token,AllCompliance,
2506 &graphic_context[n]->stroke,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00002507 if (status == MagickFalse)
2508 {
2509 ImageInfo
2510 *pattern_info;
2511
2512 pattern_info=AcquireImageInfo();
2513 (void) CopyMagickString(pattern_info->filename,token,
2514 MaxTextExtent);
2515 graphic_context[n]->stroke_pattern=
2516 ReadImage(pattern_info,&image->exception);
2517 CatchException(&image->exception);
2518 pattern_info=DestroyImageInfo(pattern_info);
2519 }
2520 }
2521 break;
2522 }
2523 if (LocaleCompare("stroke-antialias",keyword) == 0)
2524 {
2525 GetMagickToken(q,&q,token);
2526 graphic_context[n]->stroke_antialias=
cristyf2f27272009-12-17 14:48:46 +00002527 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002528 break;
2529 }
2530 if (LocaleCompare("stroke-dasharray",keyword) == 0)
2531 {
2532 if (graphic_context[n]->dash_pattern != (double *) NULL)
2533 graphic_context[n]->dash_pattern=(double *)
2534 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2535 if (IsPoint(q) != MagickFalse)
2536 {
2537 const char
2538 *p;
2539
2540 p=q;
2541 GetMagickToken(p,&p,token);
2542 if (*token == ',')
2543 GetMagickToken(p,&p,token);
2544 for (x=0; IsPoint(token) != MagickFalse; x++)
2545 {
2546 GetMagickToken(p,&p,token);
2547 if (*token == ',')
2548 GetMagickToken(p,&p,token);
2549 }
2550 graphic_context[n]->dash_pattern=(double *)
2551 AcquireQuantumMemory((size_t) (2UL*x+1UL),
2552 sizeof(*graphic_context[n]->dash_pattern));
2553 if (graphic_context[n]->dash_pattern == (double *) NULL)
2554 {
2555 (void) ThrowMagickException(&image->exception,
2556 GetMagickModule(),ResourceLimitError,
2557 "MemoryAllocationFailed","`%s'",image->filename);
2558 break;
2559 }
2560 for (j=0; j < x; j++)
2561 {
2562 GetMagickToken(q,&q,token);
2563 if (*token == ',')
2564 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002565 graphic_context[n]->dash_pattern[j]=InterpretLocaleValue(
2566 token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002567 }
2568 if ((x & 0x01) != 0)
2569 for ( ; j < (2*x); j++)
2570 graphic_context[n]->dash_pattern[j]=
2571 graphic_context[n]->dash_pattern[j-x];
2572 graphic_context[n]->dash_pattern[j]=0.0;
2573 break;
2574 }
2575 GetMagickToken(q,&q,token);
2576 break;
2577 }
2578 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2579 {
2580 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002581 graphic_context[n]->dash_offset=InterpretLocaleValue(token,
2582 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002583 break;
2584 }
2585 if (LocaleCompare("stroke-linecap",keyword) == 0)
2586 {
cristybb503372010-05-27 20:51:26 +00002587 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002588 linecap;
2589
2590 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002591 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002592 if (linecap == -1)
anthony2a021472011-10-08 11:29:29 +00002593 status=MagickFalse;
2594 else
2595 graphic_context[n]->linecap=(LineCap) linecap;
cristy3ed852e2009-09-05 21:47:34 +00002596 break;
2597 }
2598 if (LocaleCompare("stroke-linejoin",keyword) == 0)
2599 {
cristybb503372010-05-27 20:51:26 +00002600 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002601 linejoin;
2602
2603 GetMagickToken(q,&q,token);
cristy7118edf2011-10-08 13:33:25 +00002604 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
2605 token);
cristy3ed852e2009-09-05 21:47:34 +00002606 if (linejoin == -1)
anthony2a021472011-10-08 11:29:29 +00002607 status=MagickFalse;
2608 else
2609 graphic_context[n]->linejoin=(LineJoin) linejoin;
cristy3ed852e2009-09-05 21:47:34 +00002610 break;
2611 }
2612 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2613 {
2614 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002615 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002616 break;
2617 }
2618 if (LocaleCompare("stroke-opacity",keyword) == 0)
2619 {
2620 GetMagickToken(q,&q,token);
2621 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002622 graphic_context[n]->stroke.alpha=(MagickRealType) QuantumRange*
2623 factor*InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002624 break;
2625 }
2626 if (LocaleCompare("stroke-width",keyword) == 0)
2627 {
2628 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002629 graphic_context[n]->stroke_width=InterpretLocaleValue(token,
2630 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002631 break;
2632 }
2633 status=MagickFalse;
2634 break;
2635 }
2636 case 't':
2637 case 'T':
2638 {
2639 if (LocaleCompare("text",keyword) == 0)
2640 {
2641 primitive_type=TextPrimitive;
2642 break;
2643 }
2644 if (LocaleCompare("text-align",keyword) == 0)
2645 {
cristybb503372010-05-27 20:51:26 +00002646 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002647 align;
2648
2649 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002650 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002651 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002652 status=MagickFalse;
2653 else
2654 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002655 break;
2656 }
2657 if (LocaleCompare("text-anchor",keyword) == 0)
2658 {
cristybb503372010-05-27 20:51:26 +00002659 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002660 align;
2661
2662 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002663 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002664 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002665 status=MagickFalse;
2666 else
2667 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002668 break;
2669 }
2670 if (LocaleCompare("text-antialias",keyword) == 0)
2671 {
2672 GetMagickToken(q,&q,token);
2673 graphic_context[n]->text_antialias=
cristyf2f27272009-12-17 14:48:46 +00002674 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002675 break;
2676 }
2677 if (LocaleCompare("text-undercolor",keyword) == 0)
2678 {
2679 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002680 (void) QueryColorCompliance(token,AllCompliance,
2681 &graphic_context[n]->undercolor,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00002682 break;
2683 }
2684 if (LocaleCompare("translate",keyword) == 0)
2685 {
2686 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002687 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002688 GetMagickToken(q,&q,token);
2689 if (*token == ',')
2690 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002691 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002692 break;
2693 }
2694 status=MagickFalse;
2695 break;
2696 }
2697 case 'v':
2698 case 'V':
2699 {
2700 if (LocaleCompare("viewbox",keyword) == 0)
2701 {
2702 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002703 graphic_context[n]->viewbox.x=(ssize_t) ceil(InterpretLocaleValue(
2704 token,(char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002705 GetMagickToken(q,&q,token);
2706 if (*token == ',')
2707 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002708 graphic_context[n]->viewbox.y=(ssize_t) ceil(InterpretLocaleValue(
2709 token,(char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002710 GetMagickToken(q,&q,token);
2711 if (*token == ',')
2712 GetMagickToken(q,&q,token);
cristybb503372010-05-27 20:51:26 +00002713 graphic_context[n]->viewbox.width=(size_t) floor(
cristyc1acd842011-05-19 23:05:47 +00002714 InterpretLocaleValue(token,(char **) NULL)+0.5);
cristy06609ee2010-03-17 20:21:27 +00002715 GetMagickToken(q,&q,token);
2716 if (*token == ',')
2717 GetMagickToken(q,&q,token);
cristybb503372010-05-27 20:51:26 +00002718 graphic_context[n]->viewbox.height=(size_t) floor(
cristyc1acd842011-05-19 23:05:47 +00002719 InterpretLocaleValue(token,(char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002720 break;
2721 }
2722 status=MagickFalse;
2723 break;
2724 }
2725 default:
2726 {
2727 status=MagickFalse;
2728 break;
2729 }
2730 }
2731 if (status == MagickFalse)
2732 break;
2733 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2734 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2735 {
cristyfbd70092010-12-01 19:31:14 +00002736 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2737 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2738 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2739 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2740 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2741 current.tx;
2742 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2743 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002744 }
2745 if (primitive_type == UndefinedPrimitive)
2746 {
2747 if (image->debug != MagickFalse)
2748 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2749 (int) (q-p),p);
2750 continue;
2751 }
2752 /*
2753 Parse the primitive attributes.
2754 */
2755 i=0;
2756 j=0;
2757 primitive_info[0].point.x=0.0;
2758 primitive_info[0].point.y=0.0;
2759 for (x=0; *q != '\0'; x++)
2760 {
2761 /*
2762 Define points.
2763 */
2764 if (IsPoint(q) == MagickFalse)
2765 break;
2766 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002767 point.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002768 GetMagickToken(q,&q,token);
2769 if (*token == ',')
2770 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00002771 point.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002772 GetMagickToken(q,(const char **) NULL,token);
2773 if (*token == ',')
2774 GetMagickToken(q,&q,token);
2775 primitive_info[i].primitive=primitive_type;
2776 primitive_info[i].point=point;
2777 primitive_info[i].coordinates=0;
2778 primitive_info[i].method=FloodfillMethod;
2779 i++;
cristybb503372010-05-27 20:51:26 +00002780 if (i < (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +00002781 continue;
2782 number_points<<=1;
2783 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2784 (size_t) number_points,sizeof(*primitive_info));
2785 if (primitive_info == (PrimitiveInfo *) NULL)
2786 {
2787 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2788 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2789 break;
2790 }
2791 }
2792 primitive_info[j].primitive=primitive_type;
cristybb503372010-05-27 20:51:26 +00002793 primitive_info[j].coordinates=(size_t) x;
cristy3ed852e2009-09-05 21:47:34 +00002794 primitive_info[j].method=FloodfillMethod;
2795 primitive_info[j].text=(char *) NULL;
2796 /*
2797 Circumscribe primitive within a circle.
2798 */
2799 bounds.x1=primitive_info[j].point.x;
2800 bounds.y1=primitive_info[j].point.y;
2801 bounds.x2=primitive_info[j].point.x;
2802 bounds.y2=primitive_info[j].point.y;
cristybb503372010-05-27 20:51:26 +00002803 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
cristy3ed852e2009-09-05 21:47:34 +00002804 {
2805 point=primitive_info[j+k].point;
2806 if (point.x < bounds.x1)
2807 bounds.x1=point.x;
2808 if (point.y < bounds.y1)
2809 bounds.y1=point.y;
2810 if (point.x > bounds.x2)
2811 bounds.x2=point.x;
2812 if (point.y > bounds.y2)
2813 bounds.y2=point.y;
2814 }
2815 /*
2816 Speculate how many points our primitive might consume.
2817 */
2818 length=primitive_info[j].coordinates;
2819 switch (primitive_type)
2820 {
2821 case RectanglePrimitive:
2822 {
2823 length*=5;
2824 break;
2825 }
2826 case RoundRectanglePrimitive:
2827 {
cristy78817ad2010-05-07 12:25:34 +00002828 length*=5+8*BezierQuantum;
cristy3ed852e2009-09-05 21:47:34 +00002829 break;
2830 }
2831 case BezierPrimitive:
2832 {
2833 if (primitive_info[j].coordinates > 107)
2834 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2835 DrawError,"TooManyBezierCoordinates","`%s'",token);
2836 length=BezierQuantum*primitive_info[j].coordinates;
2837 break;
2838 }
2839 case PathPrimitive:
2840 {
2841 char
2842 *s,
2843 *t;
2844
2845 GetMagickToken(q,&q,token);
cristy40a08ad2010-02-09 02:27:44 +00002846 length=1;
cristy3ed852e2009-09-05 21:47:34 +00002847 t=token;
2848 for (s=token; *s != '\0'; s=t)
2849 {
cristy00976d82011-02-20 20:31:28 +00002850 double
2851 value;
2852
cristyc1acd842011-05-19 23:05:47 +00002853 value=InterpretLocaleValue(s,&t);
cristy00976d82011-02-20 20:31:28 +00002854 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00002855 if (s == t)
2856 {
2857 t++;
2858 continue;
2859 }
cristyef7a6ce2011-05-14 15:01:11 +00002860 length++;
cristy3ed852e2009-09-05 21:47:34 +00002861 }
cristyd2f832c2011-06-28 12:35:24 +00002862 length=length*BezierQuantum/2;
cristy3ed852e2009-09-05 21:47:34 +00002863 break;
2864 }
2865 case CirclePrimitive:
2866 case ArcPrimitive:
2867 case EllipsePrimitive:
2868 {
2869 MagickRealType
2870 alpha,
2871 beta,
2872 radius;
2873
2874 alpha=bounds.x2-bounds.x1;
2875 beta=bounds.y2-bounds.y1;
2876 radius=hypot((double) alpha,(double) beta);
cristyf53f26f2011-05-16 00:56:05 +00002877 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002878 break;
2879 }
2880 default:
2881 break;
2882 }
cristybb503372010-05-27 20:51:26 +00002883 if ((size_t) (i+length) >= number_points)
cristy3ed852e2009-09-05 21:47:34 +00002884 {
2885 /*
2886 Resize based on speculative points required by primitive.
2887 */
cristy9ce61b92010-05-12 16:30:26 +00002888 number_points+=length+1;
cristy3ed852e2009-09-05 21:47:34 +00002889 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2890 (size_t) number_points,sizeof(*primitive_info));
2891 if (primitive_info == (PrimitiveInfo *) NULL)
2892 {
2893 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2894 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2895 image->filename);
2896 break;
2897 }
2898 }
2899 switch (primitive_type)
2900 {
2901 case PointPrimitive:
2902 default:
2903 {
2904 if (primitive_info[j].coordinates != 1)
2905 {
2906 status=MagickFalse;
2907 break;
2908 }
2909 TracePoint(primitive_info+j,primitive_info[j].point);
cristybb503372010-05-27 20:51:26 +00002910 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002911 break;
2912 }
2913 case LinePrimitive:
2914 {
2915 if (primitive_info[j].coordinates != 2)
2916 {
2917 status=MagickFalse;
2918 break;
2919 }
2920 TraceLine(primitive_info+j,primitive_info[j].point,
2921 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002922 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002923 break;
2924 }
2925 case RectanglePrimitive:
2926 {
2927 if (primitive_info[j].coordinates != 2)
2928 {
2929 status=MagickFalse;
2930 break;
2931 }
2932 TraceRectangle(primitive_info+j,primitive_info[j].point,
2933 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002934 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002935 break;
2936 }
2937 case RoundRectanglePrimitive:
2938 {
2939 if (primitive_info[j].coordinates != 3)
2940 {
2941 status=MagickFalse;
2942 break;
2943 }
2944 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
2945 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002946 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002947 break;
2948 }
2949 case ArcPrimitive:
2950 {
2951 if (primitive_info[j].coordinates != 3)
2952 {
2953 primitive_type=UndefinedPrimitive;
2954 break;
2955 }
2956 TraceArc(primitive_info+j,primitive_info[j].point,
2957 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002958 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002959 break;
2960 }
2961 case EllipsePrimitive:
2962 {
2963 if (primitive_info[j].coordinates != 3)
2964 {
2965 status=MagickFalse;
2966 break;
2967 }
2968 TraceEllipse(primitive_info+j,primitive_info[j].point,
2969 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002970 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002971 break;
2972 }
2973 case CirclePrimitive:
2974 {
2975 if (primitive_info[j].coordinates != 2)
2976 {
2977 status=MagickFalse;
2978 break;
2979 }
2980 TraceCircle(primitive_info+j,primitive_info[j].point,
2981 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002982 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002983 break;
2984 }
2985 case PolylinePrimitive:
2986 break;
2987 case PolygonPrimitive:
2988 {
2989 primitive_info[i]=primitive_info[j];
2990 primitive_info[i].coordinates=0;
2991 primitive_info[j].coordinates++;
2992 i++;
2993 break;
2994 }
2995 case BezierPrimitive:
2996 {
2997 if (primitive_info[j].coordinates < 3)
2998 {
2999 status=MagickFalse;
3000 break;
3001 }
3002 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
cristybb503372010-05-27 20:51:26 +00003003 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00003004 break;
3005 }
3006 case PathPrimitive:
3007 {
cristybb503372010-05-27 20:51:26 +00003008 i=(ssize_t) (j+TracePath(primitive_info+j,token));
cristy3ed852e2009-09-05 21:47:34 +00003009 break;
3010 }
3011 case ColorPrimitive:
3012 case MattePrimitive:
3013 {
cristybb503372010-05-27 20:51:26 +00003014 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003015 method;
3016
3017 if (primitive_info[j].coordinates != 1)
3018 {
3019 status=MagickFalse;
3020 break;
3021 }
3022 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00003023 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00003024 if (method == -1)
anthony2a021472011-10-08 11:29:29 +00003025 status=MagickFalse;
3026 else
3027 primitive_info[j].method=(PaintMethod) method;
cristy3ed852e2009-09-05 21:47:34 +00003028 break;
3029 }
3030 case TextPrimitive:
3031 {
3032 if (primitive_info[j].coordinates != 1)
3033 {
3034 status=MagickFalse;
3035 break;
3036 }
3037 if (*token != ',')
3038 GetMagickToken(q,&q,token);
3039 primitive_info[j].text=AcquireString(token);
3040 break;
3041 }
3042 case ImagePrimitive:
3043 {
3044 if (primitive_info[j].coordinates != 2)
3045 {
3046 status=MagickFalse;
3047 break;
3048 }
3049 GetMagickToken(q,&q,token);
3050 primitive_info[j].text=AcquireString(token);
3051 break;
3052 }
3053 }
3054 if (primitive_info == (PrimitiveInfo *) NULL)
3055 break;
3056 if (image->debug != MagickFalse)
3057 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3058 if (status == MagickFalse)
3059 break;
3060 primitive_info[i].primitive=UndefinedPrimitive;
3061 if (i == 0)
3062 continue;
3063 /*
3064 Transform points.
3065 */
3066 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3067 {
3068 point=primitive_info[i].point;
3069 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3070 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3071 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3072 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3073 point=primitive_info[i].point;
3074 if (point.x < graphic_context[n]->bounds.x1)
3075 graphic_context[n]->bounds.x1=point.x;
3076 if (point.y < graphic_context[n]->bounds.y1)
3077 graphic_context[n]->bounds.y1=point.y;
3078 if (point.x > graphic_context[n]->bounds.x2)
3079 graphic_context[n]->bounds.x2=point.x;
3080 if (point.y > graphic_context[n]->bounds.y2)
3081 graphic_context[n]->bounds.y2=point.y;
3082 if (primitive_info[i].primitive == ImagePrimitive)
3083 break;
cristybb503372010-05-27 20:51:26 +00003084 if (i >= (ssize_t) number_points)
cristy9ce61b92010-05-12 16:30:26 +00003085 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00003086 }
cristy3ed852e2009-09-05 21:47:34 +00003087 if (graphic_context[n]->render != MagickFalse)
3088 {
3089 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3090 (LocaleCompare(graphic_context[n]->clip_mask,
3091 graphic_context[n-1]->clip_mask) != 0))
3092 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00003093 graphic_context[n]->clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00003094 (void) DrawPrimitive(image,graphic_context[n],primitive_info);
3095 }
3096 if (primitive_info->text != (char *) NULL)
3097 primitive_info->text=(char *) RelinquishMagickMemory(
3098 primitive_info->text);
3099 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3100 primitive_extent);
3101 if (proceed == MagickFalse)
3102 break;
3103 }
3104 if (image->debug != MagickFalse)
3105 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3106 /*
3107 Relinquish resources.
3108 */
3109 token=DestroyString(token);
3110 if (primitive_info != (PrimitiveInfo *) NULL)
3111 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3112 primitive=DestroyString(primitive);
3113 for ( ; n >= 0; n--)
3114 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3115 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3116 if (status == MagickFalse)
3117 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3118 keyword);
3119 return(status);
3120}
3121
3122/*
3123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3124% %
3125% %
3126% %
3127% D r a w G r a d i e n t I m a g e %
3128% %
3129% %
3130% %
3131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3132%
3133% DrawGradientImage() draws a linear gradient on the image.
3134%
3135% The format of the DrawGradientImage method is:
3136%
3137% MagickBooleanType DrawGradientImage(Image *image,
3138% const DrawInfo *draw_info)
3139%
3140% A description of each parameter follows:
3141%
3142% o image: the image.
3143%
anthony2a021472011-10-08 11:29:29 +00003144% o draw_info: the draw info.
cristy3ed852e2009-09-05 21:47:34 +00003145%
3146*/
3147
3148static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
cristybb503372010-05-27 20:51:26 +00003149 const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00003150{
3151 switch (gradient->type)
3152 {
3153 case UndefinedGradient:
3154 case LinearGradient:
3155 {
3156 MagickRealType
3157 gamma,
3158 length,
3159 offset,
3160 scale;
3161
3162 PointInfo
3163 p,
3164 q;
3165
3166 const SegmentInfo
3167 *gradient_vector;
3168
3169 gradient_vector=(&gradient->gradient_vector);
3170 p.x=gradient_vector->x2-gradient_vector->x1;
3171 p.y=gradient_vector->y2-gradient_vector->y1;
3172 q.x=(double) x-gradient_vector->x1;
3173 q.y=(double) y-gradient_vector->y1;
3174 length=sqrt(q.x*q.x+q.y*q.y);
3175 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
3176 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
3177 scale=p.x*q.x+p.y*q.y;
3178 offset=gamma*scale*length;
3179 return(offset);
3180 }
3181 case RadialGradient:
3182 {
3183 MagickRealType
3184 length,
3185 offset;
3186
3187 PointInfo
3188 v;
3189
3190 v.x=(double) x-gradient->center.x;
3191 v.y=(double) y-gradient->center.y;
3192 length=sqrt(v.x*v.x+v.y*v.y);
3193 if (gradient->spread == RepeatSpread)
3194 return(length);
3195 offset=length/gradient->radius;
3196 return(offset);
3197 }
3198 }
3199 return(0.0);
3200}
3201
3202MagickExport MagickBooleanType DrawGradientImage(Image *image,
3203 const DrawInfo *draw_info)
3204{
cristyc4c8d132010-01-07 01:58:38 +00003205 CacheView
3206 *image_view;
3207
cristy3ed852e2009-09-05 21:47:34 +00003208 const GradientInfo
3209 *gradient;
3210
3211 const SegmentInfo
3212 *gradient_vector;
3213
3214 ExceptionInfo
3215 *exception;
3216
cristy3ed852e2009-09-05 21:47:34 +00003217 MagickBooleanType
3218 status;
3219
cristy4c08aed2011-07-01 19:47:50 +00003220 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003221 zero;
3222
3223 MagickRealType
3224 length;
3225
3226 PointInfo
3227 point;
3228
3229 RectangleInfo
3230 bounding_box;
3231
cristy826a5472010-08-31 23:21:38 +00003232 ssize_t
3233 y;
3234
cristy3ed852e2009-09-05 21:47:34 +00003235 /*
3236 Draw linear or radial gradient on image.
3237 */
3238 assert(image != (Image *) NULL);
3239 assert(image->signature == MagickSignature);
3240 if (image->debug != MagickFalse)
3241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3242 assert(draw_info != (const DrawInfo *) NULL);
3243 gradient=(&draw_info->gradient);
3244 gradient_vector=(&gradient->gradient_vector);
3245 point.x=gradient_vector->x2-gradient_vector->x1;
3246 point.y=gradient_vector->y2-gradient_vector->y1;
3247 length=sqrt(point.x*point.x+point.y*point.y);
3248 bounding_box=gradient->bounding_box;
3249 status=MagickTrue;
3250 exception=(&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00003251 GetPixelInfo(image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00003252 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003253#if defined(MAGICKCORE_OPENMP_SUPPORT)
3254 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003255#endif
cristybb503372010-05-27 20:51:26 +00003256 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00003257 {
cristy4c08aed2011-07-01 19:47:50 +00003258 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003259 composite,
3260 pixel;
3261
3262 MagickRealType
3263 alpha,
3264 offset;
3265
cristy4c08aed2011-07-01 19:47:50 +00003266 register Quantum
3267 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003268
cristybb503372010-05-27 20:51:26 +00003269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003270 i,
3271 x;
3272
cristy826a5472010-08-31 23:21:38 +00003273 ssize_t
3274 j;
3275
cristy3ed852e2009-09-05 21:47:34 +00003276 if (status == MagickFalse)
3277 continue;
3278 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003279 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003280 {
3281 status=MagickFalse;
3282 continue;
3283 }
cristy3ed852e2009-09-05 21:47:34 +00003284 pixel=zero;
3285 composite=zero;
3286 offset=GetStopColorOffset(gradient,0,y);
3287 if (gradient->type != RadialGradient)
3288 offset/=length;
cristybb503372010-05-27 20:51:26 +00003289 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
cristy3ed852e2009-09-05 21:47:34 +00003290 {
cristy4c08aed2011-07-01 19:47:50 +00003291 SetPixelInfo(image,q,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003292 switch (gradient->spread)
3293 {
3294 case UndefinedSpread:
3295 case PadSpread:
3296 {
cristybb503372010-05-27 20:51:26 +00003297 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3298 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003299 {
3300 offset=GetStopColorOffset(gradient,x,y);
3301 if (gradient->type != RadialGradient)
3302 offset/=length;
3303 }
cristybb503372010-05-27 20:51:26 +00003304 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003305 if (offset < gradient->stops[i].offset)
3306 break;
3307 if ((offset < 0.0) || (i == 0))
3308 composite=gradient->stops[0].color;
3309 else
cristybb503372010-05-27 20:51:26 +00003310 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
cristy3ed852e2009-09-05 21:47:34 +00003311 composite=gradient->stops[gradient->number_stops-1].color;
3312 else
3313 {
3314 j=i;
3315 i--;
3316 alpha=(offset-gradient->stops[i].offset)/
3317 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003318 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003319 &gradient->stops[j].color,alpha,&composite);
3320 }
3321 break;
3322 }
3323 case ReflectSpread:
3324 {
cristybb503372010-05-27 20:51:26 +00003325 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3326 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003327 {
3328 offset=GetStopColorOffset(gradient,x,y);
3329 if (gradient->type != RadialGradient)
3330 offset/=length;
3331 }
3332 if (offset < 0.0)
3333 offset=(-offset);
cristybb503372010-05-27 20:51:26 +00003334 if ((ssize_t) fmod(offset,2.0) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003335 offset=fmod(offset,1.0);
3336 else
3337 offset=1.0-fmod(offset,1.0);
cristybb503372010-05-27 20:51:26 +00003338 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003339 if (offset < gradient->stops[i].offset)
3340 break;
3341 if (i == 0)
3342 composite=gradient->stops[0].color;
3343 else
cristybb503372010-05-27 20:51:26 +00003344 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003345 composite=gradient->stops[gradient->number_stops-1].color;
3346 else
3347 {
3348 j=i;
3349 i--;
3350 alpha=(offset-gradient->stops[i].offset)/
3351 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003352 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003353 &gradient->stops[j].color,alpha,&composite);
3354 }
3355 break;
3356 }
3357 case RepeatSpread:
3358 {
3359 MagickBooleanType
3360 antialias;
3361
3362 MagickRealType
3363 repeat;
3364
3365 antialias=MagickFalse;
3366 repeat=0.0;
cristybb503372010-05-27 20:51:26 +00003367 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3368 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003369 {
3370 offset=GetStopColorOffset(gradient,x,y);
3371 if (gradient->type == LinearGradient)
3372 {
3373 repeat=fmod(offset,length);
3374 if (repeat < 0.0)
3375 repeat=length-fmod(-repeat,length);
3376 else
3377 repeat=fmod(offset,length);
3378 antialias=(repeat < length) && ((repeat+1.0) > length) ?
3379 MagickTrue : MagickFalse;
3380 offset=repeat/length;
3381 }
3382 else
3383 {
3384 repeat=fmod(offset,gradient->radius);
3385 if (repeat < 0.0)
3386 repeat=gradient->radius-fmod(-repeat,gradient->radius);
3387 else
3388 repeat=fmod(offset,gradient->radius);
cristy4c08aed2011-07-01 19:47:50 +00003389 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
3390 MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003391 offset=repeat/gradient->radius;
3392 }
3393 }
cristybb503372010-05-27 20:51:26 +00003394 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003395 if (offset < gradient->stops[i].offset)
3396 break;
3397 if (i == 0)
3398 composite=gradient->stops[0].color;
3399 else
cristybb503372010-05-27 20:51:26 +00003400 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003401 composite=gradient->stops[gradient->number_stops-1].color;
3402 else
3403 {
3404 j=i;
3405 i--;
3406 alpha=(offset-gradient->stops[i].offset)/
3407 (gradient->stops[j].offset-gradient->stops[i].offset);
3408 if (antialias != MagickFalse)
3409 {
3410 if (gradient->type == LinearGradient)
3411 alpha=length-repeat;
3412 else
3413 alpha=gradient->radius-repeat;
3414 i=0;
cristybb503372010-05-27 20:51:26 +00003415 j=(ssize_t) gradient->number_stops-1L;
cristy3ed852e2009-09-05 21:47:34 +00003416 }
cristy4c08aed2011-07-01 19:47:50 +00003417 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003418 &gradient->stops[j].color,alpha,&composite);
3419 }
3420 break;
3421 }
3422 }
cristy4c08aed2011-07-01 19:47:50 +00003423 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
3424 &pixel);
3425 SetPixelPixelInfo(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003426 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003427 }
3428 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3429 status=MagickFalse;
3430 }
3431 image_view=DestroyCacheView(image_view);
3432 return(status);
3433}
3434
3435/*
3436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3437% %
3438% %
3439% %
3440% D r a w P a t t e r n P a t h %
3441% %
3442% %
3443% %
3444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3445%
3446% DrawPatternPath() draws a pattern.
3447%
3448% The format of the DrawPatternPath method is:
3449%
3450% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00003451% const char *name,Image **pattern,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003452%
3453% A description of each parameter follows:
3454%
3455% o image: the image.
3456%
3457% o draw_info: the draw info.
3458%
3459% o name: the pattern name.
3460%
3461% o image: the image.
3462%
cristy018f07f2011-09-04 21:15:19 +00003463% o exception: return any errors or warnings in this structure.
3464%
cristy3ed852e2009-09-05 21:47:34 +00003465*/
3466MagickExport MagickBooleanType DrawPatternPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00003467 const DrawInfo *draw_info,const char *name,Image **pattern,
3468 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003469{
3470 char
3471 property[MaxTextExtent];
3472
3473 const char
3474 *geometry,
3475 *path;
3476
3477 DrawInfo
3478 *clone_info;
3479
3480 ImageInfo
3481 *image_info;
3482
3483 MagickBooleanType
3484 status;
3485
3486 assert(image != (Image *) NULL);
3487 assert(image->signature == MagickSignature);
3488 if (image->debug != MagickFalse)
3489 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3490 assert(draw_info != (const DrawInfo *) NULL);
3491 assert(name != (const char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00003492 (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00003493 path=GetImageArtifact(image,property);
3494 if (path == (const char *) NULL)
3495 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00003496 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
cristy3ed852e2009-09-05 21:47:34 +00003497 geometry=GetImageArtifact(image,property);
3498 if (geometry == (const char *) NULL)
3499 return(MagickFalse);
3500 if ((*pattern) != (Image *) NULL)
3501 *pattern=DestroyImage(*pattern);
3502 image_info=AcquireImageInfo();
3503 image_info->size=AcquireString(geometry);
cristy9950d572011-10-01 18:22:35 +00003504 *pattern=AcquireImage(image_info,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00003505 image_info=DestroyImageInfo(image_info);
cristy9950d572011-10-01 18:22:35 +00003506 (void) QueryColorCompliance("#00000000",AllCompliance,
3507 &(*pattern)->background_color,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00003508 (void) SetImageBackgroundColor(*pattern);
3509 if (image->debug != MagickFalse)
3510 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3511 "begin pattern-path %s %s",name,geometry);
3512 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3513 clone_info->fill_pattern=NewImageList();
3514 clone_info->stroke_pattern=NewImageList();
3515 (void) CloneString(&clone_info->primitive,path);
cristy018f07f2011-09-04 21:15:19 +00003516 status=DrawImage(*pattern,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003517 clone_info=DestroyDrawInfo(clone_info);
3518 if (image->debug != MagickFalse)
3519 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3520 return(status);
3521}
3522
3523/*
3524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3525% %
3526% %
3527% %
3528+ D r a w P o l y g o n P r i m i t i v e %
3529% %
3530% %
3531% %
3532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3533%
3534% DrawPolygonPrimitive() draws a polygon on the image.
3535%
3536% The format of the DrawPolygonPrimitive method is:
3537%
3538% MagickBooleanType DrawPolygonPrimitive(Image *image,
3539% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3540%
3541% A description of each parameter follows:
3542%
3543% o image: the image.
3544%
3545% o draw_info: the draw info.
3546%
3547% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3548%
3549*/
3550
3551static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
3552{
cristybb503372010-05-27 20:51:26 +00003553 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003554 i;
3555
3556 assert(polygon_info != (PolygonInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00003557 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00003558 if (polygon_info[i] != (PolygonInfo *) NULL)
3559 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
cristyb41ee102010-10-04 16:46:15 +00003560 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
cristy3ed852e2009-09-05 21:47:34 +00003561 return(polygon_info);
3562}
3563
3564static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
3565 const PrimitiveInfo *primitive_info)
3566{
3567 PathInfo
cristyfa112112010-01-04 17:48:07 +00003568 *restrict path_info;
cristy3ed852e2009-09-05 21:47:34 +00003569
cristy3ed852e2009-09-05 21:47:34 +00003570 PolygonInfo
3571 **polygon_info;
3572
cristy826a5472010-08-31 23:21:38 +00003573 register ssize_t
3574 i;
3575
cristybb503372010-05-27 20:51:26 +00003576 size_t
cristy3ed852e2009-09-05 21:47:34 +00003577 number_threads;
3578
3579 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00003580 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00003581 sizeof(*polygon_info));
3582 if (polygon_info == (PolygonInfo **) NULL)
3583 return((PolygonInfo **) NULL);
3584 (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()*
3585 sizeof(*polygon_info));
3586 path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
3587 if (path_info == (PathInfo *) NULL)
3588 return(DestroyPolygonThreadSet(polygon_info));
cristybb503372010-05-27 20:51:26 +00003589 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003590 {
3591 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
3592 if (polygon_info[i] == (PolygonInfo *) NULL)
3593 return(DestroyPolygonThreadSet(polygon_info));
3594 }
3595 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
3596 return(polygon_info);
3597}
3598
cristy4c08aed2011-07-01 19:47:50 +00003599static MagickRealType GetPixelOpacity(PolygonInfo *polygon_info,
cristy3ed852e2009-09-05 21:47:34 +00003600 const MagickRealType mid,const MagickBooleanType fill,
cristy77f38fb2010-04-22 15:51:47 +00003601 const FillRule fill_rule,const double x,const double y,
cristy3ed852e2009-09-05 21:47:34 +00003602 MagickRealType *stroke_opacity)
3603{
cristy3ed852e2009-09-05 21:47:34 +00003604 MagickRealType
cristyb32b90a2009-09-07 21:45:48 +00003605 alpha,
3606 beta,
cristy3ed852e2009-09-05 21:47:34 +00003607 distance,
cristy3ed852e2009-09-05 21:47:34 +00003608 subpath_opacity;
3609
3610 PointInfo
cristyb32b90a2009-09-07 21:45:48 +00003611 delta;
cristy3ed852e2009-09-05 21:47:34 +00003612
3613 register EdgeInfo
3614 *p;
3615
cristyb32b90a2009-09-07 21:45:48 +00003616 register const PointInfo
3617 *q;
3618
cristybb503372010-05-27 20:51:26 +00003619 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003620 i;
3621
cristycee97112010-05-28 00:44:52 +00003622 ssize_t
3623 j,
3624 winding_number;
3625
cristy3ed852e2009-09-05 21:47:34 +00003626 /*
3627 Compute fill & stroke opacity for this (x,y) point.
3628 */
3629 *stroke_opacity=0.0;
3630 subpath_opacity=0.0;
cristy3ed852e2009-09-05 21:47:34 +00003631 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003632 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristy3ed852e2009-09-05 21:47:34 +00003633 {
cristyb32b90a2009-09-07 21:45:48 +00003634 if (y <= (p->bounds.y1-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003635 break;
cristyb32b90a2009-09-07 21:45:48 +00003636 if (y > (p->bounds.y2+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003637 {
cristybb503372010-05-27 20:51:26 +00003638 (void) DestroyEdge(polygon_info,(size_t) j);
cristy3ed852e2009-09-05 21:47:34 +00003639 continue;
3640 }
cristyb32b90a2009-09-07 21:45:48 +00003641 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003642 continue;
cristybb503372010-05-27 20:51:26 +00003643 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3644 for ( ; i < (ssize_t) p->number_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00003645 {
cristyb32b90a2009-09-07 21:45:48 +00003646 if (y <= (p->points[i-1].y-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003647 break;
cristyb32b90a2009-09-07 21:45:48 +00003648 if (y > (p->points[i].y+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003649 continue;
cristyb32b90a2009-09-07 21:45:48 +00003650 if (p->scanline != y)
cristy3ed852e2009-09-05 21:47:34 +00003651 {
cristyb32b90a2009-09-07 21:45:48 +00003652 p->scanline=y;
cristybb503372010-05-27 20:51:26 +00003653 p->highwater=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00003654 }
3655 /*
3656 Compute distance between a point and an edge.
3657 */
cristyb32b90a2009-09-07 21:45:48 +00003658 q=p->points+i-1;
3659 delta.x=(q+1)->x-q->x;
3660 delta.y=(q+1)->y-q->y;
3661 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
cristy3ed852e2009-09-05 21:47:34 +00003662 if (beta < 0.0)
3663 {
cristyb32b90a2009-09-07 21:45:48 +00003664 delta.x=x-q->x;
3665 delta.y=y-q->y;
cristy3ed852e2009-09-05 21:47:34 +00003666 distance=delta.x*delta.x+delta.y*delta.y;
3667 }
3668 else
3669 {
3670 alpha=delta.x*delta.x+delta.y*delta.y;
3671 if (beta > alpha)
3672 {
cristyb32b90a2009-09-07 21:45:48 +00003673 delta.x=x-(q+1)->x;
3674 delta.y=y-(q+1)->y;
cristy3ed852e2009-09-05 21:47:34 +00003675 distance=delta.x*delta.x+delta.y*delta.y;
3676 }
3677 else
3678 {
cristyb32b90a2009-09-07 21:45:48 +00003679 alpha=1.0/alpha;
3680 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
3681 distance=alpha*beta*beta;
cristy3ed852e2009-09-05 21:47:34 +00003682 }
3683 }
3684 /*
3685 Compute stroke & subpath opacity.
3686 */
3687 beta=0.0;
3688 if (p->ghostline == MagickFalse)
3689 {
cristyb32b90a2009-09-07 21:45:48 +00003690 alpha=mid+0.5;
cristy3ed852e2009-09-05 21:47:34 +00003691 if ((*stroke_opacity < 1.0) &&
3692 (distance <= ((alpha+0.25)*(alpha+0.25))))
3693 {
3694 alpha=mid-0.5;
3695 if (distance <= ((alpha+0.25)*(alpha+0.25)))
3696 *stroke_opacity=1.0;
3697 else
3698 {
3699 beta=1.0;
3700 if (distance != 1.0)
3701 beta=sqrt((double) distance);
cristyb32b90a2009-09-07 21:45:48 +00003702 alpha=beta-mid-0.5;
cristy3ed852e2009-09-05 21:47:34 +00003703 if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
3704 *stroke_opacity=(alpha-0.25)*(alpha-0.25);
3705 }
3706 }
3707 }
3708 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_opacity >= 1.0))
3709 continue;
3710 if (distance <= 0.0)
3711 {
3712 subpath_opacity=1.0;
3713 continue;
3714 }
3715 if (distance > 1.0)
3716 continue;
3717 if (beta == 0.0)
3718 {
3719 beta=1.0;
3720 if (distance != 1.0)
cristyb32b90a2009-09-07 21:45:48 +00003721 beta=sqrt(distance);
cristy3ed852e2009-09-05 21:47:34 +00003722 }
3723 alpha=beta-1.0;
cristyb32b90a2009-09-07 21:45:48 +00003724 if (subpath_opacity < (alpha*alpha))
cristy3ed852e2009-09-05 21:47:34 +00003725 subpath_opacity=alpha*alpha;
3726 }
cristy3ed852e2009-09-05 21:47:34 +00003727 }
3728 /*
3729 Compute fill opacity.
3730 */
3731 if (fill == MagickFalse)
3732 return(0.0);
3733 if (subpath_opacity >= 1.0)
3734 return(1.0);
cristyb32b90a2009-09-07 21:45:48 +00003735 /*
3736 Determine winding number.
3737 */
3738 winding_number=0;
3739 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003740 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristyb32b90a2009-09-07 21:45:48 +00003741 {
3742 if (y <= p->bounds.y1)
3743 break;
3744 if ((y > p->bounds.y2) || (x <= p->bounds.x1))
3745 continue;
3746 if (x > p->bounds.x2)
3747 {
3748 winding_number+=p->direction ? 1 : -1;
3749 continue;
3750 }
cristybb503372010-05-27 20:51:26 +00003751 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3752 for ( ; i < (ssize_t) p->number_points; i++)
cristyb32b90a2009-09-07 21:45:48 +00003753 if (y <= p->points[i].y)
3754 break;
3755 q=p->points+i-1;
3756 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
3757 winding_number+=p->direction ? 1 : -1;
3758 }
cristy3ed852e2009-09-05 21:47:34 +00003759 if (fill_rule != NonZeroRule)
3760 {
3761 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
3762 return(1.0);
3763 }
3764 else
3765 if (MagickAbsoluteValue(winding_number) != 0)
3766 return(1.0);
3767 return(subpath_opacity);
3768}
3769
3770static MagickBooleanType DrawPolygonPrimitive(Image *image,
3771 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
3772{
cristyfa112112010-01-04 17:48:07 +00003773 CacheView
3774 *image_view;
3775
cristy3ed852e2009-09-05 21:47:34 +00003776 ExceptionInfo
3777 *exception;
3778
cristy3ed852e2009-09-05 21:47:34 +00003779 MagickBooleanType
3780 fill,
3781 status;
3782
3783 MagickRealType
3784 mid;
3785
3786 PolygonInfo
cristyfa112112010-01-04 17:48:07 +00003787 **restrict polygon_info;
cristy3ed852e2009-09-05 21:47:34 +00003788
3789 register EdgeInfo
3790 *p;
3791
cristybb503372010-05-27 20:51:26 +00003792 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003793 i;
3794
3795 SegmentInfo
3796 bounds;
3797
cristy826a5472010-08-31 23:21:38 +00003798 ssize_t
3799 start,
3800 stop,
3801 y;
3802
cristy3ed852e2009-09-05 21:47:34 +00003803 /*
3804 Compute bounding box.
3805 */
3806 assert(image != (Image *) NULL);
3807 assert(image->signature == MagickSignature);
3808 if (image->debug != MagickFalse)
3809 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3810 assert(draw_info != (DrawInfo *) NULL);
3811 assert(draw_info->signature == MagickSignature);
3812 assert(primitive_info != (PrimitiveInfo *) NULL);
3813 if (primitive_info->coordinates == 0)
3814 return(MagickTrue);
3815 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3816 if (polygon_info == (PolygonInfo **) NULL)
3817 return(MagickFalse);
3818 if (0)
3819 DrawBoundingRectangles(image,draw_info,polygon_info[0]);
3820 if (image->debug != MagickFalse)
3821 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3822 fill=(primitive_info->method == FillToBorderMethod) ||
3823 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3824 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3825 bounds=polygon_info[0]->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00003826 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00003827 {
3828 p=polygon_info[0]->edges+i;
3829 if (p->bounds.x1 < bounds.x1)
3830 bounds.x1=p->bounds.x1;
3831 if (p->bounds.y1 < bounds.y1)
3832 bounds.y1=p->bounds.y1;
3833 if (p->bounds.x2 > bounds.x2)
3834 bounds.x2=p->bounds.x2;
3835 if (p->bounds.y2 > bounds.y2)
3836 bounds.y2=p->bounds.y2;
3837 }
3838 bounds.x1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003839 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003840 image->columns ? (double) image->columns-1.0 : bounds.x1;
3841 bounds.y1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003842 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003843 image->rows ? (double) image->rows-1.0 : bounds.y1;
3844 bounds.x2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003845 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003846 image->columns ? (double) image->columns-1.0 : bounds.x2;
3847 bounds.y2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003848 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003849 image->rows ? (double) image->rows-1.0 : bounds.y2;
3850 status=MagickTrue;
3851 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00003852 start=(ssize_t) ceil(bounds.x1-0.5);
3853 stop=(ssize_t) floor(bounds.x2+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003854 image_view=AcquireCacheView(image);
3855 if (primitive_info->coordinates == 1)
3856 {
3857 /*
3858 Draw point.
3859 */
cristyb5d5f722009-11-04 03:03:49 +00003860#if defined(MAGICKCORE_OPENMP_SUPPORT)
3861 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003862#endif
cristybb503372010-05-27 20:51:26 +00003863 for (y=(ssize_t) ceil(bounds.y1-0.5); y <= (ssize_t) floor(bounds.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00003864 {
3865 MagickBooleanType
3866 sync;
3867
cristy101ab702011-10-13 13:06:32 +00003868 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003869 pixel;
3870
cristybb503372010-05-27 20:51:26 +00003871 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003872 x;
3873
cristy4c08aed2011-07-01 19:47:50 +00003874 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003875 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003876
3877 if (status == MagickFalse)
3878 continue;
3879 x=start;
cristybb503372010-05-27 20:51:26 +00003880 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),
cristy3ed852e2009-09-05 21:47:34 +00003881 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003882 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003883 {
3884 status=MagickFalse;
3885 continue;
3886 }
cristy101ab702011-10-13 13:06:32 +00003887 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003888 for ( ; x <= stop; x++)
3889 {
cristybb503372010-05-27 20:51:26 +00003890 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3891 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
cristy4c08aed2011-07-01 19:47:50 +00003892 {
cristy2ed42f62011-10-02 19:49:57 +00003893 (void) GetStrokeColor(draw_info,x,y,&pixel,exception);
cristy101ab702011-10-13 13:06:32 +00003894 SetPixelPixelInfo(image,&pixel,q);
cristy4c08aed2011-07-01 19:47:50 +00003895 }
cristyed231572011-07-14 02:18:59 +00003896 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003897 }
3898 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3899 if (sync == MagickFalse)
3900 status=MagickFalse;
3901 }
3902 image_view=DestroyCacheView(image_view);
3903 polygon_info=DestroyPolygonThreadSet(polygon_info);
3904 if (image->debug != MagickFalse)
3905 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3906 " end draw-polygon");
3907 return(status);
3908 }
3909 /*
3910 Draw polygon or line.
3911 */
3912 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00003913 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristyb5d5f722009-11-04 03:03:49 +00003914#if defined(MAGICKCORE_OPENMP_SUPPORT)
3915 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003916#endif
cristybb503372010-05-27 20:51:26 +00003917 for (y=(ssize_t) ceil(bounds.y1-0.5); y <= (ssize_t) floor(bounds.y2+0.5); y++)
cristy3ed852e2009-09-05 21:47:34 +00003918 {
cristy5c9e6f22010-09-17 17:31:01 +00003919 const int
3920 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003921
cristy3ed852e2009-09-05 21:47:34 +00003922 MagickRealType
3923 fill_opacity,
3924 stroke_opacity;
3925
cristy101ab702011-10-13 13:06:32 +00003926 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003927 fill_color,
3928 stroke_color;
3929
cristy4c08aed2011-07-01 19:47:50 +00003930 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003931 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003932
cristy826a5472010-08-31 23:21:38 +00003933 register ssize_t
3934 x;
3935
cristy3ed852e2009-09-05 21:47:34 +00003936 if (status == MagickFalse)
3937 continue;
cristybb503372010-05-27 20:51:26 +00003938 q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-
cristy3ed852e2009-09-05 21:47:34 +00003939 start+1),1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003940 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003941 {
3942 status=MagickFalse;
3943 continue;
3944 }
cristy3ed852e2009-09-05 21:47:34 +00003945 for (x=start; x <= stop; x++)
3946 {
3947 /*
3948 Fill and/or stroke.
3949 */
cristy4c08aed2011-07-01 19:47:50 +00003950 fill_opacity=GetPixelOpacity(polygon_info[id],mid,fill,
cristy77f38fb2010-04-22 15:51:47 +00003951 draw_info->fill_rule,(double) x,(double) y,&stroke_opacity);
cristy3ed852e2009-09-05 21:47:34 +00003952 if (draw_info->stroke_antialias == MagickFalse)
3953 {
3954 fill_opacity=fill_opacity > 0.25 ? 1.0 : 0.0;
3955 stroke_opacity=stroke_opacity > 0.25 ? 1.0 : 0.0;
3956 }
cristy2ed42f62011-10-02 19:49:57 +00003957 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristy4c08aed2011-07-01 19:47:50 +00003958 fill_opacity=fill_opacity*fill_color.alpha;
3959 CompositePixelOver(image,&fill_color,fill_opacity,q,(MagickRealType)
3960 GetPixelAlpha(image,q),q);
cristy2ed42f62011-10-02 19:49:57 +00003961 (void) GetStrokeColor(draw_info,x,y,&stroke_color,exception);
cristy4c08aed2011-07-01 19:47:50 +00003962 stroke_opacity=stroke_opacity*stroke_color.alpha;
3963 CompositePixelOver(image,&stroke_color,stroke_opacity,q,(MagickRealType)
3964 GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00003965 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003966 }
3967 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3968 status=MagickFalse;
3969 }
3970 image_view=DestroyCacheView(image_view);
3971 polygon_info=DestroyPolygonThreadSet(polygon_info);
3972 if (image->debug != MagickFalse)
3973 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
3974 return(status);
3975}
3976
3977/*
3978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3979% %
3980% %
3981% %
3982% D r a w P r i m i t i v e %
3983% %
3984% %
3985% %
3986%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3987%
3988% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
3989%
3990% The format of the DrawPrimitive method is:
3991%
3992% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
3993% PrimitiveInfo *primitive_info)
3994%
3995% A description of each parameter follows:
3996%
3997% o image: the image.
3998%
3999% o draw_info: the draw info.
4000%
4001% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4002%
4003*/
4004
4005static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4006{
4007 const char
4008 *methods[] =
4009 {
4010 "point",
4011 "replace",
4012 "floodfill",
4013 "filltoborder",
4014 "reset",
4015 "?"
4016 };
4017
cristy3ed852e2009-09-05 21:47:34 +00004018 PointInfo
4019 p,
4020 q,
4021 point;
4022
cristybb503372010-05-27 20:51:26 +00004023 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004024 i,
4025 x;
4026
cristy826a5472010-08-31 23:21:38 +00004027 ssize_t
4028 coordinates,
4029 y;
4030
cristybb503372010-05-27 20:51:26 +00004031 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4032 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004033 switch (primitive_info->primitive)
4034 {
4035 case PointPrimitive:
4036 {
4037 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004038 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004039 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004040 return;
4041 }
4042 case ColorPrimitive:
4043 {
4044 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004045 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004046 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004047 return;
4048 }
4049 case MattePrimitive:
4050 {
4051 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004052 "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004053 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004054 return;
4055 }
4056 case TextPrimitive:
4057 {
4058 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004059 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004060 return;
4061 }
4062 case ImagePrimitive:
4063 {
4064 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004065 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004066 return;
4067 }
4068 default:
4069 break;
4070 }
4071 coordinates=0;
4072 p=primitive_info[0].point;
4073 q.x=(-1.0);
4074 q.y=(-1.0);
4075 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4076 {
4077 point=primitive_info[i].point;
4078 if (coordinates <= 0)
4079 {
cristybb503372010-05-27 20:51:26 +00004080 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004081 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004082 " begin open (%.20g)",(double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004083 p=point;
4084 }
4085 point=primitive_info[i].point;
4086 if ((fabs(q.x-point.x) > MagickEpsilon) ||
4087 (fabs(q.y-point.y) > MagickEpsilon))
cristy8cd5b312010-01-07 01:10:24 +00004088 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004089 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004090 else
4091 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004092 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004093 q=point;
4094 coordinates--;
4095 if (coordinates > 0)
4096 continue;
4097 if ((fabs(p.x-point.x) > MagickEpsilon) ||
4098 (fabs(p.y-point.y) > MagickEpsilon))
cristye8c25f92010-06-03 00:53:06 +00004099 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4100 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004101 else
cristye8c25f92010-06-03 00:53:06 +00004102 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4103 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004104 }
4105}
4106
4107MagickExport MagickBooleanType DrawPrimitive(Image *image,
4108 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4109{
cristyc4c8d132010-01-07 01:58:38 +00004110 CacheView
4111 *image_view;
4112
cristy3ed852e2009-09-05 21:47:34 +00004113 ExceptionInfo
4114 *exception;
4115
cristy3ed852e2009-09-05 21:47:34 +00004116 MagickStatusType
4117 status;
4118
cristybb503372010-05-27 20:51:26 +00004119 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004120 i,
4121 x;
4122
cristy826a5472010-08-31 23:21:38 +00004123 ssize_t
4124 y;
4125
cristy3ed852e2009-09-05 21:47:34 +00004126 if (image->debug != MagickFalse)
4127 {
4128 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4129 " begin draw-primitive");
4130 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004131 " affine: %g %g %g %g %g %g",draw_info->affine.sx,
cristy3ed852e2009-09-05 21:47:34 +00004132 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4133 draw_info->affine.tx,draw_info->affine.ty);
4134 }
4135 status=MagickTrue;
4136 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00004137 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4138 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004139 image_view=AcquireCacheView(image);
4140 switch (primitive_info->primitive)
4141 {
4142 case PointPrimitive:
4143 {
cristy101ab702011-10-13 13:06:32 +00004144 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004145 fill_color;
4146
cristy4c08aed2011-07-01 19:47:50 +00004147 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004148 *q;
4149
cristybb503372010-05-27 20:51:26 +00004150 if ((y < 0) || (y >= (ssize_t) image->rows))
cristyb32b90a2009-09-07 21:45:48 +00004151 break;
cristybb503372010-05-27 20:51:26 +00004152 if ((x < 0) || (x >= (ssize_t) image->columns))
cristyb32b90a2009-09-07 21:45:48 +00004153 break;
cristy3ed852e2009-09-05 21:47:34 +00004154 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004155 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004156 break;
cristy2ed42f62011-10-02 19:49:57 +00004157 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristy4c08aed2011-07-01 19:47:50 +00004158 CompositePixelOver(image,&fill_color,(MagickRealType) fill_color.alpha,q,
4159 (MagickRealType) GetPixelAlpha(image,q),q);
cristy3ed852e2009-09-05 21:47:34 +00004160 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4161 break;
4162 }
4163 case ColorPrimitive:
4164 {
4165 switch (primitive_info->method)
4166 {
4167 case PointMethod:
4168 default:
4169 {
cristy101ab702011-10-13 13:06:32 +00004170 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004171 pixel;
4172
4173 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004174 *q;
4175
4176 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004177 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004178 break;
cristy101ab702011-10-13 13:06:32 +00004179 GetPixelInfo(image,&pixel);
cristy2ed42f62011-10-02 19:49:57 +00004180 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy101ab702011-10-13 13:06:32 +00004181 SetPixelPixelInfo(image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004182 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4183 break;
4184 }
4185 case ReplaceMethod:
4186 {
4187 MagickBooleanType
4188 sync;
4189
cristy101ab702011-10-13 13:06:32 +00004190 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004191 pixel,
cristy3ed852e2009-09-05 21:47:34 +00004192 target;
4193
cristy2ed42f62011-10-02 19:49:57 +00004194 Quantum
4195 virtual_pixel[MaxPixelChannels];
4196
4197 (void) GetOneCacheViewVirtualPixel(image_view,x,y,virtual_pixel,
4198 exception);
cristyda1f9c12011-10-02 21:39:49 +00004199 target.red=(double) virtual_pixel[RedPixelChannel];
4200 target.green=(double) virtual_pixel[GreenPixelChannel];
4201 target.blue=(double) virtual_pixel[BluePixelChannel];
4202 target.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristybb503372010-05-27 20:51:26 +00004203 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004204 {
cristy4c08aed2011-07-01 19:47:50 +00004205 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004206 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004207
4208 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4209 exception);
cristy4c08aed2011-07-01 19:47:50 +00004210 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004211 break;
cristybb503372010-05-27 20:51:26 +00004212 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004213 {
cristy101ab702011-10-13 13:06:32 +00004214 GetPixelInfoPixel(image,q,&pixel);
4215 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004216 {
cristyed231572011-07-14 02:18:59 +00004217 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004218 continue;
4219 }
cristy2ed42f62011-10-02 19:49:57 +00004220 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy101ab702011-10-13 13:06:32 +00004221 SetPixelPixelInfo(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004222 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004223 }
4224 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4225 if (sync == MagickFalse)
4226 break;
4227 }
4228 break;
4229 }
4230 case FloodfillMethod:
4231 case FillToBorderMethod:
4232 {
cristy4c08aed2011-07-01 19:47:50 +00004233 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004234 target;
4235
4236 (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4237 if (primitive_info->method == FillToBorderMethod)
4238 {
4239 target.red=(MagickRealType) draw_info->border_color.red;
4240 target.green=(MagickRealType) draw_info->border_color.green;
4241 target.blue=(MagickRealType) draw_info->border_color.blue;
4242 }
cristyd42d9952011-07-08 14:21:50 +00004243 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
4244 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004245 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004246 break;
4247 }
4248 case ResetMethod:
4249 {
4250 MagickBooleanType
4251 sync;
4252
cristy101ab702011-10-13 13:06:32 +00004253 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004254 pixel;
4255
cristy101ab702011-10-13 13:06:32 +00004256 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00004257 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004258 {
cristy4c08aed2011-07-01 19:47:50 +00004259 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004260 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004261
cristy826a5472010-08-31 23:21:38 +00004262 register ssize_t
4263 x;
4264
cristy3ed852e2009-09-05 21:47:34 +00004265 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4266 exception);
cristy4c08aed2011-07-01 19:47:50 +00004267 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004268 break;
cristybb503372010-05-27 20:51:26 +00004269 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004270 {
cristy2ed42f62011-10-02 19:49:57 +00004271 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy101ab702011-10-13 13:06:32 +00004272 SetPixelPixelInfo(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004273 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004274 }
4275 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4276 if (sync == MagickFalse)
4277 break;
4278 }
4279 break;
4280 }
4281 }
4282 break;
4283 }
4284 case MattePrimitive:
4285 {
4286 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004287 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004288 switch (primitive_info->method)
4289 {
4290 case PointMethod:
4291 default:
4292 {
cristy101ab702011-10-13 13:06:32 +00004293 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004294 pixel;
4295
cristy4c08aed2011-07-01 19:47:50 +00004296 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004297 *q;
4298
4299 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004300 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004301 break;
cristy2ed42f62011-10-02 19:49:57 +00004302 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004303 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00004304 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4305 break;
4306 }
4307 case ReplaceMethod:
4308 {
4309 MagickBooleanType
4310 sync;
4311
cristy101ab702011-10-13 13:06:32 +00004312 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004313 pixel,
4314 target;
4315
cristy2ed42f62011-10-02 19:49:57 +00004316 Quantum
4317 virtual_pixel[MaxPixelChannels];
4318
4319 (void) GetOneCacheViewVirtualPixel(image_view,x,y,virtual_pixel,
4320 exception);
cristyda1f9c12011-10-02 21:39:49 +00004321 target.red=(double) virtual_pixel[RedPixelChannel];
4322 target.green=(double) virtual_pixel[GreenPixelChannel];
4323 target.blue=(double) virtual_pixel[BluePixelChannel];
4324 target.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristybb503372010-05-27 20:51:26 +00004325 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004326 {
cristy4c08aed2011-07-01 19:47:50 +00004327 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004328 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004329
cristy826a5472010-08-31 23:21:38 +00004330 register ssize_t
4331 x;
4332
cristy3ed852e2009-09-05 21:47:34 +00004333 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4334 exception);
cristy4c08aed2011-07-01 19:47:50 +00004335 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004336 break;
cristybb503372010-05-27 20:51:26 +00004337 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004338 {
cristy101ab702011-10-13 13:06:32 +00004339 GetPixelInfoPixel(image,q,&pixel);
4340 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004341 {
cristyed231572011-07-14 02:18:59 +00004342 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004343 continue;
4344 }
cristy2ed42f62011-10-02 19:49:57 +00004345 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004346 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004347 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004348 }
4349 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4350 if (sync == MagickFalse)
4351 break;
4352 }
4353 break;
4354 }
4355 case FloodfillMethod:
4356 case FillToBorderMethod:
4357 {
cristybd5a96c2011-08-21 00:04:26 +00004358 ChannelType
4359 channel_mask;
4360
cristy4c08aed2011-07-01 19:47:50 +00004361 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004362 target;
4363
4364 (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
4365 if (primitive_info->method == FillToBorderMethod)
4366 {
4367 target.red=(MagickRealType) draw_info->border_color.red;
4368 target.green=(MagickRealType) draw_info->border_color.green;
4369 target.blue=(MagickRealType) draw_info->border_color.blue;
4370 }
cristybd5a96c2011-08-21 00:04:26 +00004371 channel_mask=SetPixelChannelMask(image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +00004372 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
cristy3ed852e2009-09-05 21:47:34 +00004373 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004374 MagickTrue,exception);
cristybd5a96c2011-08-21 00:04:26 +00004375 (void) SetPixelChannelMask(image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004376 break;
4377 }
4378 case ResetMethod:
4379 {
4380 MagickBooleanType
4381 sync;
4382
cristy101ab702011-10-13 13:06:32 +00004383 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004384 pixel;
4385
cristybb503372010-05-27 20:51:26 +00004386 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004387 {
cristy4c08aed2011-07-01 19:47:50 +00004388 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004389 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004390
cristy826a5472010-08-31 23:21:38 +00004391 register ssize_t
4392 x;
4393
cristy3ed852e2009-09-05 21:47:34 +00004394 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4395 exception);
cristy4c08aed2011-07-01 19:47:50 +00004396 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004397 break;
cristybb503372010-05-27 20:51:26 +00004398 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004399 {
cristy2ed42f62011-10-02 19:49:57 +00004400 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004401 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004402 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004403 }
4404 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4405 if (sync == MagickFalse)
4406 break;
4407 }
4408 break;
4409 }
4410 }
4411 break;
4412 }
4413 case TextPrimitive:
4414 {
4415 char
4416 geometry[MaxTextExtent];
4417
4418 DrawInfo
4419 *clone_info;
4420
4421 if (primitive_info->text == (char *) NULL)
4422 break;
4423 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4424 (void) CloneString(&clone_info->text,primitive_info->text);
cristyb51dff52011-05-19 16:55:47 +00004425 (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
cristy3ed852e2009-09-05 21:47:34 +00004426 primitive_info->point.x,primitive_info->point.y);
4427 (void) CloneString(&clone_info->geometry,geometry);
cristy5cbc0162011-08-29 00:36:28 +00004428 status=AnnotateImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004429 clone_info=DestroyDrawInfo(clone_info);
4430 break;
4431 }
4432 case ImagePrimitive:
4433 {
4434 AffineMatrix
4435 affine;
4436
4437 char
4438 composite_geometry[MaxTextExtent];
4439
4440 Image
4441 *composite_image;
4442
4443 ImageInfo
4444 *clone_info;
4445
cristy826a5472010-08-31 23:21:38 +00004446 RectangleInfo
4447 geometry;
4448
cristybb503372010-05-27 20:51:26 +00004449 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004450 x1,
4451 y1;
4452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (primitive_info->text == (char *) NULL)
4454 break;
4455 clone_info=AcquireImageInfo();
4456 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4457 composite_image=ReadInlineImage(clone_info,primitive_info->text,
4458 &image->exception);
4459 else
4460 {
4461 (void) CopyMagickString(clone_info->filename,primitive_info->text,
4462 MaxTextExtent);
4463 composite_image=ReadImage(clone_info,&image->exception);
4464 }
4465 clone_info=DestroyImageInfo(clone_info);
4466 if (composite_image == (Image *) NULL)
4467 break;
4468 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4469 NULL,(void *) NULL);
cristybb503372010-05-27 20:51:26 +00004470 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4471 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4472 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4473 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
cristy3ed852e2009-09-05 21:47:34 +00004474 {
4475 char
4476 geometry[MaxTextExtent];
4477
4478 /*
4479 Resize image.
4480 */
cristyb51dff52011-05-19 16:55:47 +00004481 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
cristy3ed852e2009-09-05 21:47:34 +00004482 primitive_info[1].point.x,primitive_info[1].point.y);
4483 composite_image->filter=image->filter;
4484 (void) TransformImage(&composite_image,(char *) NULL,geometry);
4485 }
4486 if (composite_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004487 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
4488 exception);
cristy4c08aed2011-07-01 19:47:50 +00004489 if (draw_info->alpha != OpaqueAlpha)
cristyb6a294d2011-10-03 00:55:17 +00004490 (void) SetImageAlpha(composite_image,draw_info->alpha);
cristy3ed852e2009-09-05 21:47:34 +00004491 SetGeometry(image,&geometry);
4492 image->gravity=draw_info->gravity;
4493 geometry.x=x;
4494 geometry.y=y;
cristyb51dff52011-05-19 16:55:47 +00004495 (void) FormatLocaleString(composite_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004496 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
cristye8c25f92010-06-03 00:53:06 +00004497 composite_image->rows,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00004498 (void) ParseGravityGeometry(image,composite_geometry,&geometry,
4499 &image->exception);
4500 affine=draw_info->affine;
4501 affine.tx=(double) geometry.x;
4502 affine.ty=(double) geometry.y;
4503 composite_image->interpolate=image->interpolate;
cristyd5f3fc32011-04-26 14:44:36 +00004504 if (draw_info->compose == OverCompositeOp)
4505 (void) DrawAffineImage(image,composite_image,&affine);
4506 else
4507 (void) CompositeImage(image,draw_info->compose,composite_image,
4508 geometry.x,geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00004509 composite_image=DestroyImage(composite_image);
4510 break;
4511 }
4512 default:
4513 {
4514 MagickRealType
4515 mid,
4516 scale;
4517
4518 DrawInfo
4519 *clone_info;
4520
4521 if (IsEventLogging() != MagickFalse)
4522 LogPrimitiveInfo(primitive_info);
4523 scale=ExpandAffine(&draw_info->affine);
4524 if ((draw_info->dash_pattern != (double *) NULL) &&
4525 (draw_info->dash_pattern[0] != 0.0) &&
4526 ((scale*draw_info->stroke_width) > MagickEpsilon) &&
cristy4c08aed2011-07-01 19:47:50 +00004527 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004528 {
4529 /*
4530 Draw dash polygon.
4531 */
4532 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4533 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004534 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004535 status=DrawPolygonPrimitive(image,clone_info,primitive_info);
4536 clone_info=DestroyDrawInfo(clone_info);
4537 (void) DrawDashPolygon(draw_info,primitive_info,image);
4538 break;
4539 }
4540 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4541 if ((mid > 1.0) &&
cristy4c08aed2011-07-01 19:47:50 +00004542 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004543 {
4544 MagickBooleanType
4545 closed_path;
4546
4547 /*
4548 Draw strokes while respecting line cap/join attributes.
4549 */
4550 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4551 closed_path=
4552 (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4553 (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4554 MagickTrue : MagickFalse;
cristybb503372010-05-27 20:51:26 +00004555 i=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004556 if ((((draw_info->linecap == RoundCap) ||
4557 (closed_path != MagickFalse)) &&
4558 (draw_info->linejoin == RoundJoin)) ||
4559 (primitive_info[i].primitive != UndefinedPrimitive))
4560 {
4561 (void) DrawPolygonPrimitive(image,draw_info,primitive_info);
4562 break;
4563 }
4564 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4565 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004566 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004567 status=DrawPolygonPrimitive(image,clone_info,primitive_info);
4568 clone_info=DestroyDrawInfo(clone_info);
4569 status|=DrawStrokePolygon(image,draw_info,primitive_info);
4570 break;
4571 }
4572 status=DrawPolygonPrimitive(image,draw_info,primitive_info);
4573 break;
4574 }
4575 }
4576 image_view=DestroyCacheView(image_view);
4577 if (image->debug != MagickFalse)
4578 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4579 return(status != 0 ? MagickTrue : MagickFalse);
4580}
4581
4582/*
4583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4584% %
4585% %
4586% %
4587+ D r a w S t r o k e P o l y g o n %
4588% %
4589% %
4590% %
4591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4592%
4593% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4594% the image while respecting the line cap and join attributes.
4595%
4596% The format of the DrawStrokePolygon method is:
4597%
4598% MagickBooleanType DrawStrokePolygon(Image *image,
4599% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4600%
4601% A description of each parameter follows:
4602%
4603% o image: the image.
4604%
4605% o draw_info: the draw info.
4606%
4607% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4608%
4609%
4610*/
4611
4612static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
4613 const PrimitiveInfo *primitive_info)
4614{
4615 PrimitiveInfo
4616 linecap[5];
4617
cristybb503372010-05-27 20:51:26 +00004618 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004619 i;
4620
4621 for (i=0; i < 4; i++)
4622 linecap[i]=(*primitive_info);
4623 linecap[0].coordinates=4;
4624 linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4625 linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4626 linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4627 linecap[3].point.y+=(double) (10.0*MagickEpsilon);
4628 linecap[4].primitive=UndefinedPrimitive;
4629 (void) DrawPolygonPrimitive(image,draw_info,linecap);
4630}
4631
4632static MagickBooleanType DrawStrokePolygon(Image *image,
4633 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4634{
4635 DrawInfo
4636 *clone_info;
4637
4638 MagickBooleanType
4639 closed_path,
4640 status;
4641
4642 PrimitiveInfo
4643 *stroke_polygon;
4644
4645 register const PrimitiveInfo
4646 *p,
4647 *q;
4648
4649 /*
4650 Draw stroked polygon.
4651 */
4652 if (image->debug != MagickFalse)
4653 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4654 " begin draw-stroke-polygon");
4655 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4656 clone_info->fill=draw_info->stroke;
cristy4c08aed2011-07-01 19:47:50 +00004657 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004658 clone_info->stroke_width=0.0;
4659 clone_info->fill_rule=NonZeroRule;
4660 status=MagickTrue;
4661 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4662 {
4663 stroke_polygon=TraceStrokePolygon(draw_info,p);
4664 status=DrawPolygonPrimitive(image,clone_info,stroke_polygon);
4665 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4666 q=p+p->coordinates-1;
4667 closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4668 MagickTrue : MagickFalse;
4669 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4670 {
4671 DrawRoundLinecap(image,draw_info,p);
4672 DrawRoundLinecap(image,draw_info,q);
4673 }
4674 }
4675 clone_info=DestroyDrawInfo(clone_info);
4676 if (image->debug != MagickFalse)
4677 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4678 " end draw-stroke-polygon");
4679 return(status);
4680}
4681
4682/*
4683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4684% %
4685% %
4686% %
4687% G e t A f f i n e M a t r i x %
4688% %
4689% %
4690% %
4691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4692%
4693% GetAffineMatrix() returns an AffineMatrix initialized to the identity
4694% matrix.
4695%
4696% The format of the GetAffineMatrix method is:
4697%
4698% void GetAffineMatrix(AffineMatrix *affine_matrix)
4699%
4700% A description of each parameter follows:
4701%
4702% o affine_matrix: the affine matrix.
4703%
4704*/
4705MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
4706{
4707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4708 assert(affine_matrix != (AffineMatrix *) NULL);
4709 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4710 affine_matrix->sx=1.0;
4711 affine_matrix->sy=1.0;
4712}
4713
4714/*
4715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4716% %
4717% %
4718% %
4719+ G e t D r a w I n f o %
4720% %
4721% %
4722% %
4723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4724%
anthony9c88e8f2011-09-29 11:31:53 +00004725% GetDrawInfo() initializes draw_info to default values from image_info.
cristy3ed852e2009-09-05 21:47:34 +00004726%
4727% The format of the GetDrawInfo method is:
4728%
4729% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4730%
4731% A description of each parameter follows:
4732%
4733% o image_info: the image info..
4734%
4735% o draw_info: the draw info.
4736%
4737*/
4738MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4739{
4740 const char
4741 *option;
4742
4743 ExceptionInfo
4744 *exception;
4745
cristy3ed852e2009-09-05 21:47:34 +00004746 /*
4747 Initialize draw attributes.
4748 */
4749 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4750 assert(draw_info != (DrawInfo *) NULL);
4751 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +00004752 GetAffineMatrix(&draw_info->affine);
4753 exception=AcquireExceptionInfo();
cristy9950d572011-10-01 18:22:35 +00004754 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
4755 exception);
4756 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
4757 exception);
cristy3ed852e2009-09-05 21:47:34 +00004758 draw_info->stroke_width=1.0;
cristy4c08aed2011-07-01 19:47:50 +00004759 draw_info->alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004760 draw_info->fill_rule=EvenOddRule;
4761 draw_info->linecap=ButtCap;
4762 draw_info->linejoin=MiterJoin;
4763 draw_info->miterlimit=10;
4764 draw_info->decorate=NoDecoration;
cristy3ed852e2009-09-05 21:47:34 +00004765 draw_info->pointsize=12.0;
cristy4c08aed2011-07-01 19:47:50 +00004766 draw_info->undercolor.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004767 draw_info->compose=OverCompositeOp;
cristy3ed852e2009-09-05 21:47:34 +00004768 draw_info->render=MagickTrue;
4769 draw_info->debug=IsEventLogging();
cristy0245b5d2011-10-05 12:03:49 +00004770 if (image_info != (ImageInfo *) NULL)
4771 {
4772 draw_info->stroke_antialias=image_info->antialias;
4773 if (image_info->font != (char *) NULL)
4774 draw_info->font=AcquireString(image_info->font);
4775 if (image_info->density != (char *) NULL)
4776 draw_info->density=AcquireString(image_info->density);
4777 draw_info->text_antialias=image_info->antialias;
4778 if (image_info->pointsize != 0.0)
4779 draw_info->pointsize=image_info->pointsize;
4780 draw_info->border_color=image_info->border_color;
4781 if (image_info->server_name != (char *) NULL)
4782 draw_info->server_name=AcquireString(image_info->server_name);
4783 option=GetImageOption(image_info,"encoding");
4784 if (option != (const char *) NULL)
4785 (void) CloneString(&draw_info->encoding,option);
4786 option=GetImageOption(image_info,"kerning");
4787 if (option != (const char *) NULL)
4788 draw_info->kerning=InterpretLocaleValue(option,(char **) NULL);
4789 option=GetImageOption(image_info,"interline-spacing");
4790 if (option != (const char *) NULL)
4791 draw_info->interline_spacing=InterpretLocaleValue(option,
4792 (char **) NULL);
4793 draw_info->direction=UndefinedDirection;
4794 option=GetImageOption(image_info,"interword-spacing");
4795 if (option != (const char *) NULL)
4796 draw_info->interword_spacing=InterpretLocaleValue(option,
4797 (char **) NULL);
4798 option=GetImageOption(image_info,"direction");
4799 if (option != (const char *) NULL)
4800 draw_info->direction=(DirectionType) ParseCommandOption(
4801 MagickDirectionOptions,MagickFalse,option);
4802 option=GetImageOption(image_info,"fill");
4803 if (option != (const char *) NULL)
4804 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
4805 exception);
4806 option=GetImageOption(image_info,"stroke");
4807 if (option != (const char *) NULL)
4808 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
4809 exception);
4810 option=GetImageOption(image_info,"strokewidth");
4811 if (option != (const char *) NULL)
4812 draw_info->stroke_width=InterpretLocaleValue(option,(char **) NULL);
4813 option=GetImageOption(image_info,"undercolor");
4814 if (option != (const char *) NULL)
4815 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
4816 exception);
4817 option=GetImageOption(image_info,"gravity");
4818 if (option != (const char *) NULL)
4819 draw_info->gravity=(GravityType) ParseCommandOption(
4820 MagickGravityOptions,MagickFalse,option);
4821 }
cristy3ed852e2009-09-05 21:47:34 +00004822 exception=DestroyExceptionInfo(exception);
4823 draw_info->signature=MagickSignature;
cristy3ed852e2009-09-05 21:47:34 +00004824}
4825
4826/*
4827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4828% %
4829% %
4830% %
4831+ P e r m u t a t e %
4832% %
4833% %
4834% %
4835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4836%
4837% Permutate() returns the permuation of the (n,k).
4838%
4839% The format of the Permutate method is:
4840%
cristybb503372010-05-27 20:51:26 +00004841% void Permutate(ssize_t n,ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004842%
4843% A description of each parameter follows:
4844%
4845% o n:
4846%
4847% o k:
4848%
4849%
4850*/
cristybb503372010-05-27 20:51:26 +00004851static inline MagickRealType Permutate(const ssize_t n,const ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004852{
4853 MagickRealType
4854 r;
4855
cristybb503372010-05-27 20:51:26 +00004856 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004857 i;
4858
4859 r=1.0;
4860 for (i=k+1; i <= n; i++)
4861 r*=i;
4862 for (i=1; i <= (n-k); i++)
4863 r/=i;
4864 return(r);
4865}
4866
4867/*
4868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4869% %
4870% %
4871% %
4872+ T r a c e P r i m i t i v e %
4873% %
4874% %
4875% %
4876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4877%
4878% TracePrimitive is a collection of methods for generating graphic
4879% primitives such as arcs, ellipses, paths, etc.
4880%
4881*/
4882
4883static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4884 const PointInfo end,const PointInfo degrees)
4885{
4886 PointInfo
4887 center,
4888 radii;
4889
4890 center.x=0.5*(end.x+start.x);
4891 center.y=0.5*(end.y+start.y);
4892 radii.x=fabs(center.x-start.x);
4893 radii.y=fabs(center.y-start.y);
4894 TraceEllipse(primitive_info,center,radii,degrees);
4895}
4896
4897static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
4898 const PointInfo end,const PointInfo arc,const MagickRealType angle,
4899 const MagickBooleanType large_arc,const MagickBooleanType sweep)
4900{
4901 MagickRealType
4902 alpha,
4903 beta,
4904 delta,
4905 factor,
4906 gamma,
4907 theta;
4908
4909 PointInfo
4910 center,
4911 points[3],
4912 radii;
4913
4914 register MagickRealType
4915 cosine,
4916 sine;
4917
4918 register PrimitiveInfo
4919 *p;
4920
cristybb503372010-05-27 20:51:26 +00004921 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004922 i;
4923
cristybb503372010-05-27 20:51:26 +00004924 size_t
cristy3ed852e2009-09-05 21:47:34 +00004925 arc_segments;
4926
4927 if ((start.x == end.x) && (start.y == end.y))
4928 {
4929 TracePoint(primitive_info,end);
4930 return;
4931 }
4932 radii.x=fabs(arc.x);
4933 radii.y=fabs(arc.y);
4934 if ((radii.x == 0.0) || (radii.y == 0.0))
4935 {
4936 TraceLine(primitive_info,start,end);
4937 return;
4938 }
4939 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
4940 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
4941 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
4942 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
4943 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
4944 (radii.y*radii.y);
4945 if (delta < MagickEpsilon)
4946 {
4947 TraceLine(primitive_info,start,end);
4948 return;
4949 }
4950 if (delta > 1.0)
4951 {
4952 radii.x*=sqrt((double) delta);
4953 radii.y*=sqrt((double) delta);
4954 }
4955 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
4956 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
4957 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
4958 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
4959 alpha=points[1].x-points[0].x;
4960 beta=points[1].y-points[0].y;
4961 factor=1.0/(alpha*alpha+beta*beta)-0.25;
4962 if (factor <= 0.0)
4963 factor=0.0;
4964 else
4965 {
4966 factor=sqrt((double) factor);
4967 if (sweep == large_arc)
4968 factor=(-factor);
4969 }
4970 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
4971 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
4972 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
4973 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
4974 if ((theta < 0.0) && (sweep != MagickFalse))
4975 theta+=(MagickRealType) (2.0*MagickPI);
4976 else
4977 if ((theta > 0.0) && (sweep == MagickFalse))
4978 theta-=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00004979 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
cristy20be8a02010-08-17 00:23:28 +00004980 MagickEpsilon))));
cristy3ed852e2009-09-05 21:47:34 +00004981 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00004982 for (i=0; i < (ssize_t) arc_segments; i++)
cristy3ed852e2009-09-05 21:47:34 +00004983 {
4984 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
4985 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
4986 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
4987 sin(fmod((double) beta,DegreesToRadians(360.0)));
4988 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
4989 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
4990 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
4991 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
4992 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
4993 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
4994 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
4995 theta/arc_segments),DegreesToRadians(360.0))));
4996 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
4997 theta/arc_segments),DegreesToRadians(360.0))));
4998 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
4999 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5000 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
5001 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5002 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
5003 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
5004 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
5005 points[0].y);
5006 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
5007 points[0].y);
5008 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
5009 points[1].y);
5010 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
5011 points[1].y);
5012 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
5013 points[2].y);
5014 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
5015 points[2].y);
cristybb503372010-05-27 20:51:26 +00005016 if (i == (ssize_t) (arc_segments-1))
cristy3ed852e2009-09-05 21:47:34 +00005017 (p+3)->point=end;
5018 TraceBezier(p,4);
5019 p+=p->coordinates;
5020 }
cristybb503372010-05-27 20:51:26 +00005021 primitive_info->coordinates=(size_t) (p-primitive_info);
5022 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005023 {
5024 p->primitive=primitive_info->primitive;
5025 p--;
5026 }
5027}
5028
5029static void TraceBezier(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005030 const size_t number_coordinates)
cristy3ed852e2009-09-05 21:47:34 +00005031{
5032 MagickRealType
5033 alpha,
5034 *coefficients,
5035 weight;
5036
5037 PointInfo
5038 end,
5039 point,
5040 *points;
5041
cristy826a5472010-08-31 23:21:38 +00005042 register PrimitiveInfo
5043 *p;
5044
cristybb503372010-05-27 20:51:26 +00005045 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005046 i,
5047 j;
5048
cristybb503372010-05-27 20:51:26 +00005049 size_t
cristy3ed852e2009-09-05 21:47:34 +00005050 control_points,
5051 quantum;
5052
5053 /*
5054 Allocate coeficients.
5055 */
5056 quantum=number_coordinates;
cristybb503372010-05-27 20:51:26 +00005057 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005058 {
cristybb503372010-05-27 20:51:26 +00005059 for (j=i+1; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005060 {
5061 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
5062 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005063 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005064 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
5065 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005066 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005067 }
5068 }
cristybb503372010-05-27 20:51:26 +00005069 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
cristy3ed852e2009-09-05 21:47:34 +00005070 (double) BezierQuantum);
5071 control_points=quantum*number_coordinates;
5072 coefficients=(MagickRealType *) AcquireQuantumMemory((size_t)
5073 number_coordinates,sizeof(*coefficients));
5074 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5075 sizeof(*points));
5076 if ((coefficients == (MagickRealType *) NULL) ||
5077 (points == (PointInfo *) NULL))
5078 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5079 /*
5080 Compute bezier points.
5081 */
5082 end=primitive_info[number_coordinates-1].point;
cristybb503372010-05-27 20:51:26 +00005083 for (i=0; i < (ssize_t) number_coordinates; i++)
5084 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
cristy3ed852e2009-09-05 21:47:34 +00005085 weight=0.0;
cristybb503372010-05-27 20:51:26 +00005086 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005087 {
5088 p=primitive_info;
5089 point.x=0.0;
5090 point.y=0.0;
5091 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
cristybb503372010-05-27 20:51:26 +00005092 for (j=0; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005093 {
5094 point.x+=alpha*coefficients[j]*p->point.x;
5095 point.y+=alpha*coefficients[j]*p->point.y;
5096 alpha*=weight/(1.0-weight);
5097 p++;
5098 }
5099 points[i]=point;
5100 weight+=1.0/control_points;
5101 }
5102 /*
5103 Bezier curves are just short segmented polys.
5104 */
5105 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005106 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005107 {
5108 TracePoint(p,points[i]);
5109 p+=p->coordinates;
5110 }
5111 TracePoint(p,end);
5112 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005113 primitive_info->coordinates=(size_t) (p-primitive_info);
5114 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005115 {
5116 p->primitive=primitive_info->primitive;
5117 p--;
5118 }
5119 points=(PointInfo *) RelinquishMagickMemory(points);
5120 coefficients=(MagickRealType *) RelinquishMagickMemory(coefficients);
5121}
5122
5123static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5124 const PointInfo end)
5125{
5126 MagickRealType
5127 alpha,
5128 beta,
5129 radius;
5130
5131 PointInfo
5132 offset,
5133 degrees;
5134
5135 alpha=end.x-start.x;
5136 beta=end.y-start.y;
5137 radius=hypot((double) alpha,(double) beta);
5138 offset.x=(double) radius;
5139 offset.y=(double) radius;
5140 degrees.x=0.0;
5141 degrees.y=360.0;
5142 TraceEllipse(primitive_info,start,offset,degrees);
5143}
5144
5145static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5146 const PointInfo stop,const PointInfo degrees)
5147{
5148 MagickRealType
5149 delta,
5150 step,
5151 y;
5152
5153 PointInfo
5154 angle,
5155 point;
5156
5157 register PrimitiveInfo
5158 *p;
5159
cristybb503372010-05-27 20:51:26 +00005160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005161 i;
5162
5163 /*
5164 Ellipses are just short segmented polys.
5165 */
5166 if ((stop.x == 0.0) && (stop.y == 0.0))
5167 {
5168 TracePoint(primitive_info,start);
5169 return;
5170 }
5171 delta=2.0/MagickMax(stop.x,stop.y);
5172 step=(MagickRealType) (MagickPI/8.0);
cristyd4e3ffa2011-01-20 13:47:55 +00005173 if ((delta >= 0.0) && (delta < (MagickRealType) (MagickPI/8.0)))
5174 step=(MagickRealType) (MagickPI/(4*(MagickPI/delta/2+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00005175 angle.x=DegreesToRadians(degrees.x);
5176 y=degrees.y;
5177 while (y < degrees.x)
5178 y+=360.0;
5179 angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
5180 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5181 {
5182 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5183 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5184 TracePoint(p,point);
5185 p+=p->coordinates;
5186 }
5187 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5188 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5189 TracePoint(p,point);
5190 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005191 primitive_info->coordinates=(size_t) (p-primitive_info);
5192 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005193 {
5194 p->primitive=primitive_info->primitive;
5195 p--;
5196 }
5197}
5198
5199static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5200 const PointInfo end)
5201{
5202 TracePoint(primitive_info,start);
5203 if ((fabs(start.x-end.x) <= MagickEpsilon) &&
5204 (fabs(start.y-end.y) <= MagickEpsilon))
5205 {
5206 primitive_info->primitive=PointPrimitive;
5207 primitive_info->coordinates=1;
5208 return;
5209 }
5210 TracePoint(primitive_info+1,end);
5211 (primitive_info+1)->primitive=primitive_info->primitive;
5212 primitive_info->coordinates=2;
5213}
5214
cristybb503372010-05-27 20:51:26 +00005215static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00005216{
5217 char
5218 token[MaxTextExtent];
5219
5220 const char
5221 *p;
5222
5223 int
5224 attribute,
5225 last_attribute;
5226
5227 MagickRealType
5228 x,
5229 y;
5230
5231 PointInfo
5232 end,
5233 points[4],
5234 point,
5235 start;
5236
5237 PrimitiveType
5238 primitive_type;
5239
5240 register PrimitiveInfo
5241 *q;
5242
cristybb503372010-05-27 20:51:26 +00005243 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005244 i;
5245
cristybb503372010-05-27 20:51:26 +00005246 size_t
cristy3ed852e2009-09-05 21:47:34 +00005247 number_coordinates,
5248 z_count;
5249
5250 attribute=0;
5251 point.x=0.0;
5252 point.y=0.0;
5253 start.x=0.0;
5254 start.y=0.0;
5255 number_coordinates=0;
5256 z_count=0;
5257 primitive_type=primitive_info->primitive;
5258 q=primitive_info;
5259 for (p=path; *p != '\0'; )
5260 {
5261 while (isspace((int) ((unsigned char) *p)) != 0)
5262 p++;
5263 if (*p == '\0')
5264 break;
5265 last_attribute=attribute;
5266 attribute=(int) (*p++);
5267 switch (attribute)
5268 {
5269 case 'a':
5270 case 'A':
5271 {
5272 MagickBooleanType
5273 large_arc,
5274 sweep;
5275
5276 MagickRealType
5277 angle;
5278
5279 PointInfo
5280 arc;
5281
5282 /*
5283 Compute arc points.
5284 */
5285 do
5286 {
5287 GetMagickToken(p,&p,token);
5288 if (*token == ',')
5289 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005290 arc.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005291 GetMagickToken(p,&p,token);
5292 if (*token == ',')
5293 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005294 arc.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005295 GetMagickToken(p,&p,token);
5296 if (*token == ',')
5297 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005298 angle=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005299 GetMagickToken(p,&p,token);
5300 if (*token == ',')
5301 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005302 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005303 GetMagickToken(p,&p,token);
5304 if (*token == ',')
5305 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005306 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005307 GetMagickToken(p,&p,token);
5308 if (*token == ',')
5309 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005310 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005311 GetMagickToken(p,&p,token);
5312 if (*token == ',')
5313 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005314 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005315 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5316 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5317 TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5318 q+=q->coordinates;
5319 point=end;
5320 } while (IsPoint(p) != MagickFalse);
5321 break;
5322 }
5323 case 'c':
5324 case 'C':
5325 {
5326 /*
5327 Compute bezier points.
5328 */
5329 do
5330 {
5331 points[0]=point;
5332 for (i=1; i < 4; i++)
5333 {
5334 GetMagickToken(p,&p,token);
5335 if (*token == ',')
5336 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005337 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005338 GetMagickToken(p,&p,token);
5339 if (*token == ',')
5340 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005341 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005342 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5343 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5344 points[i]=end;
5345 }
5346 for (i=0; i < 4; i++)
5347 (q+i)->point=points[i];
5348 TraceBezier(q,4);
5349 q+=q->coordinates;
5350 point=end;
5351 } while (IsPoint(p) != MagickFalse);
5352 break;
5353 }
5354 case 'H':
5355 case 'h':
5356 {
5357 do
5358 {
5359 GetMagickToken(p,&p,token);
5360 if (*token == ',')
5361 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005362 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005363 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
5364 TracePoint(q,point);
5365 q+=q->coordinates;
5366 } while (IsPoint(p) != MagickFalse);
5367 break;
5368 }
5369 case 'l':
5370 case 'L':
5371 {
5372 do
5373 {
5374 GetMagickToken(p,&p,token);
5375 if (*token == ',')
5376 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005377 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005378 GetMagickToken(p,&p,token);
5379 if (*token == ',')
5380 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005381 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005382 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
5383 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
5384 TracePoint(q,point);
5385 q+=q->coordinates;
5386 } while (IsPoint(p) != MagickFalse);
5387 break;
5388 }
5389 case 'M':
5390 case 'm':
5391 {
5392 if (q != primitive_info)
5393 {
cristybb503372010-05-27 20:51:26 +00005394 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005395 number_coordinates+=primitive_info->coordinates;
5396 primitive_info=q;
5397 }
5398 i=0;
5399 do
5400 {
5401 GetMagickToken(p,&p,token);
5402 if (*token == ',')
5403 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005404 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005405 GetMagickToken(p,&p,token);
5406 if (*token == ',')
5407 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005408 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005409 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
5410 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
5411 if (i == 0)
5412 start=point;
5413 i++;
5414 TracePoint(q,point);
5415 q+=q->coordinates;
cristy826a5472010-08-31 23:21:38 +00005416 if ((i != 0) && (attribute == (int) 'M'))
cristy3ed852e2009-09-05 21:47:34 +00005417 {
5418 TracePoint(q,point);
5419 q+=q->coordinates;
5420 }
5421 } while (IsPoint(p) != MagickFalse);
5422 break;
5423 }
5424 case 'q':
5425 case 'Q':
5426 {
5427 /*
5428 Compute bezier points.
5429 */
5430 do
5431 {
5432 points[0]=point;
5433 for (i=1; i < 3; i++)
5434 {
5435 GetMagickToken(p,&p,token);
5436 if (*token == ',')
5437 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005438 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005439 GetMagickToken(p,&p,token);
5440 if (*token == ',')
5441 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005442 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005443 if (*p == ',')
5444 p++;
5445 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
5446 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
5447 points[i]=end;
5448 }
5449 for (i=0; i < 3; i++)
5450 (q+i)->point=points[i];
5451 TraceBezier(q,3);
5452 q+=q->coordinates;
5453 point=end;
5454 } while (IsPoint(p) != MagickFalse);
5455 break;
5456 }
5457 case 's':
5458 case 'S':
5459 {
5460 /*
5461 Compute bezier points.
5462 */
5463 do
5464 {
5465 points[0]=points[3];
5466 points[1].x=2.0*points[3].x-points[2].x;
5467 points[1].y=2.0*points[3].y-points[2].y;
5468 for (i=2; i < 4; i++)
5469 {
5470 GetMagickToken(p,&p,token);
5471 if (*token == ',')
5472 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005473 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005474 GetMagickToken(p,&p,token);
5475 if (*token == ',')
5476 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005477 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005478 if (*p == ',')
5479 p++;
5480 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
5481 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
5482 points[i]=end;
5483 }
5484 if (strchr("CcSs",last_attribute) == (char *) NULL)
5485 {
5486 points[0]=points[2];
5487 points[1]=points[3];
5488 }
5489 for (i=0; i < 4; i++)
5490 (q+i)->point=points[i];
5491 TraceBezier(q,4);
5492 q+=q->coordinates;
5493 point=end;
5494 } while (IsPoint(p) != MagickFalse);
5495 break;
5496 }
5497 case 't':
5498 case 'T':
5499 {
5500 /*
5501 Compute bezier points.
5502 */
5503 do
5504 {
5505 points[0]=points[2];
5506 points[1].x=2.0*points[2].x-points[1].x;
5507 points[1].y=2.0*points[2].y-points[1].y;
5508 for (i=2; i < 3; i++)
5509 {
5510 GetMagickToken(p,&p,token);
5511 if (*token == ',')
5512 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005513 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005514 GetMagickToken(p,&p,token);
5515 if (*token == ',')
5516 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005517 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005518 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
5519 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
5520 points[i]=end;
5521 }
5522 if (strchr("QqTt",last_attribute) == (char *) NULL)
5523 {
5524 points[0]=points[2];
5525 points[1]=points[3];
5526 }
5527 for (i=0; i < 3; i++)
5528 (q+i)->point=points[i];
5529 TraceBezier(q,3);
5530 q+=q->coordinates;
5531 point=end;
5532 } while (IsPoint(p) != MagickFalse);
5533 break;
5534 }
5535 case 'v':
5536 case 'V':
5537 {
5538 do
5539 {
5540 GetMagickToken(p,&p,token);
5541 if (*token == ',')
5542 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00005543 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005544 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
5545 TracePoint(q,point);
5546 q+=q->coordinates;
5547 } while (IsPoint(p) != MagickFalse);
5548 break;
5549 }
5550 case 'z':
5551 case 'Z':
5552 {
5553 point=start;
5554 TracePoint(q,point);
5555 q+=q->coordinates;
cristybb503372010-05-27 20:51:26 +00005556 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005557 number_coordinates+=primitive_info->coordinates;
5558 primitive_info=q;
5559 z_count++;
5560 break;
5561 }
5562 default:
5563 {
5564 if (isalpha((int) ((unsigned char) attribute)) != 0)
cristy1e604812011-05-19 18:07:50 +00005565 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n",
5566 attribute);
cristy3ed852e2009-09-05 21:47:34 +00005567 break;
5568 }
5569 }
5570 }
cristybb503372010-05-27 20:51:26 +00005571 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005572 number_coordinates+=primitive_info->coordinates;
cristybb503372010-05-27 20:51:26 +00005573 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005574 {
5575 q--;
5576 q->primitive=primitive_type;
5577 if (z_count > 1)
5578 q->method=FillToBorderMethod;
5579 }
5580 q=primitive_info;
5581 return(number_coordinates);
5582}
5583
5584static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
5585 const PointInfo end)
5586{
5587 PointInfo
5588 point;
5589
5590 register PrimitiveInfo
5591 *p;
5592
cristybb503372010-05-27 20:51:26 +00005593 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005594 i;
5595
5596 p=primitive_info;
5597 TracePoint(p,start);
5598 p+=p->coordinates;
5599 point.x=start.x;
5600 point.y=end.y;
5601 TracePoint(p,point);
5602 p+=p->coordinates;
5603 TracePoint(p,end);
5604 p+=p->coordinates;
5605 point.x=end.x;
5606 point.y=start.y;
5607 TracePoint(p,point);
5608 p+=p->coordinates;
5609 TracePoint(p,start);
5610 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005611 primitive_info->coordinates=(size_t) (p-primitive_info);
5612 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005613 {
5614 p->primitive=primitive_info->primitive;
5615 p--;
5616 }
5617}
5618
5619static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
5620 const PointInfo start,const PointInfo end,PointInfo arc)
5621{
5622 PointInfo
5623 degrees,
5624 offset,
5625 point;
5626
5627 register PrimitiveInfo
5628 *p;
5629
cristybb503372010-05-27 20:51:26 +00005630 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005631 i;
5632
5633 p=primitive_info;
5634 offset.x=fabs(end.x-start.x);
5635 offset.y=fabs(end.y-start.y);
5636 if (arc.x > (0.5*offset.x))
5637 arc.x=0.5*offset.x;
5638 if (arc.y > (0.5*offset.y))
5639 arc.y=0.5*offset.y;
5640 point.x=start.x+offset.x-arc.x;
5641 point.y=start.y+arc.y;
5642 degrees.x=270.0;
5643 degrees.y=360.0;
5644 TraceEllipse(p,point,arc,degrees);
5645 p+=p->coordinates;
5646 point.x=start.x+offset.x-arc.x;
5647 point.y=start.y+offset.y-arc.y;
5648 degrees.x=0.0;
5649 degrees.y=90.0;
5650 TraceEllipse(p,point,arc,degrees);
5651 p+=p->coordinates;
5652 point.x=start.x+arc.x;
5653 point.y=start.y+offset.y-arc.y;
5654 degrees.x=90.0;
5655 degrees.y=180.0;
5656 TraceEllipse(p,point,arc,degrees);
5657 p+=p->coordinates;
5658 point.x=start.x+arc.x;
5659 point.y=start.y+arc.y;
5660 degrees.x=180.0;
5661 degrees.y=270.0;
5662 TraceEllipse(p,point,arc,degrees);
5663 p+=p->coordinates;
5664 TracePoint(p,primitive_info->point);
5665 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005666 primitive_info->coordinates=(size_t) (p-primitive_info);
5667 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005668 {
5669 p->primitive=primitive_info->primitive;
5670 p--;
5671 }
5672}
5673
5674static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005675 const size_t number_vertices,const MagickRealType offset)
cristy3ed852e2009-09-05 21:47:34 +00005676{
5677 MagickRealType
5678 distance;
5679
cristy3ed852e2009-09-05 21:47:34 +00005680 register MagickRealType
5681 dx,
5682 dy;
5683
cristybb503372010-05-27 20:51:26 +00005684 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005685 i;
5686
cristy826a5472010-08-31 23:21:38 +00005687 ssize_t
5688 j;
5689
cristy3ed852e2009-09-05 21:47:34 +00005690 dx=0.0;
5691 dy=0.0;
cristybb503372010-05-27 20:51:26 +00005692 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005693 {
5694 dx=primitive_info[0].point.x-primitive_info[i].point.x;
5695 dy=primitive_info[0].point.y-primitive_info[i].point.y;
5696 if ((fabs((double) dx) >= MagickEpsilon) ||
5697 (fabs((double) dy) >= MagickEpsilon))
5698 break;
5699 }
cristybb503372010-05-27 20:51:26 +00005700 if (i == (ssize_t) number_vertices)
5701 i=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005702 distance=hypot((double) dx,(double) dy);
5703 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
5704 dx*(distance+offset)/distance);
5705 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
5706 dy*(distance+offset)/distance);
cristybb503372010-05-27 20:51:26 +00005707 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00005708 {
5709 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
5710 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
5711 if ((fabs((double) dx) >= MagickEpsilon) ||
5712 (fabs((double) dy) >= MagickEpsilon))
5713 break;
5714 }
5715 distance=hypot((double) dx,(double) dy);
5716 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
5717 dx*(distance+offset)/distance);
5718 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
5719 dy*(distance+offset)/distance);
5720}
5721
5722static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
5723 const PrimitiveInfo *primitive_info)
5724{
5725 typedef struct _LineSegment
5726 {
5727 double
5728 p,
5729 q;
5730 } LineSegment;
5731
5732 LineSegment
5733 dx,
5734 dy,
5735 inverse_slope,
5736 slope,
5737 theta;
5738
cristy3ed852e2009-09-05 21:47:34 +00005739 MagickBooleanType
5740 closed_path;
5741
5742 MagickRealType
5743 delta_theta,
5744 dot_product,
5745 mid,
5746 miterlimit;
5747
5748 PointInfo
5749 box_p[5],
5750 box_q[5],
5751 center,
5752 offset,
5753 *path_p,
5754 *path_q;
5755
5756 PrimitiveInfo
5757 *polygon_primitive,
5758 *stroke_polygon;
5759
cristybb503372010-05-27 20:51:26 +00005760 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005761 i;
5762
cristybb503372010-05-27 20:51:26 +00005763 size_t
cristy3ed852e2009-09-05 21:47:34 +00005764 arc_segments,
5765 max_strokes,
5766 number_vertices;
5767
cristy826a5472010-08-31 23:21:38 +00005768 ssize_t
5769 j,
5770 n,
5771 p,
5772 q;
5773
cristy3ed852e2009-09-05 21:47:34 +00005774 /*
5775 Allocate paths.
5776 */
5777 number_vertices=primitive_info->coordinates;
5778 max_strokes=2*number_vertices+6*BezierQuantum+360;
5779 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5780 sizeof(*path_p));
5781 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5782 sizeof(*path_q));
5783 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
5784 number_vertices+2UL,sizeof(*polygon_primitive));
5785 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
5786 (polygon_primitive == (PrimitiveInfo *) NULL))
5787 return((PrimitiveInfo *) NULL);
5788 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
5789 number_vertices*sizeof(*polygon_primitive));
5790 closed_path=
5791 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
5792 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
5793 MagickTrue : MagickFalse;
5794 if ((draw_info->linejoin == RoundJoin) ||
5795 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
5796 {
5797 polygon_primitive[number_vertices]=primitive_info[1];
5798 number_vertices++;
5799 }
5800 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
5801 /*
5802 Compute the slope for the first line segment, p.
5803 */
5804 dx.p=0.0;
5805 dy.p=0.0;
cristybb503372010-05-27 20:51:26 +00005806 for (n=1; n < (ssize_t) number_vertices; n++)
cristy3ed852e2009-09-05 21:47:34 +00005807 {
5808 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
5809 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
5810 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
5811 break;
5812 }
cristybb503372010-05-27 20:51:26 +00005813 if (n == (ssize_t) number_vertices)
5814 n=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005815 slope.p=0.0;
5816 inverse_slope.p=0.0;
5817 if (fabs(dx.p) <= MagickEpsilon)
5818 {
5819 if (dx.p >= 0.0)
5820 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5821 else
5822 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5823 }
5824 else
5825 if (fabs(dy.p) <= MagickEpsilon)
5826 {
5827 if (dy.p >= 0.0)
5828 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5829 else
5830 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5831 }
5832 else
5833 {
5834 slope.p=dy.p/dx.p;
5835 inverse_slope.p=(-1.0/slope.p);
5836 }
5837 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5838 miterlimit=(MagickRealType) (draw_info->miterlimit*draw_info->miterlimit*
5839 mid*mid);
5840 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
5841 TraceSquareLinecap(polygon_primitive,number_vertices,mid);
5842 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
5843 offset.y=(double) (offset.x*inverse_slope.p);
5844 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
5845 {
5846 box_p[0].x=polygon_primitive[0].point.x-offset.x;
5847 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
5848 box_p[1].x=polygon_primitive[n].point.x-offset.x;
5849 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
5850 box_q[0].x=polygon_primitive[0].point.x+offset.x;
5851 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
5852 box_q[1].x=polygon_primitive[n].point.x+offset.x;
5853 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
5854 }
5855 else
5856 {
5857 box_p[0].x=polygon_primitive[0].point.x+offset.x;
5858 box_p[0].y=polygon_primitive[0].point.y+offset.y;
5859 box_p[1].x=polygon_primitive[n].point.x+offset.x;
5860 box_p[1].y=polygon_primitive[n].point.y+offset.y;
5861 box_q[0].x=polygon_primitive[0].point.x-offset.x;
5862 box_q[0].y=polygon_primitive[0].point.y-offset.y;
5863 box_q[1].x=polygon_primitive[n].point.x-offset.x;
5864 box_q[1].y=polygon_primitive[n].point.y-offset.y;
5865 }
5866 /*
5867 Create strokes for the line join attribute: bevel, miter, round.
5868 */
5869 p=0;
5870 q=0;
5871 path_q[p++]=box_q[0];
5872 path_p[q++]=box_p[0];
cristybb503372010-05-27 20:51:26 +00005873 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005874 {
5875 /*
5876 Compute the slope for this line segment, q.
5877 */
5878 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
5879 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
5880 dot_product=dx.q*dx.q+dy.q*dy.q;
5881 if (dot_product < 0.25)
5882 continue;
5883 slope.q=0.0;
5884 inverse_slope.q=0.0;
5885 if (fabs(dx.q) < MagickEpsilon)
5886 {
5887 if (dx.q >= 0.0)
5888 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5889 else
5890 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5891 }
5892 else
5893 if (fabs(dy.q) <= MagickEpsilon)
5894 {
5895 if (dy.q >= 0.0)
5896 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5897 else
5898 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5899 }
5900 else
5901 {
5902 slope.q=dy.q/dx.q;
5903 inverse_slope.q=(-1.0/slope.q);
5904 }
5905 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
5906 offset.y=(double) (offset.x*inverse_slope.q);
5907 dot_product=dy.q*offset.x-dx.q*offset.y;
5908 if (dot_product > 0.0)
5909 {
5910 box_p[2].x=polygon_primitive[n].point.x-offset.x;
5911 box_p[2].y=polygon_primitive[n].point.y-offset.y;
5912 box_p[3].x=polygon_primitive[i].point.x-offset.x;
5913 box_p[3].y=polygon_primitive[i].point.y-offset.y;
5914 box_q[2].x=polygon_primitive[n].point.x+offset.x;
5915 box_q[2].y=polygon_primitive[n].point.y+offset.y;
5916 box_q[3].x=polygon_primitive[i].point.x+offset.x;
5917 box_q[3].y=polygon_primitive[i].point.y+offset.y;
5918 }
5919 else
5920 {
5921 box_p[2].x=polygon_primitive[n].point.x+offset.x;
5922 box_p[2].y=polygon_primitive[n].point.y+offset.y;
5923 box_p[3].x=polygon_primitive[i].point.x+offset.x;
5924 box_p[3].y=polygon_primitive[i].point.y+offset.y;
5925 box_q[2].x=polygon_primitive[n].point.x-offset.x;
5926 box_q[2].y=polygon_primitive[n].point.y-offset.y;
5927 box_q[3].x=polygon_primitive[i].point.x-offset.x;
5928 box_q[3].y=polygon_primitive[i].point.y-offset.y;
5929 }
5930 if (fabs((double) (slope.p-slope.q)) <= MagickEpsilon)
5931 {
5932 box_p[4]=box_p[1];
5933 box_q[4]=box_q[1];
5934 }
5935 else
5936 {
5937 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
5938 box_p[3].y)/(slope.p-slope.q));
5939 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
5940 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
5941 box_q[3].y)/(slope.p-slope.q));
5942 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
5943 }
cristybb503372010-05-27 20:51:26 +00005944 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00005945 {
5946 max_strokes+=6*BezierQuantum+360;
5947 path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
5948 sizeof(*path_p));
5949 path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
5950 sizeof(*path_q));
5951 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
5952 {
5953 polygon_primitive=(PrimitiveInfo *)
5954 RelinquishMagickMemory(polygon_primitive);
5955 return((PrimitiveInfo *) NULL);
5956 }
5957 }
5958 dot_product=dx.q*dy.p-dx.p*dy.q;
5959 if (dot_product <= 0.0)
5960 switch (draw_info->linejoin)
5961 {
5962 case BevelJoin:
5963 {
5964 path_q[q++]=box_q[1];
5965 path_q[q++]=box_q[2];
5966 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5967 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5968 if (dot_product <= miterlimit)
5969 path_p[p++]=box_p[4];
5970 else
5971 {
5972 path_p[p++]=box_p[1];
5973 path_p[p++]=box_p[2];
5974 }
5975 break;
5976 }
5977 case MiterJoin:
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 {
5983 path_q[q++]=box_q[4];
5984 path_p[p++]=box_p[4];
5985 }
5986 else
5987 {
5988 path_q[q++]=box_q[1];
5989 path_q[q++]=box_q[2];
5990 path_p[p++]=box_p[1];
5991 path_p[p++]=box_p[2];
5992 }
5993 break;
5994 }
5995 case RoundJoin:
5996 {
5997 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5998 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5999 if (dot_product <= miterlimit)
6000 path_p[p++]=box_p[4];
6001 else
6002 {
6003 path_p[p++]=box_p[1];
6004 path_p[p++]=box_p[2];
6005 }
6006 center=polygon_primitive[n].point;
6007 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
6008 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
6009 if (theta.q < theta.p)
6010 theta.q+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006011 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
cristy3ed852e2009-09-05 21:47:34 +00006012 (2.0*sqrt((double) (1.0/mid)))));
6013 path_q[q].x=box_q[1].x;
6014 path_q[q].y=box_q[1].y;
6015 q++;
cristybb503372010-05-27 20:51:26 +00006016 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006017 {
6018 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6019 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
6020 (theta.p+delta_theta),DegreesToRadians(360.0))));
6021 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
6022 (theta.p+delta_theta),DegreesToRadians(360.0))));
6023 q++;
6024 }
6025 path_q[q++]=box_q[2];
6026 break;
6027 }
6028 default:
6029 break;
6030 }
6031 else
6032 switch (draw_info->linejoin)
6033 {
6034 case BevelJoin:
6035 {
6036 path_p[p++]=box_p[1];
6037 path_p[p++]=box_p[2];
6038 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6039 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6040 if (dot_product <= miterlimit)
6041 path_q[q++]=box_q[4];
6042 else
6043 {
6044 path_q[q++]=box_q[1];
6045 path_q[q++]=box_q[2];
6046 }
6047 break;
6048 }
6049 case MiterJoin:
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 {
6055 path_q[q++]=box_q[4];
6056 path_p[p++]=box_p[4];
6057 }
6058 else
6059 {
6060 path_q[q++]=box_q[1];
6061 path_q[q++]=box_q[2];
6062 path_p[p++]=box_p[1];
6063 path_p[p++]=box_p[2];
6064 }
6065 break;
6066 }
6067 case RoundJoin:
6068 {
6069 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6070 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6071 if (dot_product <= miterlimit)
6072 path_q[q++]=box_q[4];
6073 else
6074 {
6075 path_q[q++]=box_q[1];
6076 path_q[q++]=box_q[2];
6077 }
6078 center=polygon_primitive[n].point;
6079 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
6080 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
6081 if (theta.p < theta.q)
6082 theta.p+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006083 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
cristy3ed852e2009-09-05 21:47:34 +00006084 (2.0*sqrt((double) (1.0/mid)))));
6085 path_p[p++]=box_p[1];
cristybb503372010-05-27 20:51:26 +00006086 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006087 {
6088 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6089 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
6090 (theta.p+delta_theta),DegreesToRadians(360.0))));
6091 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
6092 (theta.p+delta_theta),DegreesToRadians(360.0))));
6093 p++;
6094 }
6095 path_p[p++]=box_p[2];
6096 break;
6097 }
6098 default:
6099 break;
6100 }
6101 slope.p=slope.q;
6102 inverse_slope.p=inverse_slope.q;
6103 box_p[0]=box_p[2];
6104 box_p[1]=box_p[3];
6105 box_q[0]=box_q[2];
6106 box_q[1]=box_q[3];
6107 dx.p=dx.q;
6108 dy.p=dy.q;
6109 n=i;
6110 }
6111 path_p[p++]=box_p[1];
6112 path_q[q++]=box_q[1];
6113 /*
6114 Trace stroked polygon.
6115 */
6116 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
6117 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
6118 if (stroke_polygon != (PrimitiveInfo *) NULL)
6119 {
cristybb503372010-05-27 20:51:26 +00006120 for (i=0; i < (ssize_t) p; i++)
cristy3ed852e2009-09-05 21:47:34 +00006121 {
6122 stroke_polygon[i]=polygon_primitive[0];
6123 stroke_polygon[i].point=path_p[i];
6124 }
6125 if (closed_path != MagickFalse)
6126 {
6127 stroke_polygon[i]=polygon_primitive[0];
6128 stroke_polygon[i].point=stroke_polygon[0].point;
6129 i++;
6130 }
cristybb503372010-05-27 20:51:26 +00006131 for ( ; i < (ssize_t) (p+q+closed_path); i++)
cristy3ed852e2009-09-05 21:47:34 +00006132 {
6133 stroke_polygon[i]=polygon_primitive[0];
6134 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
6135 }
6136 if (closed_path != MagickFalse)
6137 {
6138 stroke_polygon[i]=polygon_primitive[0];
6139 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
6140 i++;
6141 }
6142 stroke_polygon[i]=polygon_primitive[0];
6143 stroke_polygon[i].point=stroke_polygon[0].point;
6144 i++;
6145 stroke_polygon[i].primitive=UndefinedPrimitive;
cristybb503372010-05-27 20:51:26 +00006146 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
cristy3ed852e2009-09-05 21:47:34 +00006147 }
6148 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
6149 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
6150 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
6151 return(stroke_polygon);
6152}