blob: bd581689517b4c99833b9793612b633147c6b969 [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% %
cristy45ef08f2012-12-07 13:13:34 +000021% Copyright 1999-2013 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"
cristy9b7a4fc2012-04-08 22:26:56 +000055#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000056#include "MagickCore/composite.h"
57#include "MagickCore/composite-private.h"
58#include "MagickCore/constitute.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/draw-private.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/gem.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image-private.h"
67#include "MagickCore/list.h"
68#include "MagickCore/log.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel-accessor.h"
cristy35f15302012-06-07 14:59:02 +000074#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000075#include "MagickCore/property.h"
76#include "MagickCore/resample.h"
77#include "MagickCore/resample-private.h"
cristyac245f82012-05-05 17:13:57 +000078#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/string_.h"
80#include "MagickCore/string-private.h"
81#include "MagickCore/thread-private.h"
82#include "MagickCore/token.h"
83#include "MagickCore/transform.h"
84#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000085
86/*
87 Define declarations.
88*/
89#define BezierQuantum 200
90
91/*
92 Typedef declarations.
93*/
94typedef struct _EdgeInfo
95{
96 SegmentInfo
97 bounds;
98
cristya19f1d72012-08-07 18:24:38 +000099 double
cristy3ed852e2009-09-05 21:47:34 +0000100 scanline;
101
102 PointInfo
103 *points;
104
cristybb503372010-05-27 20:51:26 +0000105 size_t
cristy3ed852e2009-09-05 21:47:34 +0000106 number_points;
107
cristybb503372010-05-27 20:51:26 +0000108 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000109 direction;
110
111 MagickBooleanType
112 ghostline;
113
cristybb503372010-05-27 20:51:26 +0000114 size_t
cristy3ed852e2009-09-05 21:47:34 +0000115 highwater;
116} EdgeInfo;
117
118typedef struct _ElementInfo
119{
cristya19f1d72012-08-07 18:24:38 +0000120 double
cristy3ed852e2009-09-05 21:47:34 +0000121 cx,
122 cy,
123 major,
124 minor,
125 angle;
126} ElementInfo;
127
128typedef struct _PolygonInfo
129{
130 EdgeInfo
131 *edges;
132
cristybb503372010-05-27 20:51:26 +0000133 size_t
cristy3ed852e2009-09-05 21:47:34 +0000134 number_edges;
135} PolygonInfo;
136
137typedef enum
138{
139 MoveToCode,
140 OpenCode,
141 GhostlineCode,
142 LineToCode,
143 EndCode
144} PathInfoCode;
145
146typedef struct _PathInfo
147{
148 PointInfo
149 point;
150
151 PathInfoCode
152 code;
153} PathInfo;
154
155/*
156 Forward declarations.
157*/
158static MagickBooleanType
cristy947cb4c2011-10-20 18:41:46 +0000159 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
160 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000161
162static PrimitiveInfo
163 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
164
cristybb503372010-05-27 20:51:26 +0000165static size_t
cristy3ed852e2009-09-05 21:47:34 +0000166 TracePath(PrimitiveInfo *,const char *);
167
168static void
169 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
170 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo,
cristya19f1d72012-08-07 18:24:38 +0000171 const double,const MagickBooleanType,const MagickBooleanType),
cristybb503372010-05-27 20:51:26 +0000172 TraceBezier(PrimitiveInfo *,const size_t),
cristy3ed852e2009-09-05 21:47:34 +0000173 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo),
174 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
175 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
176 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
177 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo,
178 PointInfo),
cristya19f1d72012-08-07 18:24:38 +0000179 TraceSquareLinecap(PrimitiveInfo *,const size_t,const double);
cristy3ed852e2009-09-05 21:47:34 +0000180
181/*
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183% %
184% %
185% %
186% A c q u i r e D r a w I n f o %
187% %
188% %
189% %
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191%
192% AcquireDrawInfo() returns a DrawInfo structure properly initialized.
193%
194% The format of the AcquireDrawInfo method is:
195%
196% DrawInfo *AcquireDrawInfo(void)
197%
198*/
199MagickExport DrawInfo *AcquireDrawInfo(void)
200{
201 DrawInfo
202 *draw_info;
203
cristy73bd4a52010-10-05 11:24:23 +0000204 draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +0000205 if (draw_info == (DrawInfo *) NULL)
206 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
207 GetDrawInfo((ImageInfo *) NULL,draw_info);
208 return(draw_info);
209}
210
211/*
212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213% %
214% %
215% %
216% C l o n e D r a w I n f o %
217% %
218% %
219% %
220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221%
anthony36fe1752011-09-29 11:28:37 +0000222% CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
cristy430522c2012-09-03 00:07:16 +0000223% is specified, a new DrawInfo structure is created initialized to default
224% values.
cristy3ed852e2009-09-05 21:47:34 +0000225%
226% The format of the CloneDrawInfo method is:
227%
228% DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
229% const DrawInfo *draw_info)
230%
231% A description of each parameter follows:
232%
233% o image_info: the image info.
234%
235% o draw_info: the draw info.
236%
237*/
238MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
239 const DrawInfo *draw_info)
240{
241 DrawInfo
242 *clone_info;
243
cristy947cb4c2011-10-20 18:41:46 +0000244 ExceptionInfo
245 *exception;
246
cristy73bd4a52010-10-05 11:24:23 +0000247 clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info));
cristy3ed852e2009-09-05 21:47:34 +0000248 if (clone_info == (DrawInfo *) NULL)
249 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
250 GetDrawInfo(image_info,clone_info);
251 if (draw_info == (DrawInfo *) NULL)
252 return(clone_info);
cristy947cb4c2011-10-20 18:41:46 +0000253 exception=AcquireExceptionInfo();
anthony42f6c202011-10-23 10:28:55 +0000254 (void) CloneString(&clone_info->primitive,draw_info->primitive);
255 (void) CloneString(&clone_info->geometry,draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000256 clone_info->viewbox=draw_info->viewbox;
257 clone_info->affine=draw_info->affine;
258 clone_info->gravity=draw_info->gravity;
259 clone_info->fill=draw_info->fill;
260 clone_info->stroke=draw_info->stroke;
261 clone_info->stroke_width=draw_info->stroke_width;
262 if (draw_info->fill_pattern != (Image *) NULL)
263 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
cristy947cb4c2011-10-20 18:41:46 +0000264 exception);
cristy3ed852e2009-09-05 21:47:34 +0000265 if (draw_info->stroke_pattern != (Image *) NULL)
266 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
cristy947cb4c2011-10-20 18:41:46 +0000267 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000268 clone_info->stroke_antialias=draw_info->stroke_antialias;
269 clone_info->text_antialias=draw_info->text_antialias;
270 clone_info->fill_rule=draw_info->fill_rule;
271 clone_info->linecap=draw_info->linecap;
272 clone_info->linejoin=draw_info->linejoin;
273 clone_info->miterlimit=draw_info->miterlimit;
274 clone_info->dash_offset=draw_info->dash_offset;
275 clone_info->decorate=draw_info->decorate;
276 clone_info->compose=draw_info->compose;
anthony42f6c202011-10-23 10:28:55 +0000277 (void) CloneString(&clone_info->text,draw_info->text);
278 (void) CloneString(&clone_info->font,draw_info->font);
279 (void) CloneString(&clone_info->metrics,draw_info->metrics);
280 (void) CloneString(&clone_info->family,draw_info->family);
cristy3ed852e2009-09-05 21:47:34 +0000281 clone_info->style=draw_info->style;
282 clone_info->stretch=draw_info->stretch;
283 clone_info->weight=draw_info->weight;
anthony42f6c202011-10-23 10:28:55 +0000284 (void) CloneString(&clone_info->encoding,draw_info->encoding);
cristy3ed852e2009-09-05 21:47:34 +0000285 clone_info->pointsize=draw_info->pointsize;
286 clone_info->kerning=draw_info->kerning;
cristyb32b90a2009-09-07 21:45:48 +0000287 clone_info->interline_spacing=draw_info->interline_spacing;
cristy3ed852e2009-09-05 21:47:34 +0000288 clone_info->interword_spacing=draw_info->interword_spacing;
cristyc9b12952010-03-28 01:12:28 +0000289 clone_info->direction=draw_info->direction;
anthony42f6c202011-10-23 10:28:55 +0000290 (void) CloneString(&clone_info->density,draw_info->density);
cristy3ed852e2009-09-05 21:47:34 +0000291 clone_info->align=draw_info->align;
292 clone_info->undercolor=draw_info->undercolor;
293 clone_info->border_color=draw_info->border_color;
anthony42f6c202011-10-23 10:28:55 +0000294 (void) CloneString(&clone_info->server_name,draw_info->server_name);
cristy3ed852e2009-09-05 21:47:34 +0000295 if (draw_info->dash_pattern != (double *) NULL)
296 {
cristybb503372010-05-27 20:51:26 +0000297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000298 x;
299
300 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
301 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
302 sizeof(*clone_info->dash_pattern));
303 if (clone_info->dash_pattern == (double *) NULL)
304 ThrowFatalException(ResourceLimitFatalError,
305 "UnableToAllocateDashPattern");
306 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
307 (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
308 }
309 clone_info->gradient=draw_info->gradient;
310 if (draw_info->gradient.stops != (StopInfo *) NULL)
311 {
cristybb503372010-05-27 20:51:26 +0000312 size_t
cristy3ed852e2009-09-05 21:47:34 +0000313 number_stops;
314
315 number_stops=clone_info->gradient.number_stops;
316 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
317 number_stops,sizeof(*clone_info->gradient.stops));
318 if (clone_info->gradient.stops == (StopInfo *) NULL)
319 ThrowFatalException(ResourceLimitFatalError,
320 "UnableToAllocateDashPattern");
321 (void) CopyMagickMemory(clone_info->gradient.stops,
322 draw_info->gradient.stops,(size_t) number_stops*
323 sizeof(*clone_info->gradient.stops));
324 }
anthony42f6c202011-10-23 10:28:55 +0000325 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
cristy3ed852e2009-09-05 21:47:34 +0000326 clone_info->bounds=draw_info->bounds;
327 clone_info->clip_units=draw_info->clip_units;
328 clone_info->render=draw_info->render;
cristy4c08aed2011-07-01 19:47:50 +0000329 clone_info->alpha=draw_info->alpha;
cristy3ed852e2009-09-05 21:47:34 +0000330 clone_info->element_reference=draw_info->element_reference;
331 clone_info->debug=IsEventLogging();
cristy947cb4c2011-10-20 18:41:46 +0000332 exception=DestroyExceptionInfo(exception);
cristy3ed852e2009-09-05 21:47:34 +0000333 return(clone_info);
334}
335
336/*
337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338% %
339% %
340% %
341+ C o n v e r t P a t h T o P o l y g o n %
342% %
343% %
344% %
345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346%
347% ConvertPathToPolygon() converts a path to the more efficient sorted
348% rendering form.
349%
350% The format of the ConvertPathToPolygon method is:
351%
352% PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
353% const PathInfo *path_info)
354%
355% A description of each parameter follows:
356%
357% o Method ConvertPathToPolygon returns the path in a more efficient sorted
358% rendering form of type PolygonInfo.
359%
360% o draw_info: Specifies a pointer to an DrawInfo structure.
361%
362% o path_info: Specifies a pointer to an PathInfo structure.
363%
364%
365*/
366
367#if defined(__cplusplus) || defined(c_plusplus)
368extern "C" {
369#endif
370
371static int CompareEdges(const void *x,const void *y)
372{
373 register const EdgeInfo
374 *p,
375 *q;
376
377 /*
378 Compare two edges.
379 */
380 p=(const EdgeInfo *) x;
381 q=(const EdgeInfo *) y;
cristy53aed702012-07-08 00:21:25 +0000382 if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
cristy3ed852e2009-09-05 21:47:34 +0000383 return(1);
cristy53aed702012-07-08 00:21:25 +0000384 if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
cristy3ed852e2009-09-05 21:47:34 +0000385 return(-1);
cristy53aed702012-07-08 00:21:25 +0000386 if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
cristy3ed852e2009-09-05 21:47:34 +0000387 return(1);
cristy53aed702012-07-08 00:21:25 +0000388 if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
cristy3ed852e2009-09-05 21:47:34 +0000389 return(-1);
390 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
391 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
392 return(1);
393 return(-1);
394}
395
396#if defined(__cplusplus) || defined(c_plusplus)
397}
398#endif
399
400static void LogPolygonInfo(const PolygonInfo *polygon_info)
401{
402 register EdgeInfo
403 *p;
404
cristybb503372010-05-27 20:51:26 +0000405 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 i,
407 j;
408
409 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
410 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +0000411 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000412 {
cristye8c25f92010-06-03 00:53:06 +0000413 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
414 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000415 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
416 p->direction != MagickFalse ? "down" : "up");
417 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
418 p->ghostline != MagickFalse ? "transparent" : "opaque");
419 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000420 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000421 p->bounds.x2,p->bounds.y2);
cristybb503372010-05-27 20:51:26 +0000422 for (j=0; j < (ssize_t) p->number_points; j++)
cristy14388de2011-05-15 14:57:16 +0000423 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g",
cristy3ed852e2009-09-05 21:47:34 +0000424 p->points[j].x,p->points[j].y);
425 p++;
426 }
427 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
428}
429
cristybb503372010-05-27 20:51:26 +0000430static void ReversePoints(PointInfo *points,const size_t number_points)
cristy3ed852e2009-09-05 21:47:34 +0000431{
432 PointInfo
433 point;
434
cristybb503372010-05-27 20:51:26 +0000435 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000436 i;
437
cristybb503372010-05-27 20:51:26 +0000438 for (i=0; i < (ssize_t) (number_points >> 1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000439 {
440 point=points[i];
441 points[i]=points[number_points-(i+1)];
442 points[number_points-(i+1)]=point;
443 }
444}
445
446static PolygonInfo *ConvertPathToPolygon(
447 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
448{
cristycee97112010-05-28 00:44:52 +0000449 long
cristy3ed852e2009-09-05 21:47:34 +0000450 direction,
451 next_direction;
452
453 PointInfo
454 point,
455 *points;
456
457 PolygonInfo
458 *polygon_info;
459
460 SegmentInfo
461 bounds;
462
cristybb503372010-05-27 20:51:26 +0000463 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000464 i,
465 n;
466
467 MagickBooleanType
468 ghostline;
469
cristybb503372010-05-27 20:51:26 +0000470 size_t
cristy3ed852e2009-09-05 21:47:34 +0000471 edge,
472 number_edges,
473 number_points;
474
475 /*
476 Convert a path to the more efficient sorted rendering form.
477 */
cristy73bd4a52010-10-05 11:24:23 +0000478 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +0000479 if (polygon_info == (PolygonInfo *) NULL)
480 return((PolygonInfo *) NULL);
481 number_edges=16;
482 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
483 sizeof(*polygon_info->edges));
484 if (polygon_info->edges == (EdgeInfo *) NULL)
485 return((PolygonInfo *) NULL);
486 direction=0;
487 edge=0;
488 ghostline=MagickFalse;
489 n=0;
490 number_points=0;
491 points=(PointInfo *) NULL;
492 (void) ResetMagickMemory(&point,0,sizeof(point));
493 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
494 for (i=0; path_info[i].code != EndCode; i++)
495 {
496 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
497 (path_info[i].code == GhostlineCode))
498 {
499 /*
500 Move to.
501 */
502 if ((points != (PointInfo *) NULL) && (n >= 2))
503 {
504 if (edge == number_edges)
505 {
506 number_edges<<=1;
507 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
508 polygon_info->edges,(size_t) number_edges,
509 sizeof(*polygon_info->edges));
510 if (polygon_info->edges == (EdgeInfo *) NULL)
511 return((PolygonInfo *) NULL);
512 }
cristybb503372010-05-27 20:51:26 +0000513 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000514 polygon_info->edges[edge].scanline=(-1.0);
515 polygon_info->edges[edge].highwater=0;
516 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000517 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000518 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000519 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000520 polygon_info->edges[edge].points=points;
521 polygon_info->edges[edge].bounds=bounds;
522 polygon_info->edges[edge].bounds.y1=points[0].y;
523 polygon_info->edges[edge].bounds.y2=points[n-1].y;
524 points=(PointInfo *) NULL;
525 ghostline=MagickFalse;
526 edge++;
527 }
528 if (points == (PointInfo *) NULL)
529 {
530 number_points=16;
531 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
532 sizeof(*points));
533 if (points == (PointInfo *) NULL)
534 return((PolygonInfo *) NULL);
535 }
536 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
537 point=path_info[i].point;
538 points[0]=point;
539 bounds.x1=point.x;
540 bounds.x2=point.x;
541 direction=0;
542 n=1;
543 continue;
544 }
545 /*
546 Line to.
547 */
548 next_direction=((path_info[i].point.y > point.y) ||
549 ((path_info[i].point.y == point.y) &&
550 (path_info[i].point.x > point.x))) ? 1 : -1;
551 if ((direction != 0) && (direction != next_direction))
552 {
553 /*
554 New edge.
555 */
556 point=points[n-1];
557 if (edge == number_edges)
558 {
559 number_edges<<=1;
560 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
561 polygon_info->edges,(size_t) number_edges,
562 sizeof(*polygon_info->edges));
563 if (polygon_info->edges == (EdgeInfo *) NULL)
564 return((PolygonInfo *) NULL);
565 }
cristybb503372010-05-27 20:51:26 +0000566 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000567 polygon_info->edges[edge].scanline=(-1.0);
568 polygon_info->edges[edge].highwater=0;
569 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000570 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000571 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000572 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000573 polygon_info->edges[edge].points=points;
574 polygon_info->edges[edge].bounds=bounds;
575 polygon_info->edges[edge].bounds.y1=points[0].y;
576 polygon_info->edges[edge].bounds.y2=points[n-1].y;
577 number_points=16;
578 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
579 sizeof(*points));
580 if (points == (PointInfo *) NULL)
581 return((PolygonInfo *) NULL);
582 n=1;
583 ghostline=MagickFalse;
584 points[0]=point;
585 bounds.x1=point.x;
586 bounds.x2=point.x;
587 edge++;
588 }
589 direction=next_direction;
590 if (points == (PointInfo *) NULL)
591 continue;
cristybb503372010-05-27 20:51:26 +0000592 if (n == (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +0000593 {
594 number_points<<=1;
595 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
596 sizeof(*points));
597 if (points == (PointInfo *) NULL)
598 return((PolygonInfo *) NULL);
599 }
600 point=path_info[i].point;
601 points[n]=point;
602 if (point.x < bounds.x1)
603 bounds.x1=point.x;
604 if (point.x > bounds.x2)
605 bounds.x2=point.x;
606 n++;
607 }
608 if (points != (PointInfo *) NULL)
609 {
610 if (n < 2)
611 points=(PointInfo *) RelinquishMagickMemory(points);
612 else
613 {
614 if (edge == number_edges)
615 {
616 number_edges<<=1;
617 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
618 polygon_info->edges,(size_t) number_edges,
619 sizeof(*polygon_info->edges));
620 if (polygon_info->edges == (EdgeInfo *) NULL)
621 return((PolygonInfo *) NULL);
622 }
cristybb503372010-05-27 20:51:26 +0000623 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000624 polygon_info->edges[edge].scanline=(-1.0);
625 polygon_info->edges[edge].highwater=0;
626 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000627 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000628 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000629 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000630 polygon_info->edges[edge].points=points;
631 polygon_info->edges[edge].bounds=bounds;
632 polygon_info->edges[edge].bounds.y1=points[0].y;
633 polygon_info->edges[edge].bounds.y2=points[n-1].y;
634 ghostline=MagickFalse;
635 edge++;
636 }
637 }
638 polygon_info->number_edges=edge;
639 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
640 sizeof(*polygon_info->edges),CompareEdges);
641 if (IsEventLogging() != MagickFalse)
642 LogPolygonInfo(polygon_info);
643 return(polygon_info);
644}
645
646/*
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648% %
649% %
650% %
651+ C o n v e r t P r i m i t i v e T o P a t h %
652% %
653% %
654% %
655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656%
657% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
658% path structure.
659%
660% The format of the ConvertPrimitiveToPath method is:
661%
662% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
663% const PrimitiveInfo *primitive_info)
664%
665% A description of each parameter follows:
666%
667% o Method ConvertPrimitiveToPath returns a vector path structure of type
668% PathInfo.
669%
670% o draw_info: a structure of type DrawInfo.
671%
672% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
673%
674%
675*/
676
677static void LogPathInfo(const PathInfo *path_info)
678{
679 register const PathInfo
680 *p;
681
682 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
683 for (p=path_info; p->code != EndCode; p++)
684 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000685 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
cristy3ed852e2009-09-05 21:47:34 +0000686 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
687 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
688 "?");
689 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
690}
691
692static PathInfo *ConvertPrimitiveToPath(
693 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
694{
cristy3ed852e2009-09-05 21:47:34 +0000695 PathInfo
696 *path_info;
697
698 PathInfoCode
699 code;
700
701 PointInfo
702 p,
703 q;
704
cristybb503372010-05-27 20:51:26 +0000705 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000706 i,
707 n;
708
cristy826a5472010-08-31 23:21:38 +0000709 ssize_t
710 coordinates,
711 start;
712
cristy3ed852e2009-09-05 21:47:34 +0000713 /*
714 Converts a PrimitiveInfo structure into a vector path structure.
715 */
716 switch (primitive_info->primitive)
717 {
718 case PointPrimitive:
719 case ColorPrimitive:
720 case MattePrimitive:
721 case TextPrimitive:
722 case ImagePrimitive:
723 return((PathInfo *) NULL);
724 default:
725 break;
726 }
727 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
728 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
729 sizeof(*path_info));
730 if (path_info == (PathInfo *) NULL)
731 return((PathInfo *) NULL);
732 coordinates=0;
733 n=0;
734 p.x=(-1.0);
735 p.y=(-1.0);
736 q.x=(-1.0);
737 q.y=(-1.0);
738 start=0;
739 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
740 {
741 code=LineToCode;
742 if (coordinates <= 0)
743 {
cristybb503372010-05-27 20:51:26 +0000744 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +0000745 p=primitive_info[i].point;
746 start=n;
747 code=MoveToCode;
748 }
749 coordinates--;
750 /*
751 Eliminate duplicate points.
752 */
cristy53aed702012-07-08 00:21:25 +0000753 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
754 (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000755 {
756 path_info[n].code=code;
757 path_info[n].point=primitive_info[i].point;
758 q=primitive_info[i].point;
759 n++;
760 }
761 if (coordinates > 0)
762 continue;
cristy53aed702012-07-08 00:21:25 +0000763 if ((fabs(p.x-primitive_info[i].point.x) < MagickEpsilon) &&
764 (fabs(p.y-primitive_info[i].point.y) < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000765 continue;
766 /*
767 Mark the p point as open if it does not match the q.
768 */
769 path_info[start].code=OpenCode;
770 path_info[n].code=GhostlineCode;
771 path_info[n].point=primitive_info[i].point;
772 n++;
773 path_info[n].code=LineToCode;
774 path_info[n].point=p;
775 n++;
776 }
777 path_info[n].code=EndCode;
778 path_info[n].point.x=0.0;
779 path_info[n].point.y=0.0;
780 if (IsEventLogging() != MagickFalse)
781 LogPathInfo(path_info);
782 return(path_info);
783}
784
785/*
786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787% %
788% %
789% %
790% D e s t r o y D r a w I n f o %
791% %
792% %
793% %
794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795%
796% DestroyDrawInfo() deallocates memory associated with an DrawInfo
797% structure.
798%
799% The format of the DestroyDrawInfo method is:
800%
801% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
802%
803% A description of each parameter follows:
804%
805% o draw_info: the draw info.
806%
807*/
808MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
809{
810 if (draw_info->debug != MagickFalse)
811 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
812 assert(draw_info != (DrawInfo *) NULL);
813 assert(draw_info->signature == MagickSignature);
814 if (draw_info->primitive != (char *) NULL)
815 draw_info->primitive=DestroyString(draw_info->primitive);
816 if (draw_info->text != (char *) NULL)
817 draw_info->text=DestroyString(draw_info->text);
818 if (draw_info->geometry != (char *) NULL)
819 draw_info->geometry=DestroyString(draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000820 if (draw_info->fill_pattern != (Image *) NULL)
821 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
822 if (draw_info->stroke_pattern != (Image *) NULL)
823 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
824 if (draw_info->font != (char *) NULL)
825 draw_info->font=DestroyString(draw_info->font);
826 if (draw_info->metrics != (char *) NULL)
827 draw_info->metrics=DestroyString(draw_info->metrics);
828 if (draw_info->family != (char *) NULL)
829 draw_info->family=DestroyString(draw_info->family);
830 if (draw_info->encoding != (char *) NULL)
831 draw_info->encoding=DestroyString(draw_info->encoding);
832 if (draw_info->density != (char *) NULL)
833 draw_info->density=DestroyString(draw_info->density);
834 if (draw_info->server_name != (char *) NULL)
835 draw_info->server_name=(char *)
836 RelinquishMagickMemory(draw_info->server_name);
837 if (draw_info->dash_pattern != (double *) NULL)
838 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
839 draw_info->dash_pattern);
840 if (draw_info->gradient.stops != (StopInfo *) NULL)
841 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
842 draw_info->gradient.stops);
843 if (draw_info->clip_mask != (char *) NULL)
844 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
845 draw_info->signature=(~MagickSignature);
846 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
847 return(draw_info);
848}
849
850/*
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852% %
853% %
854% %
855+ D e s t r o y E d g e %
856% %
857% %
858% %
859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860%
861% DestroyEdge() destroys the specified polygon edge.
862%
863% The format of the DestroyEdge method is:
864%
cristybb503372010-05-27 20:51:26 +0000865% ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
cristy3ed852e2009-09-05 21:47:34 +0000866%
867% A description of each parameter follows:
868%
869% o polygon_info: Specifies a pointer to an PolygonInfo structure.
870%
871% o edge: the polygon edge number to destroy.
872%
873*/
cristybb503372010-05-27 20:51:26 +0000874static size_t DestroyEdge(PolygonInfo *polygon_info,
875 const size_t edge)
cristy3ed852e2009-09-05 21:47:34 +0000876{
877 assert(edge < polygon_info->number_edges);
878 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
879 polygon_info->edges[edge].points);
880 polygon_info->number_edges--;
881 if (edge < polygon_info->number_edges)
882 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
883 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
884 return(polygon_info->number_edges);
885}
886
887/*
888%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
889% %
890% %
891% %
892+ D e s t r o y P o l y g o n I n f o %
893% %
894% %
895% %
896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897%
898% DestroyPolygonInfo() destroys the PolygonInfo data structure.
899%
900% The format of the DestroyPolygonInfo method is:
901%
902% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
903%
904% A description of each parameter follows:
905%
906% o polygon_info: Specifies a pointer to an PolygonInfo structure.
907%
908*/
909static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
910{
cristybb503372010-05-27 20:51:26 +0000911 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000912 i;
913
cristybb503372010-05-27 20:51:26 +0000914 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000915 polygon_info->edges[i].points=(PointInfo *)
916 RelinquishMagickMemory(polygon_info->edges[i].points);
917 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
918 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
919}
920
921/*
922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923% %
924% %
925% %
926% D r a w A f f i n e I m a g e %
927% %
928% %
929% %
930%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
931%
932% DrawAffineImage() composites the source over the destination image as
933% dictated by the affine transform.
934%
935% The format of the DrawAffineImage method is:
936%
937% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
cristy947cb4c2011-10-20 18:41:46 +0000938% const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000939%
940% A description of each parameter follows:
941%
942% o image: the image.
943%
944% o source: the source image.
945%
946% o affine: the affine transform.
947%
cristy947cb4c2011-10-20 18:41:46 +0000948% o exception: return any errors or warnings in this structure.
949%
cristy3ed852e2009-09-05 21:47:34 +0000950*/
cristyefa9e8a2011-11-07 01:56:51 +0000951
cristy3ed852e2009-09-05 21:47:34 +0000952static 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;
cristy53aed702012-07-08 00:21:25 +0000973 if (affine->sx >= MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +0000974 {
975 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000976 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000977 if (x > inverse_edge.x1)
978 inverse_edge.x1=x;
979 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000980 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000981 if (x < inverse_edge.x2)
982 inverse_edge.x2=x;
983 }
984 else
cristy53aed702012-07-08 00:21:25 +0000985 if (affine->sx < -MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +0000986 {
987 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000988 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000989 if (x > inverse_edge.x1)
990 inverse_edge.x1=x;
991 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000992 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000993 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;
cristy53aed702012-07-08 00:21:25 +00001006 if (affine->rx >= MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +00001007 {
1008 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001009 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001010 if (x > inverse_edge.x1)
1011 inverse_edge.x1=x;
1012 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001013 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001014 if (x < inverse_edge.x2)
1015 inverse_edge.x2=x;
1016 }
1017 else
cristy53aed702012-07-08 00:21:25 +00001018 if (affine->rx < -MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +00001019 {
1020 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001021 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001022 if (x > inverse_edge.x1)
1023 inverse_edge.x1=x;
1024 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001025 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001026 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
cristy3e3ec3a2012-11-03 23:11:06 +00001046 determinant=PerceptibleReciprocal(affine->sx*affine->sy-affine->rx*
cristy35f15302012-06-07 14:59:02 +00001047 affine->ry);
cristy3ed852e2009-09-05 21:47:34 +00001048 inverse_affine.sx=determinant*affine->sy;
1049 inverse_affine.rx=determinant*(-affine->rx);
1050 inverse_affine.ry=determinant*(-affine->ry);
1051 inverse_affine.sy=determinant*affine->sx;
1052 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1053 inverse_affine.ry;
1054 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1055 inverse_affine.sy;
1056 return(inverse_affine);
1057}
1058
cristybb503372010-05-27 20:51:26 +00001059static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +00001060{
1061 if (x < 0)
1062 return(-x);
1063 return(x);
1064}
1065
1066static inline double MagickMax(const double x,const double y)
1067{
1068 if (x > y)
1069 return(x);
1070 return(y);
1071}
1072
1073static inline double MagickMin(const double x,const double y)
1074{
1075 if (x < y)
1076 return(x);
1077 return(y);
1078}
1079
1080MagickExport MagickBooleanType DrawAffineImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00001081 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001082{
1083 AffineMatrix
1084 inverse_affine;
1085
cristyfa112112010-01-04 17:48:07 +00001086 CacheView
1087 *image_view,
1088 *source_view;
1089
cristy3ed852e2009-09-05 21:47:34 +00001090 MagickBooleanType
1091 status;
1092
cristy4c08aed2011-07-01 19:47:50 +00001093 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001094 zero;
1095
1096 PointInfo
1097 extent[4],
1098 min,
1099 max,
1100 point;
1101
cristybb503372010-05-27 20:51:26 +00001102 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001103 i;
1104
cristy3ed852e2009-09-05 21:47:34 +00001105 SegmentInfo
1106 edge;
1107
cristy826a5472010-08-31 23:21:38 +00001108 ssize_t
cristy564a5692012-01-20 23:56:26 +00001109 start,
1110 stop,
cristy826a5472010-08-31 23:21:38 +00001111 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 */
cristy574cc262011-08-05 01:23:58 +00001153 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001154 return(MagickFalse);
1155 status=MagickTrue;
1156 edge.x1=MagickMax(min.x,0.0);
1157 edge.y1=MagickMax(min.y,0.0);
1158 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1159 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1160 inverse_affine=InverseAffineMatrix(affine);
cristy4c08aed2011-07-01 19:47:50 +00001161 GetPixelInfo(image,&zero);
cristy564a5692012-01-20 23:56:26 +00001162 start=(ssize_t) ceil(edge.y1-0.5);
cristy8071c472012-09-24 12:41:06 +00001163 stop=(ssize_t) floor(edge.y2+0.5);
cristy46ff2672012-12-14 15:32:26 +00001164 source_view=AcquireVirtualCacheView(source,exception);
1165 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001166#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00001167 #pragma omp parallel for schedule(static,4) shared(status) \
cristycb7dfcc2013-01-06 18:34:59 +00001168 magick_threads(source,image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001169#endif
cristy564a5692012-01-20 23:56:26 +00001170 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00001171 {
cristy4c08aed2011-07-01 19:47:50 +00001172 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001173 composite,
1174 pixel;
1175
1176 PointInfo
1177 point;
1178
cristybb503372010-05-27 20:51:26 +00001179 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001180 x;
1181
cristy4c08aed2011-07-01 19:47:50 +00001182 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001183 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001184
1185 SegmentInfo
1186 inverse_edge;
1187
cristy826a5472010-08-31 23:21:38 +00001188 ssize_t
1189 x_offset;
1190
cristy3ed852e2009-09-05 21:47:34 +00001191 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1192 if (inverse_edge.x2 < inverse_edge.x1)
1193 continue;
cristy6ebe97c2010-07-03 01:17:28 +00001194 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
cristy592aefd2013-02-03 15:41:39 +00001195 0.5),y,(size_t) (floor(inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),
1196 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001197 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001198 continue;
cristy3ed852e2009-09-05 21:47:34 +00001199 pixel=zero;
1200 composite=zero;
1201 x_offset=0;
cristy8071c472012-09-24 12:41:06 +00001202 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 +00001203 {
1204 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1205 inverse_affine.tx;
1206 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1207 inverse_affine.ty;
cristy4c08aed2011-07-01 19:47:50 +00001208 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1209 point.x,point.y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00001210 GetPixelInfoPixel(image,q,&composite);
cristy4c08aed2011-07-01 19:47:50 +00001211 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1212 &composite);
cristy803640d2011-11-17 02:11:32 +00001213 SetPixelInfoPixel(image,&composite,q);
cristy3ed852e2009-09-05 21:47:34 +00001214 x_offset++;
cristyed231572011-07-14 02:18:59 +00001215 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001216 }
1217 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1218 status=MagickFalse;
1219 }
cristy3ed852e2009-09-05 21:47:34 +00001220 source_view=DestroyCacheView(source_view);
1221 image_view=DestroyCacheView(image_view);
1222 return(status);
1223}
1224
1225/*
1226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1227% %
1228% %
1229% %
1230+ D r a w B o u n d i n g R e c t a n g l e s %
1231% %
1232% %
1233% %
1234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235%
1236% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1237% is only useful for developers debugging the rendering algorithm.
1238%
1239% The format of the DrawBoundingRectangles method is:
1240%
1241% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001242% PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001243%
1244% A description of each parameter follows:
1245%
1246% o image: the image.
1247%
1248% o draw_info: the draw info.
1249%
1250% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1251%
cristy947cb4c2011-10-20 18:41:46 +00001252% o exception: return any errors or warnings in this structure.
1253%
cristy3ed852e2009-09-05 21:47:34 +00001254*/
1255static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001256 const PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001257{
1258 DrawInfo
1259 *clone_info;
1260
cristya19f1d72012-08-07 18:24:38 +00001261 double
cristy3ed852e2009-09-05 21:47:34 +00001262 mid;
1263
1264 PointInfo
1265 end,
1266 resolution,
1267 start;
1268
1269 PrimitiveInfo
1270 primitive_info[6];
1271
cristybb503372010-05-27 20:51:26 +00001272 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001273 i;
1274
1275 SegmentInfo
1276 bounds;
1277
cristy826a5472010-08-31 23:21:38 +00001278 ssize_t
1279 coordinates;
1280
cristy3ed852e2009-09-05 21:47:34 +00001281 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
cristyfad60c92012-01-19 18:32:39 +00001282 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill,
cristy947cb4c2011-10-20 18:41:46 +00001283 exception);
cristy3ed852e2009-09-05 21:47:34 +00001284 resolution.x=DefaultResolution;
1285 resolution.y=DefaultResolution;
1286 if (clone_info->density != (char *) NULL)
1287 {
1288 GeometryInfo
1289 geometry_info;
1290
1291 MagickStatusType
1292 flags;
1293
1294 flags=ParseGeometry(clone_info->density,&geometry_info);
1295 resolution.x=geometry_info.rho;
1296 resolution.y=geometry_info.sigma;
1297 if ((flags & SigmaValue) == MagickFalse)
1298 resolution.y=resolution.x;
1299 }
1300 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1301 clone_info->stroke_width/2.0;
1302 bounds.x1=0.0;
1303 bounds.y1=0.0;
1304 bounds.x2=0.0;
1305 bounds.y2=0.0;
1306 if (polygon_info != (PolygonInfo *) NULL)
1307 {
1308 bounds=polygon_info->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00001309 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001310 {
1311 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1312 bounds.x1=polygon_info->edges[i].bounds.x1;
1313 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1314 bounds.y1=polygon_info->edges[i].bounds.y1;
1315 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1316 bounds.x2=polygon_info->edges[i].bounds.x2;
1317 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1318 bounds.y2=polygon_info->edges[i].bounds.y2;
1319 }
1320 bounds.x1-=mid;
1321 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
cristy6c5494a2012-09-22 00:30:37 +00001322 image->columns ? (double) image->columns-1 : bounds.x1;
cristy3ed852e2009-09-05 21:47:34 +00001323 bounds.y1-=mid;
1324 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
cristy6c5494a2012-09-22 00:30:37 +00001325 image->rows ? (double) image->rows-1 : bounds.y1;
cristy3ed852e2009-09-05 21:47:34 +00001326 bounds.x2+=mid;
1327 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
cristy6c5494a2012-09-22 00:30:37 +00001328 image->columns ? (double) image->columns-1 : bounds.x2;
cristy3ed852e2009-09-05 21:47:34 +00001329 bounds.y2+=mid;
1330 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
cristy6c5494a2012-09-22 00:30:37 +00001331 image->rows ? (double) image->rows-1 : bounds.y2;
cristybb503372010-05-27 20:51:26 +00001332 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001333 {
1334 if (polygon_info->edges[i].direction != 0)
cristy9950d572011-10-01 18:22:35 +00001335 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001336 exception);
cristy3ed852e2009-09-05 21:47:34 +00001337 else
cristy9950d572011-10-01 18:22:35 +00001338 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001339 exception);
cristy3ed852e2009-09-05 21:47:34 +00001340 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1341 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1342 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1343 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1344 primitive_info[0].primitive=RectanglePrimitive;
1345 TraceRectangle(primitive_info,start,end);
1346 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001347 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001348 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001349 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001350 }
1351 }
cristy9950d572011-10-01 18:22:35 +00001352 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001353 exception);
cristy3ed852e2009-09-05 21:47:34 +00001354 start.x=(double) (bounds.x1-mid);
1355 start.y=(double) (bounds.y1-mid);
1356 end.x=(double) (bounds.x2+mid);
1357 end.y=(double) (bounds.y2+mid);
1358 primitive_info[0].primitive=RectanglePrimitive;
1359 TraceRectangle(primitive_info,start,end);
1360 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001361 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001362 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001363 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001364 clone_info=DestroyDrawInfo(clone_info);
1365}
1366
1367/*
1368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1369% %
1370% %
1371% %
1372% D r a w C l i p P a t h %
1373% %
1374% %
1375% %
1376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377%
1378% DrawClipPath() draws the clip path on the image mask.
1379%
1380% The format of the DrawClipPath method is:
1381%
1382% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00001383% const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001384%
1385% A description of each parameter follows:
1386%
1387% o image: the image.
1388%
1389% o draw_info: the draw info.
1390%
1391% o name: the name of the clip path.
1392%
cristy018f07f2011-09-04 21:15:19 +00001393% o exception: return any errors or warnings in this structure.
1394%
cristy3ed852e2009-09-05 21:47:34 +00001395*/
1396MagickExport MagickBooleanType DrawClipPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00001397 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001398{
1399 char
cristy10a6c612012-01-29 21:41:05 +00001400 filename[MaxTextExtent];
1401
1402 Image
1403 *clip_mask;
cristy3ed852e2009-09-05 21:47:34 +00001404
1405 const char
1406 *value;
1407
1408 DrawInfo
1409 *clone_info;
1410
1411 MagickStatusType
1412 status;
1413
1414 assert(image != (Image *) NULL);
1415 assert(image->signature == MagickSignature);
1416 if (image->debug != MagickFalse)
1417 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1418 assert(draw_info != (const DrawInfo *) NULL);
cristy10a6c612012-01-29 21:41:05 +00001419 (void) FormatLocaleString(filename,MaxTextExtent,"%s",name);
1420 value=GetImageArtifact(image,filename);
cristy3ed852e2009-09-05 21:47:34 +00001421 if (value == (const char *) NULL)
1422 return(MagickFalse);
cristy10a6c612012-01-29 21:41:05 +00001423 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1424 if (clip_mask == (Image *) NULL)
1425 return(MagickFalse);
cristyfad60c92012-01-19 18:32:39 +00001426 (void) QueryColorCompliance("#0000",AllCompliance,
cristy10a6c612012-01-29 21:41:05 +00001427 &clip_mask->background_color,exception);
1428 clip_mask->background_color.alpha=(Quantum) TransparentAlpha;
1429 (void) SetImageBackgroundColor(clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001430 if (image->debug != MagickFalse)
1431 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1432 draw_info->clip_mask);
1433 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1434 (void) CloneString(&clone_info->primitive,value);
cristy9950d572011-10-01 18:22:35 +00001435 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
cristyea1a8aa2011-10-20 13:24:06 +00001436 exception);
cristy3ed852e2009-09-05 21:47:34 +00001437 clone_info->clip_mask=(char *) NULL;
cristy0c901882012-01-30 15:58:59 +00001438 status=NegateImage(clip_mask,MagickFalse,exception);
cristy10a6c612012-01-29 21:41:05 +00001439 (void) SetImageMask(image,clip_mask,exception);
1440 clip_mask=DestroyImage(clip_mask);
cristy10a6c612012-01-29 21:41:05 +00001441 status=DrawImage(image,clone_info,exception);
cristy8e508182012-09-22 00:21:17 +00001442 clone_info=DestroyDrawInfo(clone_info);
cristy3ed852e2009-09-05 21:47:34 +00001443 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,
cristy947cb4c2011-10-20 18:41:46 +00001465% const PrimitiveInfo *primitive_info,Image *image,
1466% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001467%
1468% A description of each parameter follows:
1469%
1470% o draw_info: the draw info.
1471%
1472% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1473%
1474% o image: the image.
1475%
cristy947cb4c2011-10-20 18:41:46 +00001476% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001477%
1478*/
1479static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001480 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001481{
1482 DrawInfo
1483 *clone_info;
1484
cristya19f1d72012-08-07 18:24:38 +00001485 double
cristy3ed852e2009-09-05 21:47:34 +00001486 length,
1487 maximum_length,
1488 offset,
1489 scale,
1490 total_length;
1491
1492 MagickStatusType
1493 status;
1494
1495 PrimitiveInfo
1496 *dash_polygon;
1497
cristybb503372010-05-27 20:51:26 +00001498 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001499 i;
1500
cristya19f1d72012-08-07 18:24:38 +00001501 register double
cristy3ed852e2009-09-05 21:47:34 +00001502 dx,
1503 dy;
1504
cristybb503372010-05-27 20:51:26 +00001505 size_t
cristy3ed852e2009-09-05 21:47:34 +00001506 number_vertices;
1507
cristy826a5472010-08-31 23:21:38 +00001508 ssize_t
1509 j,
1510 n;
1511
cristy3ed852e2009-09-05 21:47:34 +00001512 assert(draw_info != (const DrawInfo *) NULL);
1513 if (image->debug != MagickFalse)
1514 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1515 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1516 clone_info->miterlimit=0;
1517 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
cristybb503372010-05-27 20:51:26 +00001518 number_vertices=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001519 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1520 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
1521 if (dash_polygon == (PrimitiveInfo *) NULL)
1522 return(MagickFalse);
1523 dash_polygon[0]=primitive_info[0];
1524 scale=ExpandAffine(&draw_info->affine);
1525 length=scale*(draw_info->dash_pattern[0]-0.5);
1526 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
1527 j=1;
1528 for (n=0; offset > 0.0; j=0)
1529 {
1530 if (draw_info->dash_pattern[n] <= 0.0)
1531 break;
1532 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1533 if (offset > length)
1534 {
1535 offset-=length;
1536 n++;
1537 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1538 continue;
1539 }
1540 if (offset < length)
1541 {
1542 length-=offset;
1543 offset=0.0;
1544 break;
1545 }
1546 offset=0.0;
1547 n++;
1548 }
1549 status=MagickTrue;
1550 maximum_length=0.0;
1551 total_length=0.0;
cristybb503372010-05-27 20:51:26 +00001552 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00001553 {
1554 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1555 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1556 maximum_length=hypot((double) dx,dy);
1557 if (length == 0.0)
1558 {
1559 n++;
1560 if (draw_info->dash_pattern[n] == 0.0)
1561 n=0;
1562 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1563 }
cristycbda6112012-05-27 20:57:16 +00001564 for (total_length=0.0; (total_length+length) <= maximum_length; )
cristy3ed852e2009-09-05 21:47:34 +00001565 {
1566 total_length+=length;
1567 if ((n & 0x01) != 0)
1568 {
1569 dash_polygon[0]=primitive_info[0];
1570 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1571 total_length/maximum_length);
1572 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1573 total_length/maximum_length);
1574 j=1;
1575 }
1576 else
1577 {
cristybb503372010-05-27 20:51:26 +00001578 if ((j+1) > (ssize_t) (2*number_vertices))
cristy3ed852e2009-09-05 21:47:34 +00001579 break;
1580 dash_polygon[j]=primitive_info[i-1];
1581 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1582 total_length/maximum_length);
1583 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1584 total_length/maximum_length);
1585 dash_polygon[j].coordinates=1;
1586 j++;
cristybb503372010-05-27 20:51:26 +00001587 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001588 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001589 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001590 }
1591 n++;
1592 if (draw_info->dash_pattern[n] == 0.0)
1593 n=0;
1594 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1595 }
1596 length-=(maximum_length-total_length);
1597 if ((n & 0x01) != 0)
1598 continue;
1599 dash_polygon[j]=primitive_info[i];
1600 dash_polygon[j].coordinates=1;
1601 j++;
1602 }
cristycbda6112012-05-27 20:57:16 +00001603 if ((total_length <= maximum_length) && ((n & 0x01) == 0) && (j > 1))
cristy3ed852e2009-09-05 21:47:34 +00001604 {
1605 dash_polygon[j]=primitive_info[i-1];
cristy53aed702012-07-08 00:21:25 +00001606 dash_polygon[j].point.x+=MagickEpsilon;
1607 dash_polygon[j].point.y+=MagickEpsilon;
cristy3ed852e2009-09-05 21:47:34 +00001608 dash_polygon[j].coordinates=1;
1609 j++;
cristybb503372010-05-27 20:51:26 +00001610 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001611 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001612 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001613 }
1614 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1615 clone_info=DestroyDrawInfo(clone_info);
1616 if (image->debug != MagickFalse)
1617 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1618 return(status != 0 ? MagickTrue : MagickFalse);
1619}
1620
1621/*
1622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623% %
1624% %
1625% %
1626% D r a w I m a g e %
1627% %
1628% %
1629% %
1630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631%
1632% DrawImage() draws a graphic primitive on your image. The primitive
1633% may be represented as a string or filename. Precede the filename with an
1634% "at" sign (@) and the contents of the file are drawn on the image. You
1635% can affect how text is drawn by setting one or more members of the draw
1636% info structure.
1637%
1638% The format of the DrawImage method is:
1639%
cristy018f07f2011-09-04 21:15:19 +00001640% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1641% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001642%
1643% A description of each parameter follows:
1644%
1645% o image: the image.
1646%
1647% o draw_info: the draw info.
1648%
cristy018f07f2011-09-04 21:15:19 +00001649% o exception: return any errors or warnings in this structure.
1650%
cristy3ed852e2009-09-05 21:47:34 +00001651*/
1652
1653static inline MagickBooleanType IsPoint(const char *point)
1654{
1655 char
1656 *p;
1657
1658 double
1659 value;
1660
cristydbdd0e32011-11-04 23:29:40 +00001661 value=StringToDouble(point,&p);
cristy3ed852e2009-09-05 21:47:34 +00001662 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1663}
1664
1665static inline void TracePoint(PrimitiveInfo *primitive_info,
1666 const PointInfo point)
1667{
1668 primitive_info->coordinates=1;
1669 primitive_info->point=point;
1670}
1671
cristy018f07f2011-09-04 21:15:19 +00001672MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1673 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001674{
1675#define RenderImageTag "Render/Image"
1676
1677 AffineMatrix
1678 affine,
1679 current;
1680
1681 char
1682 key[2*MaxTextExtent],
1683 keyword[MaxTextExtent],
1684 geometry[MaxTextExtent],
1685 name[MaxTextExtent],
1686 pattern[MaxTextExtent],
1687 *primitive,
1688 *token;
1689
1690 const char
1691 *q;
1692
1693 DrawInfo
1694 **graphic_context;
1695
cristy3ed852e2009-09-05 21:47:34 +00001696 MagickBooleanType
1697 proceed,
1698 status;
1699
cristya19f1d72012-08-07 18:24:38 +00001700 double
cristy3ed852e2009-09-05 21:47:34 +00001701 angle,
1702 factor,
1703 primitive_extent;
1704
1705 PointInfo
1706 point;
1707
cristy101ab702011-10-13 13:06:32 +00001708 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001709 start_color;
1710
1711 PrimitiveInfo
1712 *primitive_info;
1713
1714 PrimitiveType
1715 primitive_type;
1716
1717 register const char
1718 *p;
1719
cristybb503372010-05-27 20:51:26 +00001720 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001721 i,
1722 x;
1723
1724 SegmentInfo
1725 bounds;
1726
1727 size_t
cristy826a5472010-08-31 23:21:38 +00001728 length,
cristy3ed852e2009-09-05 21:47:34 +00001729 number_points;
1730
cristy826a5472010-08-31 23:21:38 +00001731 ssize_t
1732 j,
1733 k,
1734 n;
1735
cristy3ed852e2009-09-05 21:47:34 +00001736 /*
1737 Ensure the annotation info is valid.
1738 */
1739 assert(image != (Image *) NULL);
1740 assert(image->signature == MagickSignature);
1741 if (image->debug != MagickFalse)
1742 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1743 assert(draw_info != (DrawInfo *) NULL);
1744 assert(draw_info->signature == MagickSignature);
1745 if (image->debug != MagickFalse)
1746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1747 if ((draw_info->primitive == (char *) NULL) ||
1748 (*draw_info->primitive == '\0'))
1749 return(MagickFalse);
1750 if (image->debug != MagickFalse)
1751 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
1752 if (*draw_info->primitive != '@')
1753 primitive=AcquireString(draw_info->primitive);
1754 else
cristy947cb4c2011-10-20 18:41:46 +00001755 primitive=FileToString(draw_info->primitive+1,~0,exception);
cristy3ed852e2009-09-05 21:47:34 +00001756 if (primitive == (char *) NULL)
1757 return(MagickFalse);
cristya19f1d72012-08-07 18:24:38 +00001758 primitive_extent=(double) strlen(primitive);
cristy3ed852e2009-09-05 21:47:34 +00001759 (void) SetImageArtifact(image,"MVG",primitive);
1760 n=0;
1761 /*
1762 Allocate primitive info memory.
1763 */
cristy73bd4a52010-10-05 11:24:23 +00001764 graphic_context=(DrawInfo **) AcquireMagickMemory(
cristyed110712010-03-23 01:16:38 +00001765 sizeof(*graphic_context));
cristy3ed852e2009-09-05 21:47:34 +00001766 if (graphic_context == (DrawInfo **) NULL)
1767 {
1768 primitive=DestroyString(primitive);
1769 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1770 image->filename);
1771 }
cristy430522c2012-09-03 00:07:16 +00001772 number_points=6553;
cristy3ed852e2009-09-05 21:47:34 +00001773 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
1774 sizeof(*primitive_info));
1775 if (primitive_info == (PrimitiveInfo *) NULL)
1776 {
1777 primitive=DestroyString(primitive);
1778 for ( ; n >= 0; n--)
1779 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
1780 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
1781 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1782 image->filename);
1783 }
1784 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1785 graphic_context[n]->viewbox=image->page;
1786 if ((image->page.width == 0) || (image->page.height == 0))
1787 {
1788 graphic_context[n]->viewbox.width=image->columns;
1789 graphic_context[n]->viewbox.height=image->rows;
1790 }
1791 token=AcquireString(primitive);
cristy9950d572011-10-01 18:22:35 +00001792 (void) QueryColorCompliance("#000000",AllCompliance,&start_color,
cristy947cb4c2011-10-20 18:41:46 +00001793 exception);
1794 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001795 return(MagickFalse);
1796 status=MagickTrue;
1797 for (q=primitive; *q != '\0'; )
1798 {
1799 /*
1800 Interpret graphic primitive.
1801 */
1802 GetMagickToken(q,&q,keyword);
1803 if (*keyword == '\0')
1804 break;
1805 if (*keyword == '#')
1806 {
1807 /*
1808 Comment.
1809 */
1810 while ((*q != '\n') && (*q != '\0'))
1811 q++;
1812 continue;
1813 }
1814 p=q-strlen(keyword)-1;
1815 primitive_type=UndefinedPrimitive;
1816 current=graphic_context[n]->affine;
1817 GetAffineMatrix(&affine);
1818 switch (*keyword)
1819 {
1820 case ';':
1821 break;
1822 case 'a':
1823 case 'A':
1824 {
1825 if (LocaleCompare("affine",keyword) == 0)
1826 {
1827 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001828 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001829 GetMagickToken(q,&q,token);
1830 if (*token == ',')
1831 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001832 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001833 GetMagickToken(q,&q,token);
1834 if (*token == ',')
1835 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001836 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001837 GetMagickToken(q,&q,token);
1838 if (*token == ',')
1839 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001840 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001841 GetMagickToken(q,&q,token);
1842 if (*token == ',')
1843 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001844 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001845 GetMagickToken(q,&q,token);
1846 if (*token == ',')
1847 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001848 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001849 break;
1850 }
1851 if (LocaleCompare("arc",keyword) == 0)
1852 {
1853 primitive_type=ArcPrimitive;
1854 break;
1855 }
1856 status=MagickFalse;
1857 break;
1858 }
1859 case 'b':
1860 case 'B':
1861 {
1862 if (LocaleCompare("bezier",keyword) == 0)
1863 {
1864 primitive_type=BezierPrimitive;
1865 break;
1866 }
1867 if (LocaleCompare("border-color",keyword) == 0)
1868 {
1869 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00001870 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00001871 &graphic_context[n]->border_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00001872 break;
1873 }
1874 status=MagickFalse;
1875 break;
1876 }
1877 case 'c':
1878 case 'C':
1879 {
1880 if (LocaleCompare("clip-path",keyword) == 0)
1881 {
1882 /*
1883 Create clip mask.
1884 */
1885 GetMagickToken(q,&q,token);
1886 (void) CloneString(&graphic_context[n]->clip_mask,token);
1887 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00001888 graphic_context[n]->clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001889 break;
1890 }
1891 if (LocaleCompare("clip-rule",keyword) == 0)
1892 {
cristybb503372010-05-27 20:51:26 +00001893 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001894 fill_rule;
1895
1896 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001897 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001898 token);
1899 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00001900 status=MagickFalse;
1901 else
1902 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00001903 break;
1904 }
1905 if (LocaleCompare("clip-units",keyword) == 0)
1906 {
cristybb503372010-05-27 20:51:26 +00001907 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001908 clip_units;
1909
1910 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001911 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001912 token);
1913 if (clip_units == -1)
1914 {
1915 status=MagickFalse;
1916 break;
1917 }
1918 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1919 if (clip_units == ObjectBoundingBox)
1920 {
1921 GetAffineMatrix(&current);
1922 affine.sx=draw_info->bounds.x2;
1923 affine.sy=draw_info->bounds.y2;
1924 affine.tx=draw_info->bounds.x1;
1925 affine.ty=draw_info->bounds.y1;
1926 break;
1927 }
1928 break;
1929 }
1930 if (LocaleCompare("circle",keyword) == 0)
1931 {
1932 primitive_type=CirclePrimitive;
1933 break;
1934 }
1935 if (LocaleCompare("color",keyword) == 0)
1936 {
1937 primitive_type=ColorPrimitive;
1938 break;
1939 }
1940 status=MagickFalse;
1941 break;
1942 }
1943 case 'd':
1944 case 'D':
1945 {
1946 if (LocaleCompare("decorate",keyword) == 0)
1947 {
cristybb503372010-05-27 20:51:26 +00001948 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001949 decorate;
1950
1951 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001952 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001953 token);
1954 if (decorate == -1)
anthony2a021472011-10-08 11:29:29 +00001955 status=MagickFalse;
1956 else
1957 graphic_context[n]->decorate=(DecorationType) decorate;
cristy3ed852e2009-09-05 21:47:34 +00001958 break;
1959 }
1960 status=MagickFalse;
1961 break;
1962 }
1963 case 'e':
1964 case 'E':
1965 {
1966 if (LocaleCompare("ellipse",keyword) == 0)
1967 {
1968 primitive_type=EllipsePrimitive;
1969 break;
1970 }
1971 if (LocaleCompare("encoding",keyword) == 0)
1972 {
1973 GetMagickToken(q,&q,token);
1974 (void) CloneString(&graphic_context[n]->encoding,token);
1975 break;
1976 }
1977 status=MagickFalse;
1978 break;
1979 }
1980 case 'f':
1981 case 'F':
1982 {
1983 if (LocaleCompare("fill",keyword) == 0)
1984 {
1985 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00001986 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00001987 if (GetImageArtifact(image,pattern) != (const char *) NULL)
1988 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00001989 &graphic_context[n]->fill_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00001990 else
1991 {
cristy9950d572011-10-01 18:22:35 +00001992 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00001993 &graphic_context[n]->fill,exception);
cristy3ed852e2009-09-05 21:47:34 +00001994 if (status == MagickFalse)
1995 {
1996 ImageInfo
1997 *pattern_info;
1998
1999 pattern_info=AcquireImageInfo();
2000 (void) CopyMagickString(pattern_info->filename,token,
2001 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00002002 graphic_context[n]->fill_pattern=ReadImage(pattern_info,
2003 exception);
2004 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002005 pattern_info=DestroyImageInfo(pattern_info);
2006 }
2007 }
2008 break;
2009 }
cristy69fcc592012-04-28 18:39:59 +00002010 if (LocaleCompare("fill-alpha",keyword) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002011 {
2012 GetMagickToken(q,&q,token);
2013 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristya19f1d72012-08-07 18:24:38 +00002014 graphic_context[n]->fill.alpha=(double) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002015 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002016 break;
2017 }
2018 if (LocaleCompare("fill-rule",keyword) == 0)
2019 {
cristybb503372010-05-27 20:51:26 +00002020 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002021 fill_rule;
2022
2023 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002024 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002025 token);
2026 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00002027 status=MagickFalse;
2028 else
2029 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00002030 break;
2031 }
2032 if (LocaleCompare("font",keyword) == 0)
2033 {
2034 GetMagickToken(q,&q,token);
2035 (void) CloneString(&graphic_context[n]->font,token);
2036 if (LocaleCompare("none",token) == 0)
2037 graphic_context[n]->font=(char *)
2038 RelinquishMagickMemory(graphic_context[n]->font);
2039 break;
2040 }
2041 if (LocaleCompare("font-family",keyword) == 0)
2042 {
2043 GetMagickToken(q,&q,token);
2044 (void) CloneString(&graphic_context[n]->family,token);
2045 break;
2046 }
2047 if (LocaleCompare("font-size",keyword) == 0)
2048 {
2049 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002050 graphic_context[n]->pointsize=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002051 break;
2052 }
2053 if (LocaleCompare("font-stretch",keyword) == 0)
2054 {
cristybb503372010-05-27 20:51:26 +00002055 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002056 stretch;
2057
2058 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002059 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002060 if (stretch == -1)
anthony2a021472011-10-08 11:29:29 +00002061 status=MagickFalse;
2062 else
2063 graphic_context[n]->stretch=(StretchType) stretch;
cristy3ed852e2009-09-05 21:47:34 +00002064 break;
2065 }
2066 if (LocaleCompare("font-style",keyword) == 0)
2067 {
cristybb503372010-05-27 20:51:26 +00002068 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002069 style;
2070
2071 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002072 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002073 if (style == -1)
anthony2a021472011-10-08 11:29:29 +00002074 status=MagickFalse;
2075 else
2076 graphic_context[n]->style=(StyleType) style;
cristy3ed852e2009-09-05 21:47:34 +00002077 break;
2078 }
2079 if (LocaleCompare("font-weight",keyword) == 0)
2080 {
2081 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002082 graphic_context[n]->weight=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002083 if (LocaleCompare(token,"all") == 0)
2084 graphic_context[n]->weight=0;
2085 if (LocaleCompare(token,"bold") == 0)
2086 graphic_context[n]->weight=700;
2087 if (LocaleCompare(token,"bolder") == 0)
2088 if (graphic_context[n]->weight <= 800)
2089 graphic_context[n]->weight+=100;
2090 if (LocaleCompare(token,"lighter") == 0)
2091 if (graphic_context[n]->weight >= 100)
2092 graphic_context[n]->weight-=100;
2093 if (LocaleCompare(token,"normal") == 0)
2094 graphic_context[n]->weight=400;
2095 break;
2096 }
2097 status=MagickFalse;
2098 break;
2099 }
2100 case 'g':
2101 case 'G':
2102 {
2103 if (LocaleCompare("gradient-units",keyword) == 0)
2104 {
2105 GetMagickToken(q,&q,token);
2106 break;
2107 }
2108 if (LocaleCompare("gravity",keyword) == 0)
2109 {
cristybb503372010-05-27 20:51:26 +00002110 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002111 gravity;
2112
2113 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002114 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002115 if (gravity == -1)
anthony2a021472011-10-08 11:29:29 +00002116 status=MagickFalse;
2117 else
2118 graphic_context[n]->gravity=(GravityType) gravity;
cristy3ed852e2009-09-05 21:47:34 +00002119 break;
2120 }
2121 status=MagickFalse;
2122 break;
2123 }
2124 case 'i':
2125 case 'I':
2126 {
2127 if (LocaleCompare("image",keyword) == 0)
2128 {
cristybb503372010-05-27 20:51:26 +00002129 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002130 compose;
2131
2132 primitive_type=ImagePrimitive;
2133 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002134 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002135 if (compose == -1)
anthony2a021472011-10-08 11:29:29 +00002136 status=MagickFalse;
2137 else
2138 graphic_context[n]->compose=(CompositeOperator) compose;
cristy3ed852e2009-09-05 21:47:34 +00002139 break;
2140 }
cristyb32b90a2009-09-07 21:45:48 +00002141 if (LocaleCompare("interline-spacing",keyword) == 0)
2142 {
2143 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002144 graphic_context[n]->interline_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002145 (char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00002146 break;
2147 }
cristy3ed852e2009-09-05 21:47:34 +00002148 if (LocaleCompare("interword-spacing",keyword) == 0)
2149 {
2150 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002151 graphic_context[n]->interword_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002152 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002153 break;
2154 }
2155 status=MagickFalse;
2156 break;
2157 }
2158 case 'k':
2159 case 'K':
2160 {
2161 if (LocaleCompare("kerning",keyword) == 0)
2162 {
2163 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002164 graphic_context[n]->kerning=StringToDouble(token,(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;
cristy8cd03c32012-07-07 18:57:59 +00002200 graphic_context[n]->alpha=ClampToQuantum(QuantumRange*(1.0-((1.0-
2201 QuantumScale*graphic_context[n]->alpha)*factor*
2202 StringToDouble(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 {
cristy947cb4c2011-10-20 18:41:46 +00002246 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00002247 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002248 n=0;
2249 break;
2250 }
2251 if (graphic_context[n]->clip_mask != (char *) NULL)
2252 if (LocaleCompare(graphic_context[n]->clip_mask,
2253 graphic_context[n-1]->clip_mask) != 0)
cristye42f6582012-02-11 17:59:50 +00002254 (void) SetImageMask(image,(Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002255 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2256 n--;
2257 break;
2258 }
2259 if (LocaleCompare("pattern",token) == 0)
2260 break;
2261 status=MagickFalse;
2262 break;
2263 }
2264 if (LocaleCompare("push",keyword) == 0)
2265 {
2266 GetMagickToken(q,&q,token);
2267 if (LocaleCompare("clip-path",token) == 0)
2268 {
2269 char
2270 name[MaxTextExtent];
2271
2272 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002273 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002274 for (p=q; *q != '\0'; )
2275 {
2276 GetMagickToken(q,&q,token);
2277 if (LocaleCompare(token,"pop") != 0)
2278 continue;
2279 GetMagickToken(q,(const char **) NULL,token);
2280 if (LocaleCompare(token,"clip-path") != 0)
2281 continue;
2282 break;
2283 }
2284 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2285 (void) SetImageArtifact(image,name,token);
2286 GetMagickToken(q,&q,token);
2287 break;
2288 }
2289 if (LocaleCompare("gradient",token) == 0)
2290 {
2291 char
2292 key[2*MaxTextExtent],
2293 name[MaxTextExtent],
2294 type[MaxTextExtent];
2295
cristy3ed852e2009-09-05 21:47:34 +00002296 SegmentInfo
2297 segment;
2298
2299 GetMagickToken(q,&q,token);
2300 (void) CopyMagickString(name,token,MaxTextExtent);
2301 GetMagickToken(q,&q,token);
2302 (void) CopyMagickString(type,token,MaxTextExtent);
2303 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002304 segment.x1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002305 GetMagickToken(q,&q,token);
2306 if (*token == ',')
2307 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002308 segment.y1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002309 GetMagickToken(q,&q,token);
2310 if (*token == ',')
2311 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002312 segment.x2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002313 GetMagickToken(q,&q,token);
2314 if (*token == ',')
2315 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002316 segment.y2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002317 if (LocaleCompare(type,"radial") == 0)
2318 {
2319 GetMagickToken(q,&q,token);
2320 if (*token == ',')
2321 GetMagickToken(q,&q,token);
cristy3ed852e2009-09-05 21:47:34 +00002322 }
2323 for (p=q; *q != '\0'; )
2324 {
2325 GetMagickToken(q,&q,token);
2326 if (LocaleCompare(token,"pop") != 0)
2327 continue;
2328 GetMagickToken(q,(const char **) NULL,token);
2329 if (LocaleCompare(token,"gradient") != 0)
2330 continue;
2331 break;
2332 }
2333 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2334 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2335 graphic_context[n]->affine.ry*segment.y1+
2336 graphic_context[n]->affine.tx;
2337 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2338 graphic_context[n]->affine.sy*segment.y1+
2339 graphic_context[n]->affine.ty;
2340 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2341 graphic_context[n]->affine.ry*segment.y2+
2342 graphic_context[n]->affine.tx;
2343 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2344 graphic_context[n]->affine.sy*segment.y2+
2345 graphic_context[n]->affine.ty;
cristyb51dff52011-05-19 16:55:47 +00002346 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002347 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002348 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2349 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002350 "%gx%g%+.15g%+.15g",
cristy3ed852e2009-09-05 21:47:34 +00002351 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2352 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2353 bounds.x1,bounds.y1);
2354 (void) SetImageArtifact(image,key,geometry);
2355 GetMagickToken(q,&q,token);
2356 break;
2357 }
2358 if (LocaleCompare("pattern",token) == 0)
2359 {
2360 RectangleInfo
2361 bounds;
2362
2363 GetMagickToken(q,&q,token);
2364 (void) CopyMagickString(name,token,MaxTextExtent);
2365 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002366 bounds.x=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2367 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002368 GetMagickToken(q,&q,token);
2369 if (*token == ',')
2370 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002371 bounds.y=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2372 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002373 GetMagickToken(q,&q,token);
2374 if (*token == ',')
2375 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002376 bounds.width=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002377 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002378 GetMagickToken(q,&q,token);
2379 if (*token == ',')
2380 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002381 bounds.height=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002382 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002383 for (p=q; *q != '\0'; )
2384 {
2385 GetMagickToken(q,&q,token);
2386 if (LocaleCompare(token,"pop") != 0)
2387 continue;
2388 GetMagickToken(q,(const char **) NULL,token);
2389 if (LocaleCompare(token,"pattern") != 0)
2390 continue;
2391 break;
2392 }
2393 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
cristyb51dff52011-05-19 16:55:47 +00002394 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002395 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002396 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2397 (void) FormatLocaleString(geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002398 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002399 bounds.height,(double) bounds.x,(double) bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00002400 (void) SetImageArtifact(image,key,geometry);
2401 GetMagickToken(q,&q,token);
2402 break;
2403 }
2404 if (LocaleCompare("graphic-context",token) == 0)
2405 {
2406 n++;
2407 graphic_context=(DrawInfo **) ResizeQuantumMemory(
2408 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2409 if (graphic_context == (DrawInfo **) NULL)
2410 {
cristy947cb4c2011-10-20 18:41:46 +00002411 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00002412 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristy947cb4c2011-10-20 18:41:46 +00002413 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002414 break;
2415 }
2416 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2417 graphic_context[n-1]);
2418 break;
2419 }
2420 if (LocaleCompare("defs",token) == 0)
2421 break;
2422 status=MagickFalse;
2423 break;
2424 }
2425 status=MagickFalse;
2426 break;
2427 }
2428 case 'r':
2429 case 'R':
2430 {
2431 if (LocaleCompare("rectangle",keyword) == 0)
2432 {
2433 primitive_type=RectanglePrimitive;
2434 break;
2435 }
2436 if (LocaleCompare("rotate",keyword) == 0)
2437 {
2438 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002439 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002440 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2441 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2442 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2443 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2444 break;
2445 }
2446 if (LocaleCompare("roundRectangle",keyword) == 0)
2447 {
2448 primitive_type=RoundRectanglePrimitive;
2449 break;
2450 }
2451 status=MagickFalse;
2452 break;
2453 }
2454 case 's':
2455 case 'S':
2456 {
2457 if (LocaleCompare("scale",keyword) == 0)
2458 {
2459 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002460 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002461 GetMagickToken(q,&q,token);
2462 if (*token == ',')
2463 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002464 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002465 break;
2466 }
2467 if (LocaleCompare("skewX",keyword) == 0)
2468 {
2469 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002470 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002471 affine.ry=sin(DegreesToRadians(angle));
2472 break;
2473 }
2474 if (LocaleCompare("skewY",keyword) == 0)
2475 {
2476 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002477 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002478 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2479 break;
2480 }
2481 if (LocaleCompare("stop-color",keyword) == 0)
2482 {
cristy101ab702011-10-13 13:06:32 +00002483 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002484 stop_color;
2485
2486 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002487 (void) QueryColorCompliance(token,AllCompliance,&stop_color,
cristy947cb4c2011-10-20 18:41:46 +00002488 exception);
cristy3ed852e2009-09-05 21:47:34 +00002489 (void) GradientImage(image,LinearGradient,ReflectSpread,
cristy947cb4c2011-10-20 18:41:46 +00002490 &start_color,&stop_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00002491 start_color=stop_color;
2492 GetMagickToken(q,&q,token);
2493 break;
2494 }
2495 if (LocaleCompare("stroke",keyword) == 0)
2496 {
2497 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002498 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002499 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2500 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00002501 &graphic_context[n]->stroke_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002502 else
2503 {
cristy9950d572011-10-01 18:22:35 +00002504 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002505 &graphic_context[n]->stroke,exception);
cristy3ed852e2009-09-05 21:47:34 +00002506 if (status == MagickFalse)
2507 {
2508 ImageInfo
2509 *pattern_info;
2510
2511 pattern_info=AcquireImageInfo();
2512 (void) CopyMagickString(pattern_info->filename,token,
2513 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00002514 graphic_context[n]->stroke_pattern=ReadImage(pattern_info,
2515 exception);
2516 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002517 pattern_info=DestroyImageInfo(pattern_info);
2518 }
2519 }
2520 break;
2521 }
2522 if (LocaleCompare("stroke-antialias",keyword) == 0)
2523 {
2524 GetMagickToken(q,&q,token);
2525 graphic_context[n]->stroke_antialias=
cristyf2f27272009-12-17 14:48:46 +00002526 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002527 break;
2528 }
2529 if (LocaleCompare("stroke-dasharray",keyword) == 0)
2530 {
2531 if (graphic_context[n]->dash_pattern != (double *) NULL)
2532 graphic_context[n]->dash_pattern=(double *)
2533 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2534 if (IsPoint(q) != MagickFalse)
2535 {
2536 const char
2537 *p;
2538
2539 p=q;
2540 GetMagickToken(p,&p,token);
2541 if (*token == ',')
2542 GetMagickToken(p,&p,token);
2543 for (x=0; IsPoint(token) != MagickFalse; x++)
2544 {
2545 GetMagickToken(p,&p,token);
2546 if (*token == ',')
2547 GetMagickToken(p,&p,token);
2548 }
2549 graphic_context[n]->dash_pattern=(double *)
2550 AcquireQuantumMemory((size_t) (2UL*x+1UL),
2551 sizeof(*graphic_context[n]->dash_pattern));
2552 if (graphic_context[n]->dash_pattern == (double *) NULL)
2553 {
cristy947cb4c2011-10-20 18:41:46 +00002554 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00002555 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristy947cb4c2011-10-20 18:41:46 +00002556 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002557 break;
2558 }
2559 for (j=0; j < x; j++)
2560 {
2561 GetMagickToken(q,&q,token);
2562 if (*token == ',')
2563 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002564 graphic_context[n]->dash_pattern[j]=StringToDouble(token,
2565 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002566 }
2567 if ((x & 0x01) != 0)
2568 for ( ; j < (2*x); j++)
2569 graphic_context[n]->dash_pattern[j]=
2570 graphic_context[n]->dash_pattern[j-x];
2571 graphic_context[n]->dash_pattern[j]=0.0;
2572 break;
2573 }
2574 GetMagickToken(q,&q,token);
2575 break;
2576 }
2577 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2578 {
2579 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002580 graphic_context[n]->dash_offset=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002581 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002582 break;
2583 }
2584 if (LocaleCompare("stroke-linecap",keyword) == 0)
2585 {
cristybb503372010-05-27 20:51:26 +00002586 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002587 linecap;
2588
2589 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002590 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002591 if (linecap == -1)
anthony2a021472011-10-08 11:29:29 +00002592 status=MagickFalse;
2593 else
2594 graphic_context[n]->linecap=(LineCap) linecap;
cristy3ed852e2009-09-05 21:47:34 +00002595 break;
2596 }
2597 if (LocaleCompare("stroke-linejoin",keyword) == 0)
2598 {
cristybb503372010-05-27 20:51:26 +00002599 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002600 linejoin;
2601
2602 GetMagickToken(q,&q,token);
cristy7118edf2011-10-08 13:33:25 +00002603 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
2604 token);
cristy3ed852e2009-09-05 21:47:34 +00002605 if (linejoin == -1)
anthony2a021472011-10-08 11:29:29 +00002606 status=MagickFalse;
2607 else
2608 graphic_context[n]->linejoin=(LineJoin) linejoin;
cristy3ed852e2009-09-05 21:47:34 +00002609 break;
2610 }
2611 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2612 {
2613 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002614 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002615 break;
2616 }
2617 if (LocaleCompare("stroke-opacity",keyword) == 0)
2618 {
2619 GetMagickToken(q,&q,token);
2620 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristya19f1d72012-08-07 18:24:38 +00002621 graphic_context[n]->stroke.alpha=(double) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002622 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002623 break;
2624 }
2625 if (LocaleCompare("stroke-width",keyword) == 0)
2626 {
2627 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002628 graphic_context[n]->stroke_width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002629 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002630 break;
2631 }
2632 status=MagickFalse;
2633 break;
2634 }
2635 case 't':
2636 case 'T':
2637 {
2638 if (LocaleCompare("text",keyword) == 0)
2639 {
2640 primitive_type=TextPrimitive;
2641 break;
2642 }
2643 if (LocaleCompare("text-align",keyword) == 0)
2644 {
cristybb503372010-05-27 20:51:26 +00002645 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002646 align;
2647
2648 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002649 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002650 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002651 status=MagickFalse;
2652 else
2653 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002654 break;
2655 }
2656 if (LocaleCompare("text-anchor",keyword) == 0)
2657 {
cristybb503372010-05-27 20:51:26 +00002658 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002659 align;
2660
2661 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002662 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002663 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002664 status=MagickFalse;
2665 else
2666 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002667 break;
2668 }
2669 if (LocaleCompare("text-antialias",keyword) == 0)
2670 {
2671 GetMagickToken(q,&q,token);
2672 graphic_context[n]->text_antialias=
cristyf2f27272009-12-17 14:48:46 +00002673 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002674 break;
2675 }
2676 if (LocaleCompare("text-undercolor",keyword) == 0)
2677 {
2678 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002679 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002680 &graphic_context[n]->undercolor,exception);
cristy3ed852e2009-09-05 21:47:34 +00002681 break;
2682 }
2683 if (LocaleCompare("translate",keyword) == 0)
2684 {
2685 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002686 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002687 GetMagickToken(q,&q,token);
2688 if (*token == ',')
2689 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002690 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002691 break;
2692 }
2693 status=MagickFalse;
2694 break;
2695 }
2696 case 'v':
2697 case 'V':
2698 {
2699 if (LocaleCompare("viewbox",keyword) == 0)
2700 {
2701 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002702 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
2703 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002704 GetMagickToken(q,&q,token);
2705 if (*token == ',')
2706 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002707 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
2708 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002709 GetMagickToken(q,&q,token);
2710 if (*token == ',')
2711 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002712 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
2713 token,(char **) NULL)+0.5);
cristy06609ee2010-03-17 20:21:27 +00002714 GetMagickToken(q,&q,token);
2715 if (*token == ',')
2716 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002717 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
2718 token,(char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002719 break;
2720 }
2721 status=MagickFalse;
2722 break;
2723 }
2724 default:
2725 {
2726 status=MagickFalse;
2727 break;
2728 }
2729 }
2730 if (status == MagickFalse)
2731 break;
2732 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2733 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2734 {
cristyfbd70092010-12-01 19:31:14 +00002735 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2736 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2737 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2738 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2739 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2740 current.tx;
2741 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2742 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002743 }
2744 if (primitive_type == UndefinedPrimitive)
2745 {
2746 if (image->debug != MagickFalse)
2747 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2748 (int) (q-p),p);
2749 continue;
2750 }
2751 /*
2752 Parse the primitive attributes.
2753 */
2754 i=0;
2755 j=0;
2756 primitive_info[0].point.x=0.0;
2757 primitive_info[0].point.y=0.0;
2758 for (x=0; *q != '\0'; x++)
2759 {
2760 /*
2761 Define points.
2762 */
2763 if (IsPoint(q) == MagickFalse)
2764 break;
2765 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002766 point.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002767 GetMagickToken(q,&q,token);
2768 if (*token == ',')
2769 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002770 point.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002771 GetMagickToken(q,(const char **) NULL,token);
2772 if (*token == ',')
2773 GetMagickToken(q,&q,token);
2774 primitive_info[i].primitive=primitive_type;
2775 primitive_info[i].point=point;
2776 primitive_info[i].coordinates=0;
2777 primitive_info[i].method=FloodfillMethod;
2778 i++;
cristybb503372010-05-27 20:51:26 +00002779 if (i < (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +00002780 continue;
2781 number_points<<=1;
2782 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2783 (size_t) number_points,sizeof(*primitive_info));
2784 if (primitive_info == (PrimitiveInfo *) NULL)
2785 {
cristy947cb4c2011-10-20 18:41:46 +00002786 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00002787 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002788 break;
2789 }
2790 }
2791 primitive_info[j].primitive=primitive_type;
cristybb503372010-05-27 20:51:26 +00002792 primitive_info[j].coordinates=(size_t) x;
cristy3ed852e2009-09-05 21:47:34 +00002793 primitive_info[j].method=FloodfillMethod;
2794 primitive_info[j].text=(char *) NULL;
2795 /*
2796 Circumscribe primitive within a circle.
2797 */
2798 bounds.x1=primitive_info[j].point.x;
2799 bounds.y1=primitive_info[j].point.y;
2800 bounds.x2=primitive_info[j].point.x;
2801 bounds.y2=primitive_info[j].point.y;
cristybb503372010-05-27 20:51:26 +00002802 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
cristy3ed852e2009-09-05 21:47:34 +00002803 {
2804 point=primitive_info[j+k].point;
2805 if (point.x < bounds.x1)
2806 bounds.x1=point.x;
2807 if (point.y < bounds.y1)
2808 bounds.y1=point.y;
2809 if (point.x > bounds.x2)
2810 bounds.x2=point.x;
2811 if (point.y > bounds.y2)
2812 bounds.y2=point.y;
2813 }
2814 /*
2815 Speculate how many points our primitive might consume.
2816 */
2817 length=primitive_info[j].coordinates;
2818 switch (primitive_type)
2819 {
2820 case RectanglePrimitive:
2821 {
2822 length*=5;
2823 break;
2824 }
2825 case RoundRectanglePrimitive:
2826 {
cristy78817ad2010-05-07 12:25:34 +00002827 length*=5+8*BezierQuantum;
cristy3ed852e2009-09-05 21:47:34 +00002828 break;
2829 }
2830 case BezierPrimitive:
2831 {
2832 if (primitive_info[j].coordinates > 107)
cristy947cb4c2011-10-20 18:41:46 +00002833 (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
cristyefe601c2013-01-05 17:51:12 +00002834 "TooManyBezierCoordinates","`%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002835 length=BezierQuantum*primitive_info[j].coordinates;
2836 break;
2837 }
2838 case PathPrimitive:
2839 {
2840 char
2841 *s,
2842 *t;
2843
2844 GetMagickToken(q,&q,token);
cristy40a08ad2010-02-09 02:27:44 +00002845 length=1;
cristy3ed852e2009-09-05 21:47:34 +00002846 t=token;
2847 for (s=token; *s != '\0'; s=t)
2848 {
cristy00976d82011-02-20 20:31:28 +00002849 double
2850 value;
2851
cristydbdd0e32011-11-04 23:29:40 +00002852 value=StringToDouble(s,&t);
cristy00976d82011-02-20 20:31:28 +00002853 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00002854 if (s == t)
2855 {
2856 t++;
2857 continue;
2858 }
cristyef7a6ce2011-05-14 15:01:11 +00002859 length++;
cristy3ed852e2009-09-05 21:47:34 +00002860 }
cristyd2f832c2011-06-28 12:35:24 +00002861 length=length*BezierQuantum/2;
cristy3ed852e2009-09-05 21:47:34 +00002862 break;
2863 }
2864 case CirclePrimitive:
2865 case ArcPrimitive:
2866 case EllipsePrimitive:
2867 {
cristya19f1d72012-08-07 18:24:38 +00002868 double
cristy3ed852e2009-09-05 21:47:34 +00002869 alpha,
2870 beta,
2871 radius;
2872
2873 alpha=bounds.x2-bounds.x1;
2874 beta=bounds.y2-bounds.y1;
2875 radius=hypot((double) alpha,(double) beta);
cristyf53f26f2011-05-16 00:56:05 +00002876 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002877 break;
2878 }
2879 default:
2880 break;
2881 }
cristybb503372010-05-27 20:51:26 +00002882 if ((size_t) (i+length) >= number_points)
cristy3ed852e2009-09-05 21:47:34 +00002883 {
2884 /*
2885 Resize based on speculative points required by primitive.
2886 */
cristy9ce61b92010-05-12 16:30:26 +00002887 number_points+=length+1;
cristy3ed852e2009-09-05 21:47:34 +00002888 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2889 (size_t) number_points,sizeof(*primitive_info));
2890 if (primitive_info == (PrimitiveInfo *) NULL)
2891 {
cristy947cb4c2011-10-20 18:41:46 +00002892 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00002893 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00002894 image->filename);
2895 break;
2896 }
2897 }
2898 switch (primitive_type)
2899 {
2900 case PointPrimitive:
2901 default:
2902 {
2903 if (primitive_info[j].coordinates != 1)
2904 {
2905 status=MagickFalse;
2906 break;
2907 }
2908 TracePoint(primitive_info+j,primitive_info[j].point);
cristybb503372010-05-27 20:51:26 +00002909 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002910 break;
2911 }
2912 case LinePrimitive:
2913 {
2914 if (primitive_info[j].coordinates != 2)
2915 {
2916 status=MagickFalse;
2917 break;
2918 }
2919 TraceLine(primitive_info+j,primitive_info[j].point,
2920 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002921 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002922 break;
2923 }
2924 case RectanglePrimitive:
2925 {
2926 if (primitive_info[j].coordinates != 2)
2927 {
2928 status=MagickFalse;
2929 break;
2930 }
2931 TraceRectangle(primitive_info+j,primitive_info[j].point,
2932 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002933 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002934 break;
2935 }
2936 case RoundRectanglePrimitive:
2937 {
2938 if (primitive_info[j].coordinates != 3)
2939 {
2940 status=MagickFalse;
2941 break;
2942 }
2943 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
2944 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002945 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002946 break;
2947 }
2948 case ArcPrimitive:
2949 {
2950 if (primitive_info[j].coordinates != 3)
2951 {
2952 primitive_type=UndefinedPrimitive;
2953 break;
2954 }
2955 TraceArc(primitive_info+j,primitive_info[j].point,
2956 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002957 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002958 break;
2959 }
2960 case EllipsePrimitive:
2961 {
2962 if (primitive_info[j].coordinates != 3)
2963 {
2964 status=MagickFalse;
2965 break;
2966 }
2967 TraceEllipse(primitive_info+j,primitive_info[j].point,
2968 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002969 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002970 break;
2971 }
2972 case CirclePrimitive:
2973 {
2974 if (primitive_info[j].coordinates != 2)
2975 {
2976 status=MagickFalse;
2977 break;
2978 }
2979 TraceCircle(primitive_info+j,primitive_info[j].point,
2980 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002981 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002982 break;
2983 }
2984 case PolylinePrimitive:
2985 break;
2986 case PolygonPrimitive:
2987 {
2988 primitive_info[i]=primitive_info[j];
2989 primitive_info[i].coordinates=0;
2990 primitive_info[j].coordinates++;
2991 i++;
2992 break;
2993 }
2994 case BezierPrimitive:
2995 {
2996 if (primitive_info[j].coordinates < 3)
2997 {
2998 status=MagickFalse;
2999 break;
3000 }
3001 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
cristybb503372010-05-27 20:51:26 +00003002 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00003003 break;
3004 }
3005 case PathPrimitive:
3006 {
cristybb503372010-05-27 20:51:26 +00003007 i=(ssize_t) (j+TracePath(primitive_info+j,token));
cristy3ed852e2009-09-05 21:47:34 +00003008 break;
3009 }
3010 case ColorPrimitive:
3011 case MattePrimitive:
3012 {
cristybb503372010-05-27 20:51:26 +00003013 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003014 method;
3015
3016 if (primitive_info[j].coordinates != 1)
3017 {
3018 status=MagickFalse;
3019 break;
3020 }
3021 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00003022 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00003023 if (method == -1)
anthony2a021472011-10-08 11:29:29 +00003024 status=MagickFalse;
3025 else
3026 primitive_info[j].method=(PaintMethod) method;
cristy3ed852e2009-09-05 21:47:34 +00003027 break;
3028 }
3029 case TextPrimitive:
3030 {
3031 if (primitive_info[j].coordinates != 1)
3032 {
3033 status=MagickFalse;
3034 break;
3035 }
3036 if (*token != ',')
3037 GetMagickToken(q,&q,token);
3038 primitive_info[j].text=AcquireString(token);
3039 break;
3040 }
3041 case ImagePrimitive:
3042 {
3043 if (primitive_info[j].coordinates != 2)
3044 {
3045 status=MagickFalse;
3046 break;
3047 }
3048 GetMagickToken(q,&q,token);
3049 primitive_info[j].text=AcquireString(token);
3050 break;
3051 }
3052 }
3053 if (primitive_info == (PrimitiveInfo *) NULL)
3054 break;
3055 if (image->debug != MagickFalse)
3056 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3057 if (status == MagickFalse)
3058 break;
3059 primitive_info[i].primitive=UndefinedPrimitive;
3060 if (i == 0)
3061 continue;
3062 /*
3063 Transform points.
3064 */
3065 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3066 {
3067 point=primitive_info[i].point;
3068 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3069 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3070 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3071 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3072 point=primitive_info[i].point;
3073 if (point.x < graphic_context[n]->bounds.x1)
3074 graphic_context[n]->bounds.x1=point.x;
3075 if (point.y < graphic_context[n]->bounds.y1)
3076 graphic_context[n]->bounds.y1=point.y;
3077 if (point.x > graphic_context[n]->bounds.x2)
3078 graphic_context[n]->bounds.x2=point.x;
3079 if (point.y > graphic_context[n]->bounds.y2)
3080 graphic_context[n]->bounds.y2=point.y;
3081 if (primitive_info[i].primitive == ImagePrimitive)
3082 break;
cristybb503372010-05-27 20:51:26 +00003083 if (i >= (ssize_t) number_points)
cristy9ce61b92010-05-12 16:30:26 +00003084 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00003085 }
cristy3ed852e2009-09-05 21:47:34 +00003086 if (graphic_context[n]->render != MagickFalse)
3087 {
3088 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3089 (LocaleCompare(graphic_context[n]->clip_mask,
3090 graphic_context[n-1]->clip_mask) != 0))
3091 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00003092 graphic_context[n]->clip_mask,exception);
cristy947cb4c2011-10-20 18:41:46 +00003093 (void) DrawPrimitive(image,graphic_context[n],primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003094 }
3095 if (primitive_info->text != (char *) NULL)
3096 primitive_info->text=(char *) RelinquishMagickMemory(
3097 primitive_info->text);
3098 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3099 primitive_extent);
3100 if (proceed == MagickFalse)
3101 break;
3102 }
3103 if (image->debug != MagickFalse)
3104 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3105 /*
3106 Relinquish resources.
3107 */
3108 token=DestroyString(token);
3109 if (primitive_info != (PrimitiveInfo *) NULL)
3110 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3111 primitive=DestroyString(primitive);
3112 for ( ; n >= 0; n--)
3113 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3114 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3115 if (status == MagickFalse)
3116 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3117 keyword);
3118 return(status);
3119}
3120
3121/*
3122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3123% %
3124% %
3125% %
3126% D r a w G r a d i e n t I m a g e %
3127% %
3128% %
3129% %
3130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3131%
3132% DrawGradientImage() draws a linear gradient on the image.
3133%
3134% The format of the DrawGradientImage method is:
3135%
3136% MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003137% const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003138%
3139% A description of each parameter follows:
3140%
3141% o image: the image.
3142%
anthony2a021472011-10-08 11:29:29 +00003143% o draw_info: the draw info.
cristy3ed852e2009-09-05 21:47:34 +00003144%
cristy947cb4c2011-10-20 18:41:46 +00003145% o exception: return any errors or warnings in this structure.
3146%
cristy3ed852e2009-09-05 21:47:34 +00003147*/
3148
cristya19f1d72012-08-07 18:24:38 +00003149static inline double GetStopColorOffset(const GradientInfo *gradient,
cristybb503372010-05-27 20:51:26 +00003150 const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00003151{
3152 switch (gradient->type)
3153 {
3154 case UndefinedGradient:
3155 case LinearGradient:
3156 {
cristya19f1d72012-08-07 18:24:38 +00003157 double
cristy3ed852e2009-09-05 21:47:34 +00003158 gamma,
3159 length,
3160 offset,
3161 scale;
3162
3163 PointInfo
3164 p,
3165 q;
3166
3167 const SegmentInfo
3168 *gradient_vector;
3169
3170 gradient_vector=(&gradient->gradient_vector);
3171 p.x=gradient_vector->x2-gradient_vector->x1;
3172 p.y=gradient_vector->y2-gradient_vector->y1;
3173 q.x=(double) x-gradient_vector->x1;
3174 q.y=(double) y-gradient_vector->y1;
3175 length=sqrt(q.x*q.x+q.y*q.y);
3176 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
cristy3e3ec3a2012-11-03 23:11:06 +00003177 gamma=PerceptibleReciprocal(gamma);
cristy3ed852e2009-09-05 21:47:34 +00003178 scale=p.x*q.x+p.y*q.y;
3179 offset=gamma*scale*length;
3180 return(offset);
3181 }
3182 case RadialGradient:
3183 {
cristya19f1d72012-08-07 18:24:38 +00003184 double
cristy3ed852e2009-09-05 21:47:34 +00003185 length,
3186 offset;
3187
3188 PointInfo
3189 v;
3190
3191 v.x=(double) x-gradient->center.x;
3192 v.y=(double) y-gradient->center.y;
3193 length=sqrt(v.x*v.x+v.y*v.y);
3194 if (gradient->spread == RepeatSpread)
3195 return(length);
3196 offset=length/gradient->radius;
3197 return(offset);
3198 }
3199 }
3200 return(0.0);
3201}
3202
3203MagickExport MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003204 const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003205{
cristyc4c8d132010-01-07 01:58:38 +00003206 CacheView
3207 *image_view;
3208
cristy3ed852e2009-09-05 21:47:34 +00003209 const GradientInfo
3210 *gradient;
3211
3212 const SegmentInfo
3213 *gradient_vector;
3214
cristy75c658f2012-12-30 16:09:36 +00003215 double
3216 length;
3217
cristy3ed852e2009-09-05 21:47:34 +00003218 MagickBooleanType
3219 status;
3220
cristy4c08aed2011-07-01 19:47:50 +00003221 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003222 zero;
3223
cristy3ed852e2009-09-05 21:47:34 +00003224 PointInfo
3225 point;
3226
3227 RectangleInfo
3228 bounding_box;
3229
cristy826a5472010-08-31 23:21:38 +00003230 ssize_t
3231 y;
3232
cristy3ed852e2009-09-05 21:47:34 +00003233 /*
3234 Draw linear or radial gradient on image.
3235 */
3236 assert(image != (Image *) NULL);
3237 assert(image->signature == MagickSignature);
3238 if (image->debug != MagickFalse)
3239 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3240 assert(draw_info != (const DrawInfo *) NULL);
3241 gradient=(&draw_info->gradient);
3242 gradient_vector=(&gradient->gradient_vector);
3243 point.x=gradient_vector->x2-gradient_vector->x1;
3244 point.y=gradient_vector->y2-gradient_vector->y1;
3245 length=sqrt(point.x*point.x+point.y*point.y);
3246 bounding_box=gradient->bounding_box;
3247 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003248 GetPixelInfo(image,&zero);
cristy46ff2672012-12-14 15:32:26 +00003249 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00003250#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00003251 #pragma omp parallel for schedule(static,4) shared(status) \
cristycb7dfcc2013-01-06 18:34:59 +00003252 magick_threads(image,image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00003253#endif
cristybb503372010-05-27 20:51:26 +00003254 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00003255 {
cristy4c08aed2011-07-01 19:47:50 +00003256 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003257 composite,
3258 pixel;
3259
cristya19f1d72012-08-07 18:24:38 +00003260 double
cristy3ed852e2009-09-05 21:47:34 +00003261 alpha,
3262 offset;
3263
cristy4c08aed2011-07-01 19:47:50 +00003264 register Quantum
3265 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003266
cristybb503372010-05-27 20:51:26 +00003267 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003268 i,
3269 x;
3270
cristy826a5472010-08-31 23:21:38 +00003271 ssize_t
3272 j;
3273
cristy3ed852e2009-09-05 21:47:34 +00003274 if (status == MagickFalse)
3275 continue;
3276 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003277 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003278 {
3279 status=MagickFalse;
3280 continue;
3281 }
cristy3ed852e2009-09-05 21:47:34 +00003282 pixel=zero;
3283 composite=zero;
3284 offset=GetStopColorOffset(gradient,0,y);
3285 if (gradient->type != RadialGradient)
3286 offset/=length;
cristybb503372010-05-27 20:51:26 +00003287 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
cristy3ed852e2009-09-05 21:47:34 +00003288 {
cristy803640d2011-11-17 02:11:32 +00003289 GetPixelInfoPixel(image,q,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003290 switch (gradient->spread)
3291 {
3292 case UndefinedSpread:
3293 case PadSpread:
3294 {
cristybb503372010-05-27 20:51:26 +00003295 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3296 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003297 {
3298 offset=GetStopColorOffset(gradient,x,y);
3299 if (gradient->type != RadialGradient)
3300 offset/=length;
3301 }
cristybb503372010-05-27 20:51:26 +00003302 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003303 if (offset < gradient->stops[i].offset)
3304 break;
3305 if ((offset < 0.0) || (i == 0))
3306 composite=gradient->stops[0].color;
3307 else
cristybb503372010-05-27 20:51:26 +00003308 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
cristy3ed852e2009-09-05 21:47:34 +00003309 composite=gradient->stops[gradient->number_stops-1].color;
3310 else
3311 {
3312 j=i;
3313 i--;
3314 alpha=(offset-gradient->stops[i].offset)/
3315 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003316 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003317 &gradient->stops[j].color,alpha,&composite);
3318 }
3319 break;
3320 }
3321 case ReflectSpread:
3322 {
cristybb503372010-05-27 20:51:26 +00003323 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3324 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003325 {
3326 offset=GetStopColorOffset(gradient,x,y);
3327 if (gradient->type != RadialGradient)
3328 offset/=length;
3329 }
3330 if (offset < 0.0)
3331 offset=(-offset);
cristybb503372010-05-27 20:51:26 +00003332 if ((ssize_t) fmod(offset,2.0) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003333 offset=fmod(offset,1.0);
3334 else
3335 offset=1.0-fmod(offset,1.0);
cristybb503372010-05-27 20:51:26 +00003336 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003337 if (offset < gradient->stops[i].offset)
3338 break;
3339 if (i == 0)
3340 composite=gradient->stops[0].color;
3341 else
cristybb503372010-05-27 20:51:26 +00003342 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003343 composite=gradient->stops[gradient->number_stops-1].color;
3344 else
3345 {
3346 j=i;
3347 i--;
3348 alpha=(offset-gradient->stops[i].offset)/
3349 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003350 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003351 &gradient->stops[j].color,alpha,&composite);
3352 }
3353 break;
3354 }
3355 case RepeatSpread:
3356 {
3357 MagickBooleanType
3358 antialias;
3359
cristya19f1d72012-08-07 18:24:38 +00003360 double
cristy3ed852e2009-09-05 21:47:34 +00003361 repeat;
3362
3363 antialias=MagickFalse;
3364 repeat=0.0;
cristybb503372010-05-27 20:51:26 +00003365 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3366 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003367 {
3368 offset=GetStopColorOffset(gradient,x,y);
3369 if (gradient->type == LinearGradient)
3370 {
3371 repeat=fmod(offset,length);
3372 if (repeat < 0.0)
3373 repeat=length-fmod(-repeat,length);
3374 else
3375 repeat=fmod(offset,length);
3376 antialias=(repeat < length) && ((repeat+1.0) > length) ?
3377 MagickTrue : MagickFalse;
3378 offset=repeat/length;
3379 }
3380 else
3381 {
3382 repeat=fmod(offset,gradient->radius);
3383 if (repeat < 0.0)
3384 repeat=gradient->radius-fmod(-repeat,gradient->radius);
3385 else
3386 repeat=fmod(offset,gradient->radius);
cristy4c08aed2011-07-01 19:47:50 +00003387 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
3388 MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003389 offset=repeat/gradient->radius;
3390 }
3391 }
cristybb503372010-05-27 20:51:26 +00003392 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003393 if (offset < gradient->stops[i].offset)
3394 break;
3395 if (i == 0)
3396 composite=gradient->stops[0].color;
3397 else
cristybb503372010-05-27 20:51:26 +00003398 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003399 composite=gradient->stops[gradient->number_stops-1].color;
3400 else
3401 {
3402 j=i;
3403 i--;
3404 alpha=(offset-gradient->stops[i].offset)/
3405 (gradient->stops[j].offset-gradient->stops[i].offset);
3406 if (antialias != MagickFalse)
3407 {
3408 if (gradient->type == LinearGradient)
3409 alpha=length-repeat;
3410 else
3411 alpha=gradient->radius-repeat;
3412 i=0;
cristybb503372010-05-27 20:51:26 +00003413 j=(ssize_t) gradient->number_stops-1L;
cristy3ed852e2009-09-05 21:47:34 +00003414 }
cristy4c08aed2011-07-01 19:47:50 +00003415 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003416 &gradient->stops[j].color,alpha,&composite);
3417 }
3418 break;
3419 }
3420 }
cristy4c08aed2011-07-01 19:47:50 +00003421 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
3422 &pixel);
cristy803640d2011-11-17 02:11:32 +00003423 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003424 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003425 }
3426 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3427 status=MagickFalse;
3428 }
3429 image_view=DestroyCacheView(image_view);
3430 return(status);
3431}
3432
3433/*
3434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3435% %
3436% %
3437% %
3438% D r a w P a t t e r n P a t h %
3439% %
3440% %
3441% %
3442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3443%
3444% DrawPatternPath() draws a pattern.
3445%
3446% The format of the DrawPatternPath method is:
3447%
3448% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00003449% const char *name,Image **pattern,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003450%
3451% A description of each parameter follows:
3452%
3453% o image: the image.
3454%
3455% o draw_info: the draw info.
3456%
3457% o name: the pattern name.
3458%
3459% o image: the image.
3460%
cristy018f07f2011-09-04 21:15:19 +00003461% o exception: return any errors or warnings in this structure.
3462%
cristy3ed852e2009-09-05 21:47:34 +00003463*/
3464MagickExport MagickBooleanType DrawPatternPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00003465 const DrawInfo *draw_info,const char *name,Image **pattern,
3466 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003467{
3468 char
3469 property[MaxTextExtent];
3470
3471 const char
3472 *geometry,
3473 *path;
3474
3475 DrawInfo
3476 *clone_info;
3477
3478 ImageInfo
3479 *image_info;
3480
3481 MagickBooleanType
3482 status;
3483
3484 assert(image != (Image *) NULL);
3485 assert(image->signature == MagickSignature);
3486 if (image->debug != MagickFalse)
3487 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3488 assert(draw_info != (const DrawInfo *) NULL);
3489 assert(name != (const char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00003490 (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00003491 path=GetImageArtifact(image,property);
3492 if (path == (const char *) NULL)
3493 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00003494 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
cristy3ed852e2009-09-05 21:47:34 +00003495 geometry=GetImageArtifact(image,property);
3496 if (geometry == (const char *) NULL)
3497 return(MagickFalse);
3498 if ((*pattern) != (Image *) NULL)
3499 *pattern=DestroyImage(*pattern);
3500 image_info=AcquireImageInfo();
3501 image_info->size=AcquireString(geometry);
cristy947cb4c2011-10-20 18:41:46 +00003502 *pattern=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003503 image_info=DestroyImageInfo(image_info);
cristyca611542013-02-19 00:54:03 +00003504 (void) QueryColorCompliance("#000000ff",AllCompliance,
cristyea1a8aa2011-10-20 13:24:06 +00003505 &(*pattern)->background_color,exception);
3506 (void) SetImageBackgroundColor(*pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00003507 if (image->debug != MagickFalse)
3508 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3509 "begin pattern-path %s %s",name,geometry);
3510 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3511 clone_info->fill_pattern=NewImageList();
3512 clone_info->stroke_pattern=NewImageList();
3513 (void) CloneString(&clone_info->primitive,path);
cristy018f07f2011-09-04 21:15:19 +00003514 status=DrawImage(*pattern,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003515 clone_info=DestroyDrawInfo(clone_info);
3516 if (image->debug != MagickFalse)
3517 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3518 return(status);
3519}
3520
3521/*
3522%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3523% %
3524% %
3525% %
3526+ D r a w P o l y g o n P r i m i t i v e %
3527% %
3528% %
3529% %
3530%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3531%
3532% DrawPolygonPrimitive() draws a polygon on the image.
3533%
3534% The format of the DrawPolygonPrimitive method is:
3535%
3536% MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003537% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3538% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003539%
3540% A description of each parameter follows:
3541%
3542% o image: the image.
3543%
3544% o draw_info: the draw info.
3545%
3546% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3547%
cristy947cb4c2011-10-20 18:41:46 +00003548% o exception: return any errors or warnings in this structure.
3549%
cristy3ed852e2009-09-05 21:47:34 +00003550*/
3551
3552static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
3553{
cristybb503372010-05-27 20:51:26 +00003554 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003555 i;
3556
3557 assert(polygon_info != (PolygonInfo **) NULL);
cristyac245f82012-05-05 17:13:57 +00003558 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +00003559 if (polygon_info[i] != (PolygonInfo *) NULL)
3560 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
cristyb41ee102010-10-04 16:46:15 +00003561 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
cristy3ed852e2009-09-05 21:47:34 +00003562 return(polygon_info);
3563}
3564
3565static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
3566 const PrimitiveInfo *primitive_info)
3567{
3568 PathInfo
cristyfa112112010-01-04 17:48:07 +00003569 *restrict path_info;
cristy3ed852e2009-09-05 21:47:34 +00003570
cristy3ed852e2009-09-05 21:47:34 +00003571 PolygonInfo
3572 **polygon_info;
3573
cristy826a5472010-08-31 23:21:38 +00003574 register ssize_t
3575 i;
3576
cristybb503372010-05-27 20:51:26 +00003577 size_t
cristy3ed852e2009-09-05 21:47:34 +00003578 number_threads;
3579
cristy9357bdd2012-07-30 12:28:34 +00003580 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +00003581 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00003582 sizeof(*polygon_info));
3583 if (polygon_info == (PolygonInfo **) NULL)
3584 return((PolygonInfo **) NULL);
cristyac245f82012-05-05 17:13:57 +00003585 (void) ResetMagickMemory(polygon_info,0,number_threads*sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +00003586 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
cristy884d1622012-09-03 18:10:10 +00003599static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
cristye4e47ae2012-09-04 23:36:43 +00003600 const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
3601 const ssize_t y,double *stroke_alpha)
cristy3ed852e2009-09-05 21:47:34 +00003602{
cristya19f1d72012-08-07 18:24:38 +00003603 double
cristyb32b90a2009-09-07 21:45:48 +00003604 alpha,
3605 beta,
cristy3ed852e2009-09-05 21:47:34 +00003606 distance,
cristyb2e6b992011-12-17 16:42:29 +00003607 subpath_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003608
3609 PointInfo
cristyb32b90a2009-09-07 21:45:48 +00003610 delta;
cristy3ed852e2009-09-05 21:47:34 +00003611
cristyb32b90a2009-09-07 21:45:48 +00003612 register const PointInfo
3613 *q;
3614
cristy884d1622012-09-03 18:10:10 +00003615 register EdgeInfo
3616 *p;
3617
cristybb503372010-05-27 20:51:26 +00003618 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003619 i;
3620
cristycee97112010-05-28 00:44:52 +00003621 ssize_t
3622 j,
3623 winding_number;
3624
cristy3ed852e2009-09-05 21:47:34 +00003625 /*
3626 Compute fill & stroke opacity for this (x,y) point.
3627 */
cristyb2e6b992011-12-17 16:42:29 +00003628 *stroke_alpha=0.0;
3629 subpath_alpha=0.0;
cristy3ed852e2009-09-05 21:47:34 +00003630 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003631 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristy3ed852e2009-09-05 21:47:34 +00003632 {
cristye4e47ae2012-09-04 23:36:43 +00003633 if ((double) y <= (p->bounds.y1-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003634 break;
cristye4e47ae2012-09-04 23:36:43 +00003635 if ((double) y > (p->bounds.y2+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003636 {
cristybb503372010-05-27 20:51:26 +00003637 (void) DestroyEdge(polygon_info,(size_t) j);
cristy3ed852e2009-09-05 21:47:34 +00003638 continue;
3639 }
cristye4e47ae2012-09-04 23:36:43 +00003640 if (((double) x <= (p->bounds.x1-mid-0.5)) ||
3641 ((double) 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 {
cristye4e47ae2012-09-04 23:36:43 +00003646 if ((double) y <= (p->points[i-1].y-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003647 break;
cristye4e47ae2012-09-04 23:36:43 +00003648 if ((double) y > (p->points[i].y+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003649 continue;
cristye4e47ae2012-09-04 23:36:43 +00003650 if (p->scanline != (double) y)
cristy3ed852e2009-09-05 21:47:34 +00003651 {
cristye4e47ae2012-09-04 23:36:43 +00003652 p->scanline=(double) 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 {
cristye4e47ae2012-09-04 23:36:43 +00003664 delta.x=(double) x-q->x;
3665 delta.y=(double) 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 {
cristye4e47ae2012-09-04 23:36:43 +00003673 delta.x=(double) x-(q+1)->x;
3674 delta.y=(double) y-(q+1)->y;
cristy3ed852e2009-09-05 21:47:34 +00003675 distance=delta.x*delta.x+delta.y*delta.y;
3676 }
3677 else
3678 {
cristy592aefd2013-02-03 15:41:39 +00003679 alpha=1.0/alpha;
cristyb32b90a2009-09-07 21:45:48 +00003680 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
cristy4b67d752012-07-07 23:50:17 +00003681 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;
cristyb2e6b992011-12-17 16:42:29 +00003691 if ((*stroke_alpha < 1.0) &&
cristy3ed852e2009-09-05 21:47:34 +00003692 (distance <= ((alpha+0.25)*(alpha+0.25))))
3693 {
3694 alpha=mid-0.5;
3695 if (distance <= ((alpha+0.25)*(alpha+0.25)))
cristyb2e6b992011-12-17 16:42:29 +00003696 *stroke_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003697 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;
cristyb2e6b992011-12-17 16:42:29 +00003703 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
3704 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
cristy3ed852e2009-09-05 21:47:34 +00003705 }
3706 }
3707 }
cristyb2e6b992011-12-17 16:42:29 +00003708 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
cristy3ed852e2009-09-05 21:47:34 +00003709 continue;
3710 if (distance <= 0.0)
3711 {
cristyb2e6b992011-12-17 16:42:29 +00003712 subpath_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003713 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;
cristyb2e6b992011-12-17 16:42:29 +00003724 if (subpath_alpha < (alpha*alpha))
3725 subpath_alpha=alpha*alpha;
cristy3ed852e2009-09-05 21:47:34 +00003726 }
cristy3ed852e2009-09-05 21:47:34 +00003727 }
3728 /*
3729 Compute fill opacity.
3730 */
3731 if (fill == MagickFalse)
3732 return(0.0);
cristyb2e6b992011-12-17 16:42:29 +00003733 if (subpath_alpha >= 1.0)
cristy3ed852e2009-09-05 21:47:34 +00003734 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 {
cristye4e47ae2012-09-04 23:36:43 +00003742 if ((double) y <= p->bounds.y1)
cristyb32b90a2009-09-07 21:45:48 +00003743 break;
cristye4e47ae2012-09-04 23:36:43 +00003744 if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
cristyb32b90a2009-09-07 21:45:48 +00003745 continue;
cristye4e47ae2012-09-04 23:36:43 +00003746 if ((double) x > p->bounds.x2)
cristyb32b90a2009-09-07 21:45:48 +00003747 {
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++)
cristye4e47ae2012-09-04 23:36:43 +00003753 if ((double) y <= p->points[i].y)
cristyb32b90a2009-09-07 21:45:48 +00003754 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);
cristyb2e6b992011-12-17 16:42:29 +00003767 return(subpath_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003768}
3769
3770static MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003771 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3772 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003773{
cristyfa112112010-01-04 17:48:07 +00003774 CacheView
3775 *image_view;
3776
cristy3ed852e2009-09-05 21:47:34 +00003777 MagickBooleanType
3778 fill,
3779 status;
3780
cristya19f1d72012-08-07 18:24:38 +00003781 double
cristy3ed852e2009-09-05 21:47:34 +00003782 mid;
3783
3784 PolygonInfo
cristyfa112112010-01-04 17:48:07 +00003785 **restrict polygon_info;
cristy3ed852e2009-09-05 21:47:34 +00003786
3787 register EdgeInfo
3788 *p;
3789
cristybb503372010-05-27 20:51:26 +00003790 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003791 i;
3792
3793 SegmentInfo
3794 bounds;
3795
cristy826a5472010-08-31 23:21:38 +00003796 ssize_t
3797 start,
3798 stop,
3799 y;
3800
cristy3ed852e2009-09-05 21:47:34 +00003801 /*
3802 Compute bounding box.
3803 */
3804 assert(image != (Image *) NULL);
3805 assert(image->signature == MagickSignature);
3806 if (image->debug != MagickFalse)
3807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3808 assert(draw_info != (DrawInfo *) NULL);
3809 assert(draw_info->signature == MagickSignature);
3810 assert(primitive_info != (PrimitiveInfo *) NULL);
3811 if (primitive_info->coordinates == 0)
3812 return(MagickTrue);
3813 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3814 if (polygon_info == (PolygonInfo **) NULL)
3815 return(MagickFalse);
3816 if (0)
cristy947cb4c2011-10-20 18:41:46 +00003817 DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
cristy3ed852e2009-09-05 21:47:34 +00003818 if (image->debug != MagickFalse)
3819 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3820 fill=(primitive_info->method == FillToBorderMethod) ||
3821 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3822 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3823 bounds=polygon_info[0]->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00003824 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00003825 {
3826 p=polygon_info[0]->edges+i;
3827 if (p->bounds.x1 < bounds.x1)
3828 bounds.x1=p->bounds.x1;
3829 if (p->bounds.y1 < bounds.y1)
3830 bounds.y1=p->bounds.y1;
3831 if (p->bounds.x2 > bounds.x2)
3832 bounds.x2=p->bounds.x2;
3833 if (p->bounds.y2 > bounds.y2)
3834 bounds.y2=p->bounds.y2;
3835 }
3836 bounds.x1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003837 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
cristy6c5494a2012-09-22 00:30:37 +00003838 image->columns ? (double) image->columns-1 : bounds.x1;
cristy3ed852e2009-09-05 21:47:34 +00003839 bounds.y1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003840 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
cristy6c5494a2012-09-22 00:30:37 +00003841 image->rows ? (double) image->rows-1 : bounds.y1;
cristy3ed852e2009-09-05 21:47:34 +00003842 bounds.x2+=(mid+1.0);
cristy8071c472012-09-24 12:41:06 +00003843 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
cristy6c5494a2012-09-22 00:30:37 +00003844 image->columns ? (double) image->columns-1 : bounds.x2;
cristy3ed852e2009-09-05 21:47:34 +00003845 bounds.y2+=(mid+1.0);
cristy8071c472012-09-24 12:41:06 +00003846 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
cristy6c5494a2012-09-22 00:30:37 +00003847 image->rows ? (double) image->rows-1 : bounds.y2;
cristy3ed852e2009-09-05 21:47:34 +00003848 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00003849 image_view=AcquireAuthenticCacheView(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003850 if (primitive_info->coordinates == 1)
3851 {
3852 /*
3853 Draw point.
3854 */
cristy564a5692012-01-20 23:56:26 +00003855 start=(ssize_t) ceil(bounds.y1-0.5);
cristy8071c472012-09-24 12:41:06 +00003856 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003857#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00003858 #pragma omp parallel for schedule(static,4) shared(status) \
cristycb7dfcc2013-01-06 18:34:59 +00003859 magick_threads(image,image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00003860#endif
cristy564a5692012-01-20 23:56:26 +00003861 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003862 {
3863 MagickBooleanType
3864 sync;
3865
cristy101ab702011-10-13 13:06:32 +00003866 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003867 pixel;
3868
cristybb503372010-05-27 20:51:26 +00003869 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003870 x;
3871
cristy4c08aed2011-07-01 19:47:50 +00003872 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003873 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003874
cristy564a5692012-01-20 23:56:26 +00003875 ssize_t
3876 start,
3877 stop;
3878
cristy3ed852e2009-09-05 21:47:34 +00003879 if (status == MagickFalse)
3880 continue;
cristy564a5692012-01-20 23:56:26 +00003881 start=(ssize_t) ceil(bounds.x1-0.5);
cristy8071c472012-09-24 12:41:06 +00003882 stop=(ssize_t) floor(bounds.x2+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003883 x=start;
cristyf707b302012-09-04 23:51:26 +00003884 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),1,
3885 exception);
cristy4c08aed2011-07-01 19:47:50 +00003886 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003887 {
3888 status=MagickFalse;
3889 continue;
3890 }
cristy101ab702011-10-13 13:06:32 +00003891 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003892 for ( ; x <= stop; x++)
3893 {
cristybb503372010-05-27 20:51:26 +00003894 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3895 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
cristy4c08aed2011-07-01 19:47:50 +00003896 {
cristy2ed42f62011-10-02 19:49:57 +00003897 (void) GetStrokeColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00003898 SetPixelInfoPixel(image,&pixel,q);
cristy4c08aed2011-07-01 19:47:50 +00003899 }
cristyed231572011-07-14 02:18:59 +00003900 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003901 }
3902 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3903 if (sync == MagickFalse)
3904 status=MagickFalse;
3905 }
3906 image_view=DestroyCacheView(image_view);
3907 polygon_info=DestroyPolygonThreadSet(polygon_info);
3908 if (image->debug != MagickFalse)
3909 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3910 " end draw-polygon");
3911 return(status);
3912 }
3913 /*
3914 Draw polygon or line.
3915 */
cristy8a46d822012-08-28 23:32:39 +00003916 if (image->alpha_trait != BlendPixelTrait)
cristy63240882011-08-05 19:05:27 +00003917 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy564a5692012-01-20 23:56:26 +00003918 start=(ssize_t) ceil(bounds.y1-0.5);
cristy8071c472012-09-24 12:41:06 +00003919 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003920#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00003921 #pragma omp parallel for schedule(static,4) shared(status) \
cristycb7dfcc2013-01-06 18:34:59 +00003922 magick_threads(image,image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00003923#endif
cristy564a5692012-01-20 23:56:26 +00003924 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003925 {
cristy5c9e6f22010-09-17 17:31:01 +00003926 const int
3927 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003928
cristya19f1d72012-08-07 18:24:38 +00003929 double
cristyb2e6b992011-12-17 16:42:29 +00003930 fill_alpha,
3931 stroke_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003932
cristy101ab702011-10-13 13:06:32 +00003933 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003934 fill_color,
3935 stroke_color;
3936
cristy4c08aed2011-07-01 19:47:50 +00003937 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003938 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003939
cristy826a5472010-08-31 23:21:38 +00003940 register ssize_t
3941 x;
3942
cristy564a5692012-01-20 23:56:26 +00003943 ssize_t
3944 start,
3945 stop;
3946
cristy3ed852e2009-09-05 21:47:34 +00003947 if (status == MagickFalse)
3948 continue;
cristy564a5692012-01-20 23:56:26 +00003949 start=(ssize_t) ceil(bounds.x1-0.5);
cristy8071c472012-09-24 12:41:06 +00003950 stop=(ssize_t) floor(bounds.x2+0.5);
cristyb2e6b992011-12-17 16:42:29 +00003951 q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-start+1),1,
3952 exception);
cristy4c08aed2011-07-01 19:47:50 +00003953 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003954 {
3955 status=MagickFalse;
3956 continue;
3957 }
cristy3ed852e2009-09-05 21:47:34 +00003958 for (x=start; x <= stop; x++)
3959 {
3960 /*
3961 Fill and/or stroke.
3962 */
cristyb2e6b992011-12-17 16:42:29 +00003963 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
cristye4e47ae2012-09-04 23:36:43 +00003964 x,y,&stroke_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003965 if (draw_info->stroke_antialias == MagickFalse)
3966 {
cristyb2e6b992011-12-17 16:42:29 +00003967 fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0;
3968 stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0;
cristy3ed852e2009-09-05 21:47:34 +00003969 }
cristy2ed42f62011-10-02 19:49:57 +00003970 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003971 fill_alpha=fill_alpha*fill_color.alpha;
cristya19f1d72012-08-07 18:24:38 +00003972 CompositePixelOver(image,&fill_color,fill_alpha,q,(double)
cristy4c08aed2011-07-01 19:47:50 +00003973 GetPixelAlpha(image,q),q);
cristy2ed42f62011-10-02 19:49:57 +00003974 (void) GetStrokeColor(draw_info,x,y,&stroke_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003975 stroke_alpha=stroke_alpha*stroke_color.alpha;
cristya19f1d72012-08-07 18:24:38 +00003976 CompositePixelOver(image,&stroke_color,stroke_alpha,q,(double)
cristy4c08aed2011-07-01 19:47:50 +00003977 GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00003978 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003979 }
3980 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3981 status=MagickFalse;
3982 }
3983 image_view=DestroyCacheView(image_view);
3984 polygon_info=DestroyPolygonThreadSet(polygon_info);
3985 if (image->debug != MagickFalse)
3986 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
3987 return(status);
3988}
3989
3990/*
3991%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3992% %
3993% %
3994% %
3995% D r a w P r i m i t i v e %
3996% %
3997% %
3998% %
3999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4000%
4001% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
4002%
4003% The format of the DrawPrimitive method is:
4004%
4005% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00004006% PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004007%
4008% A description of each parameter follows:
4009%
4010% o image: the image.
4011%
4012% o draw_info: the draw info.
4013%
4014% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4015%
cristy947cb4c2011-10-20 18:41:46 +00004016% o exception: return any errors or warnings in this structure.
4017%
cristy3ed852e2009-09-05 21:47:34 +00004018*/
4019
4020static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4021{
4022 const char
4023 *methods[] =
4024 {
4025 "point",
4026 "replace",
4027 "floodfill",
4028 "filltoborder",
4029 "reset",
4030 "?"
4031 };
4032
cristy3ed852e2009-09-05 21:47:34 +00004033 PointInfo
4034 p,
4035 q,
4036 point;
4037
cristybb503372010-05-27 20:51:26 +00004038 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004039 i,
4040 x;
4041
cristy826a5472010-08-31 23:21:38 +00004042 ssize_t
4043 coordinates,
4044 y;
4045
cristybb503372010-05-27 20:51:26 +00004046 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4047 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004048 switch (primitive_info->primitive)
4049 {
4050 case PointPrimitive:
4051 {
4052 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004053 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004054 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004055 return;
4056 }
4057 case ColorPrimitive:
4058 {
4059 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004060 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004061 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004062 return;
4063 }
4064 case MattePrimitive:
4065 {
4066 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004067 "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004068 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004069 return;
4070 }
4071 case TextPrimitive:
4072 {
4073 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004074 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004075 return;
4076 }
4077 case ImagePrimitive:
4078 {
4079 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004080 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004081 return;
4082 }
4083 default:
4084 break;
4085 }
4086 coordinates=0;
4087 p=primitive_info[0].point;
4088 q.x=(-1.0);
4089 q.y=(-1.0);
4090 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4091 {
4092 point=primitive_info[i].point;
4093 if (coordinates <= 0)
4094 {
cristybb503372010-05-27 20:51:26 +00004095 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004096 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004097 " begin open (%.20g)",(double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004098 p=point;
4099 }
4100 point=primitive_info[i].point;
cristy53aed702012-07-08 00:21:25 +00004101 if ((fabs(q.x-point.x) >= MagickEpsilon) ||
4102 (fabs(q.y-point.y) >= MagickEpsilon))
cristy8cd5b312010-01-07 01:10:24 +00004103 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004104 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004105 else
4106 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004107 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004108 q=point;
4109 coordinates--;
4110 if (coordinates > 0)
4111 continue;
cristy53aed702012-07-08 00:21:25 +00004112 if ((fabs(p.x-point.x) >= MagickEpsilon) ||
4113 (fabs(p.y-point.y) >= MagickEpsilon))
cristye8c25f92010-06-03 00:53:06 +00004114 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4115 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004116 else
cristye8c25f92010-06-03 00:53:06 +00004117 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4118 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004119 }
4120}
4121
4122MagickExport MagickBooleanType DrawPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004123 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4124 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004125{
cristyc4c8d132010-01-07 01:58:38 +00004126 CacheView
4127 *image_view;
4128
cristy3ed852e2009-09-05 21:47:34 +00004129 MagickStatusType
4130 status;
4131
cristybb503372010-05-27 20:51:26 +00004132 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004133 i,
4134 x;
4135
cristy826a5472010-08-31 23:21:38 +00004136 ssize_t
4137 y;
4138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (image->debug != MagickFalse)
4140 {
4141 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4142 " begin draw-primitive");
4143 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004144 " affine: %g %g %g %g %g %g",draw_info->affine.sx,
cristy3ed852e2009-09-05 21:47:34 +00004145 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4146 draw_info->affine.tx,draw_info->affine.ty);
4147 }
cristya6400b12013-03-15 12:20:18 +00004148 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
cristy4dab3802013-03-15 22:08:15 +00004149 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
4150 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
cristy0c81d062013-04-21 15:22:02 +00004151 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00004152 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004153 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4154 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy46ff2672012-12-14 15:32:26 +00004155 image_view=AcquireAuthenticCacheView(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004156 switch (primitive_info->primitive)
4157 {
4158 case PointPrimitive:
4159 {
cristy101ab702011-10-13 13:06:32 +00004160 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004161 fill_color;
4162
cristy4c08aed2011-07-01 19:47:50 +00004163 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004164 *q;
4165
cristybb503372010-05-27 20:51:26 +00004166 if ((y < 0) || (y >= (ssize_t) image->rows))
cristyb32b90a2009-09-07 21:45:48 +00004167 break;
cristybb503372010-05-27 20:51:26 +00004168 if ((x < 0) || (x >= (ssize_t) image->columns))
cristyb32b90a2009-09-07 21:45:48 +00004169 break;
cristy3ed852e2009-09-05 21:47:34 +00004170 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004171 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004172 break;
cristy2ed42f62011-10-02 19:49:57 +00004173 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristya19f1d72012-08-07 18:24:38 +00004174 CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,
4175 (double) GetPixelAlpha(image,q),q);
cristy3ed852e2009-09-05 21:47:34 +00004176 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4177 break;
4178 }
4179 case ColorPrimitive:
4180 {
4181 switch (primitive_info->method)
4182 {
4183 case PointMethod:
4184 default:
4185 {
cristy101ab702011-10-13 13:06:32 +00004186 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004187 pixel;
4188
4189 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004190 *q;
4191
4192 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004193 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004194 break;
cristy101ab702011-10-13 13:06:32 +00004195 GetPixelInfo(image,&pixel);
cristy2ed42f62011-10-02 19:49:57 +00004196 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004197 SetPixelInfoPixel(image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004198 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4199 break;
4200 }
4201 case ReplaceMethod:
4202 {
4203 MagickBooleanType
4204 sync;
4205
cristy101ab702011-10-13 13:06:32 +00004206 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004207 pixel,
cristy3ed852e2009-09-05 21:47:34 +00004208 target;
4209
cristyf05d4942012-03-17 16:26:09 +00004210 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
cristy2ed42f62011-10-02 19:49:57 +00004211 exception);
cristybb503372010-05-27 20:51:26 +00004212 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004213 {
cristy4c08aed2011-07-01 19:47:50 +00004214 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004215 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004216
4217 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4218 exception);
cristy4c08aed2011-07-01 19:47:50 +00004219 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004220 break;
cristybb503372010-05-27 20:51:26 +00004221 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004222 {
cristy101ab702011-10-13 13:06:32 +00004223 GetPixelInfoPixel(image,q,&pixel);
4224 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004225 {
cristyed231572011-07-14 02:18:59 +00004226 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004227 continue;
4228 }
cristy2ed42f62011-10-02 19:49:57 +00004229 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004230 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004231 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004232 }
4233 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4234 if (sync == MagickFalse)
4235 break;
4236 }
4237 break;
4238 }
4239 case FloodfillMethod:
4240 case FillToBorderMethod:
4241 {
cristy4c08aed2011-07-01 19:47:50 +00004242 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004243 target;
4244
cristy3aa93752011-12-18 15:54:24 +00004245 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004246 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004247 if (primitive_info->method == FillToBorderMethod)
4248 {
cristya19f1d72012-08-07 18:24:38 +00004249 target.red=(double) draw_info->border_color.red;
4250 target.green=(double) draw_info->border_color.green;
4251 target.blue=(double) draw_info->border_color.blue;
cristy3ed852e2009-09-05 21:47:34 +00004252 }
cristyd42d9952011-07-08 14:21:50 +00004253 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
4254 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004255 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004256 break;
4257 }
4258 case ResetMethod:
4259 {
4260 MagickBooleanType
4261 sync;
4262
cristy101ab702011-10-13 13:06:32 +00004263 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004264 pixel;
4265
cristy101ab702011-10-13 13:06:32 +00004266 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00004267 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004268 {
cristy4c08aed2011-07-01 19:47:50 +00004269 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004270 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004271
cristy826a5472010-08-31 23:21:38 +00004272 register ssize_t
4273 x;
4274
cristy3ed852e2009-09-05 21:47:34 +00004275 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4276 exception);
cristy4c08aed2011-07-01 19:47:50 +00004277 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004278 break;
cristybb503372010-05-27 20:51:26 +00004279 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004280 {
cristy2ed42f62011-10-02 19:49:57 +00004281 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004282 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004283 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004284 }
4285 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4286 if (sync == MagickFalse)
4287 break;
4288 }
4289 break;
4290 }
4291 }
4292 break;
4293 }
4294 case MattePrimitive:
4295 {
cristy8a46d822012-08-28 23:32:39 +00004296 if (image->alpha_trait != BlendPixelTrait)
cristy63240882011-08-05 19:05:27 +00004297 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004298 switch (primitive_info->method)
4299 {
4300 case PointMethod:
4301 default:
4302 {
cristy101ab702011-10-13 13:06:32 +00004303 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004304 pixel;
4305
cristy4c08aed2011-07-01 19:47:50 +00004306 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004307 *q;
4308
4309 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004310 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004311 break;
cristy2ed42f62011-10-02 19:49:57 +00004312 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004313 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00004314 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4315 break;
4316 }
4317 case ReplaceMethod:
4318 {
4319 MagickBooleanType
4320 sync;
4321
cristy101ab702011-10-13 13:06:32 +00004322 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004323 pixel,
4324 target;
4325
cristyf05d4942012-03-17 16:26:09 +00004326 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
cristy2ed42f62011-10-02 19:49:57 +00004327 exception);
cristybb503372010-05-27 20:51:26 +00004328 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004329 {
cristy4c08aed2011-07-01 19:47:50 +00004330 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004331 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004332
cristy826a5472010-08-31 23:21:38 +00004333 register ssize_t
4334 x;
4335
cristy3ed852e2009-09-05 21:47:34 +00004336 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4337 exception);
cristy4c08aed2011-07-01 19:47:50 +00004338 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004339 break;
cristybb503372010-05-27 20:51:26 +00004340 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004341 {
cristy101ab702011-10-13 13:06:32 +00004342 GetPixelInfoPixel(image,q,&pixel);
4343 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004344 {
cristyed231572011-07-14 02:18:59 +00004345 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004346 continue;
4347 }
cristy2ed42f62011-10-02 19:49:57 +00004348 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004349 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004350 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004351 }
4352 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4353 if (sync == MagickFalse)
4354 break;
4355 }
4356 break;
4357 }
4358 case FloodfillMethod:
4359 case FillToBorderMethod:
4360 {
cristybd5a96c2011-08-21 00:04:26 +00004361 ChannelType
4362 channel_mask;
4363
cristy4c08aed2011-07-01 19:47:50 +00004364 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004365 target;
4366
cristy3aa93752011-12-18 15:54:24 +00004367 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004368 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004369 if (primitive_info->method == FillToBorderMethod)
4370 {
cristya19f1d72012-08-07 18:24:38 +00004371 target.red=(double) draw_info->border_color.red;
4372 target.green=(double) draw_info->border_color.green;
4373 target.blue=(double) draw_info->border_color.blue;
cristy3ed852e2009-09-05 21:47:34 +00004374 }
cristycf1296e2012-08-26 23:40:49 +00004375 channel_mask=SetImageChannelMask(image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +00004376 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
cristy3ed852e2009-09-05 21:47:34 +00004377 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004378 MagickTrue,exception);
cristycf1296e2012-08-26 23:40:49 +00004379 (void) SetImageChannelMask(image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004380 break;
4381 }
4382 case ResetMethod:
4383 {
4384 MagickBooleanType
4385 sync;
4386
cristy101ab702011-10-13 13:06:32 +00004387 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004388 pixel;
4389
cristybb503372010-05-27 20:51:26 +00004390 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004391 {
cristy4c08aed2011-07-01 19:47:50 +00004392 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004393 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004394
cristy826a5472010-08-31 23:21:38 +00004395 register ssize_t
4396 x;
4397
cristy3ed852e2009-09-05 21:47:34 +00004398 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4399 exception);
cristy4c08aed2011-07-01 19:47:50 +00004400 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004401 break;
cristybb503372010-05-27 20:51:26 +00004402 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004403 {
cristy2ed42f62011-10-02 19:49:57 +00004404 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004405 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004406 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004407 }
4408 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4409 if (sync == MagickFalse)
4410 break;
4411 }
4412 break;
4413 }
4414 }
4415 break;
4416 }
4417 case TextPrimitive:
4418 {
4419 char
4420 geometry[MaxTextExtent];
4421
4422 DrawInfo
4423 *clone_info;
4424
4425 if (primitive_info->text == (char *) NULL)
4426 break;
4427 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4428 (void) CloneString(&clone_info->text,primitive_info->text);
cristyb51dff52011-05-19 16:55:47 +00004429 (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
cristy3ed852e2009-09-05 21:47:34 +00004430 primitive_info->point.x,primitive_info->point.y);
4431 (void) CloneString(&clone_info->geometry,geometry);
cristy5cbc0162011-08-29 00:36:28 +00004432 status=AnnotateImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004433 clone_info=DestroyDrawInfo(clone_info);
4434 break;
4435 }
4436 case ImagePrimitive:
4437 {
4438 AffineMatrix
4439 affine;
4440
4441 char
4442 composite_geometry[MaxTextExtent];
4443
4444 Image
4445 *composite_image;
4446
4447 ImageInfo
4448 *clone_info;
4449
cristy826a5472010-08-31 23:21:38 +00004450 RectangleInfo
4451 geometry;
4452
cristybb503372010-05-27 20:51:26 +00004453 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004454 x1,
4455 y1;
4456
cristy3ed852e2009-09-05 21:47:34 +00004457 if (primitive_info->text == (char *) NULL)
4458 break;
4459 clone_info=AcquireImageInfo();
4460 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4461 composite_image=ReadInlineImage(clone_info,primitive_info->text,
cristy947cb4c2011-10-20 18:41:46 +00004462 exception);
cristy3ed852e2009-09-05 21:47:34 +00004463 else
4464 {
4465 (void) CopyMagickString(clone_info->filename,primitive_info->text,
4466 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00004467 composite_image=ReadImage(clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004468 }
4469 clone_info=DestroyImageInfo(clone_info);
4470 if (composite_image == (Image *) NULL)
4471 break;
4472 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4473 NULL,(void *) NULL);
cristybb503372010-05-27 20:51:26 +00004474 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4475 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4476 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4477 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
cristy3ed852e2009-09-05 21:47:34 +00004478 {
4479 char
4480 geometry[MaxTextExtent];
4481
4482 /*
4483 Resize image.
4484 */
cristyb51dff52011-05-19 16:55:47 +00004485 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
cristy3ed852e2009-09-05 21:47:34 +00004486 primitive_info[1].point.x,primitive_info[1].point.y);
4487 composite_image->filter=image->filter;
cristye941a752011-10-15 01:52:48 +00004488 (void) TransformImage(&composite_image,(char *) NULL,geometry,
4489 exception);
cristy3ed852e2009-09-05 21:47:34 +00004490 }
cristy8a46d822012-08-28 23:32:39 +00004491 if (composite_image->alpha_trait != BlendPixelTrait)
cristy63240882011-08-05 19:05:27 +00004492 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
4493 exception);
cristy4c08aed2011-07-01 19:47:50 +00004494 if (draw_info->alpha != OpaqueAlpha)
cristye941a752011-10-15 01:52:48 +00004495 (void) SetImageAlpha(composite_image,draw_info->alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00004496 SetGeometry(image,&geometry);
4497 image->gravity=draw_info->gravity;
4498 geometry.x=x;
4499 geometry.y=y;
cristyb51dff52011-05-19 16:55:47 +00004500 (void) FormatLocaleString(composite_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004501 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
cristye8c25f92010-06-03 00:53:06 +00004502 composite_image->rows,(double) geometry.x,(double) geometry.y);
cristy947cb4c2011-10-20 18:41:46 +00004503 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00004504 affine=draw_info->affine;
4505 affine.tx=(double) geometry.x;
4506 affine.ty=(double) geometry.y;
4507 composite_image->interpolate=image->interpolate;
cristyd5f3fc32011-04-26 14:44:36 +00004508 if (draw_info->compose == OverCompositeOp)
cristy947cb4c2011-10-20 18:41:46 +00004509 (void) DrawAffineImage(image,composite_image,&affine,exception);
cristyd5f3fc32011-04-26 14:44:36 +00004510 else
cristyfeb3e962012-03-29 17:25:55 +00004511 (void) CompositeImage(image,composite_image,draw_info->compose,
cristy39172402012-03-30 13:04:39 +00004512 MagickTrue,geometry.x,geometry.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00004513 composite_image=DestroyImage(composite_image);
4514 break;
4515 }
4516 default:
4517 {
cristya19f1d72012-08-07 18:24:38 +00004518 double
cristy3ed852e2009-09-05 21:47:34 +00004519 mid,
4520 scale;
4521
4522 DrawInfo
4523 *clone_info;
4524
4525 if (IsEventLogging() != MagickFalse)
4526 LogPrimitiveInfo(primitive_info);
4527 scale=ExpandAffine(&draw_info->affine);
4528 if ((draw_info->dash_pattern != (double *) NULL) &&
4529 (draw_info->dash_pattern[0] != 0.0) &&
cristy53aed702012-07-08 00:21:25 +00004530 ((scale*draw_info->stroke_width) >= MagickEpsilon) &&
cristy4c08aed2011-07-01 19:47:50 +00004531 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004532 {
4533 /*
4534 Draw dash polygon.
4535 */
4536 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4537 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004538 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004539 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4540 exception);
cristy3ed852e2009-09-05 21:47:34 +00004541 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004542 (void) DrawDashPolygon(draw_info,primitive_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004543 break;
4544 }
4545 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4546 if ((mid > 1.0) &&
cristy4c08aed2011-07-01 19:47:50 +00004547 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004548 {
4549 MagickBooleanType
4550 closed_path;
4551
4552 /*
4553 Draw strokes while respecting line cap/join attributes.
4554 */
4555 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4556 closed_path=
4557 (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4558 (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4559 MagickTrue : MagickFalse;
cristybb503372010-05-27 20:51:26 +00004560 i=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004561 if ((((draw_info->linecap == RoundCap) ||
4562 (closed_path != MagickFalse)) &&
4563 (draw_info->linejoin == RoundJoin)) ||
4564 (primitive_info[i].primitive != UndefinedPrimitive))
4565 {
cristy947cb4c2011-10-20 18:41:46 +00004566 (void) DrawPolygonPrimitive(image,draw_info,primitive_info,
4567 exception);
cristy3ed852e2009-09-05 21:47:34 +00004568 break;
4569 }
4570 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4571 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004572 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004573 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4574 exception);
cristy3ed852e2009-09-05 21:47:34 +00004575 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004576 status|=DrawStrokePolygon(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004577 break;
4578 }
cristy947cb4c2011-10-20 18:41:46 +00004579 status=DrawPolygonPrimitive(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004580 break;
4581 }
4582 }
4583 image_view=DestroyCacheView(image_view);
4584 if (image->debug != MagickFalse)
4585 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4586 return(status != 0 ? MagickTrue : MagickFalse);
4587}
4588
4589/*
4590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4591% %
4592% %
4593% %
4594+ D r a w S t r o k e P o l y g o n %
4595% %
4596% %
4597% %
4598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4599%
4600% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4601% the image while respecting the line cap and join attributes.
4602%
4603% The format of the DrawStrokePolygon method is:
4604%
4605% MagickBooleanType DrawStrokePolygon(Image *image,
4606% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4607%
4608% A description of each parameter follows:
4609%
4610% o image: the image.
4611%
4612% o draw_info: the draw info.
4613%
4614% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4615%
4616%
4617*/
4618
4619static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00004620 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004621{
4622 PrimitiveInfo
4623 linecap[5];
4624
cristybb503372010-05-27 20:51:26 +00004625 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004626 i;
4627
4628 for (i=0; i < 4; i++)
4629 linecap[i]=(*primitive_info);
4630 linecap[0].coordinates=4;
cristy53aed702012-07-08 00:21:25 +00004631 linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4632 linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4633 linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4634 linecap[3].point.y+=(double) (10.0*MagickEpsilon);
cristy3ed852e2009-09-05 21:47:34 +00004635 linecap[4].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00004636 (void) DrawPolygonPrimitive(image,draw_info,linecap,exception);
cristy3ed852e2009-09-05 21:47:34 +00004637}
4638
4639static MagickBooleanType DrawStrokePolygon(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004640 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4641 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004642{
4643 DrawInfo
4644 *clone_info;
4645
4646 MagickBooleanType
4647 closed_path,
4648 status;
4649
4650 PrimitiveInfo
4651 *stroke_polygon;
4652
4653 register const PrimitiveInfo
4654 *p,
4655 *q;
4656
4657 /*
4658 Draw stroked polygon.
4659 */
4660 if (image->debug != MagickFalse)
4661 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4662 " begin draw-stroke-polygon");
4663 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4664 clone_info->fill=draw_info->stroke;
cristy2d2d5622012-01-19 19:11:29 +00004665 if (clone_info->fill_pattern != (Image *) NULL)
4666 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
cristy2195b752012-01-19 16:04:04 +00004667 if (clone_info->stroke_pattern != (Image *) NULL)
cristy2d2d5622012-01-19 19:11:29 +00004668 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
4669 MagickTrue,exception);
cristy4c08aed2011-07-01 19:47:50 +00004670 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004671 clone_info->stroke_width=0.0;
4672 clone_info->fill_rule=NonZeroRule;
4673 status=MagickTrue;
4674 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4675 {
4676 stroke_polygon=TraceStrokePolygon(draw_info,p);
cristy947cb4c2011-10-20 18:41:46 +00004677 status=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00004678 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4679 q=p+p->coordinates-1;
4680 closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4681 MagickTrue : MagickFalse;
4682 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4683 {
cristy947cb4c2011-10-20 18:41:46 +00004684 DrawRoundLinecap(image,draw_info,p,exception);
4685 DrawRoundLinecap(image,draw_info,q,exception);
cristy3ed852e2009-09-05 21:47:34 +00004686 }
4687 }
4688 clone_info=DestroyDrawInfo(clone_info);
4689 if (image->debug != MagickFalse)
4690 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4691 " end draw-stroke-polygon");
4692 return(status);
4693}
4694
4695/*
4696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4697% %
4698% %
4699% %
4700% G e t A f f i n e M a t r i x %
4701% %
4702% %
4703% %
4704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4705%
4706% GetAffineMatrix() returns an AffineMatrix initialized to the identity
4707% matrix.
4708%
4709% The format of the GetAffineMatrix method is:
4710%
4711% void GetAffineMatrix(AffineMatrix *affine_matrix)
4712%
4713% A description of each parameter follows:
4714%
4715% o affine_matrix: the affine matrix.
4716%
4717*/
4718MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
4719{
4720 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4721 assert(affine_matrix != (AffineMatrix *) NULL);
4722 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4723 affine_matrix->sx=1.0;
4724 affine_matrix->sy=1.0;
4725}
4726
4727/*
4728%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4729% %
4730% %
4731% %
4732+ G e t D r a w I n f o %
4733% %
4734% %
4735% %
4736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4737%
anthony9c88e8f2011-09-29 11:31:53 +00004738% GetDrawInfo() initializes draw_info to default values from image_info.
cristy3ed852e2009-09-05 21:47:34 +00004739%
4740% The format of the GetDrawInfo method is:
4741%
4742% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4743%
4744% A description of each parameter follows:
4745%
4746% o image_info: the image info..
4747%
4748% o draw_info: the draw info.
4749%
4750*/
4751MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4752{
4753 const char
4754 *option;
4755
4756 ExceptionInfo
4757 *exception;
4758
cristy3ed852e2009-09-05 21:47:34 +00004759 /*
4760 Initialize draw attributes.
4761 */
4762 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4763 assert(draw_info != (DrawInfo *) NULL);
4764 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +00004765 GetAffineMatrix(&draw_info->affine);
4766 exception=AcquireExceptionInfo();
cristyfad60c92012-01-19 18:32:39 +00004767 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
cristy9950d572011-10-01 18:22:35 +00004768 exception);
cristyfad60c92012-01-19 18:32:39 +00004769 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
cristy9950d572011-10-01 18:22:35 +00004770 exception);
cristy3ed852e2009-09-05 21:47:34 +00004771 draw_info->stroke_width=1.0;
cristy4c08aed2011-07-01 19:47:50 +00004772 draw_info->alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004773 draw_info->fill_rule=EvenOddRule;
4774 draw_info->linecap=ButtCap;
4775 draw_info->linejoin=MiterJoin;
4776 draw_info->miterlimit=10;
4777 draw_info->decorate=NoDecoration;
cristy3ed852e2009-09-05 21:47:34 +00004778 draw_info->pointsize=12.0;
cristy4c08aed2011-07-01 19:47:50 +00004779 draw_info->undercolor.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004780 draw_info->compose=OverCompositeOp;
cristy3ed852e2009-09-05 21:47:34 +00004781 draw_info->render=MagickTrue;
4782 draw_info->debug=IsEventLogging();
cristy0245b5d2011-10-05 12:03:49 +00004783 if (image_info != (ImageInfo *) NULL)
4784 {
4785 draw_info->stroke_antialias=image_info->antialias;
4786 if (image_info->font != (char *) NULL)
4787 draw_info->font=AcquireString(image_info->font);
4788 if (image_info->density != (char *) NULL)
4789 draw_info->density=AcquireString(image_info->density);
4790 draw_info->text_antialias=image_info->antialias;
4791 if (image_info->pointsize != 0.0)
4792 draw_info->pointsize=image_info->pointsize;
4793 draw_info->border_color=image_info->border_color;
4794 if (image_info->server_name != (char *) NULL)
4795 draw_info->server_name=AcquireString(image_info->server_name);
4796 option=GetImageOption(image_info,"encoding");
4797 if (option != (const char *) NULL)
4798 (void) CloneString(&draw_info->encoding,option);
4799 option=GetImageOption(image_info,"kerning");
4800 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004801 draw_info->kerning=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004802 option=GetImageOption(image_info,"interline-spacing");
4803 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004804 draw_info->interline_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004805 option=GetImageOption(image_info,"interword-spacing");
4806 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004807 draw_info->interword_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004808 option=GetImageOption(image_info,"direction");
4809 if (option != (const char *) NULL)
4810 draw_info->direction=(DirectionType) ParseCommandOption(
4811 MagickDirectionOptions,MagickFalse,option);
anthony42f6c202011-10-23 10:28:55 +00004812 else
4813 draw_info->direction=UndefinedDirection;
cristy0245b5d2011-10-05 12:03:49 +00004814 option=GetImageOption(image_info,"fill");
4815 if (option != (const char *) NULL)
4816 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
4817 exception);
4818 option=GetImageOption(image_info,"stroke");
4819 if (option != (const char *) NULL)
4820 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
4821 exception);
4822 option=GetImageOption(image_info,"strokewidth");
4823 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004824 draw_info->stroke_width=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004825 option=GetImageOption(image_info,"undercolor");
4826 if (option != (const char *) NULL)
4827 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
4828 exception);
4829 option=GetImageOption(image_info,"gravity");
4830 if (option != (const char *) NULL)
4831 draw_info->gravity=(GravityType) ParseCommandOption(
4832 MagickGravityOptions,MagickFalse,option);
4833 }
cristy3ed852e2009-09-05 21:47:34 +00004834 exception=DestroyExceptionInfo(exception);
4835 draw_info->signature=MagickSignature;
cristy3ed852e2009-09-05 21:47:34 +00004836}
4837
4838/*
4839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4840% %
4841% %
4842% %
4843+ P e r m u t a t e %
4844% %
4845% %
4846% %
4847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4848%
4849% Permutate() returns the permuation of the (n,k).
4850%
4851% The format of the Permutate method is:
4852%
cristybb503372010-05-27 20:51:26 +00004853% void Permutate(ssize_t n,ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004854%
4855% A description of each parameter follows:
4856%
4857% o n:
4858%
4859% o k:
4860%
4861%
4862*/
cristya19f1d72012-08-07 18:24:38 +00004863static inline double Permutate(const ssize_t n,const ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004864{
cristya19f1d72012-08-07 18:24:38 +00004865 double
cristy3ed852e2009-09-05 21:47:34 +00004866 r;
4867
cristybb503372010-05-27 20:51:26 +00004868 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004869 i;
4870
4871 r=1.0;
4872 for (i=k+1; i <= n; i++)
4873 r*=i;
4874 for (i=1; i <= (n-k); i++)
4875 r/=i;
4876 return(r);
4877}
4878
4879/*
4880%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4881% %
4882% %
4883% %
4884+ T r a c e P r i m i t i v e %
4885% %
4886% %
4887% %
4888%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4889%
4890% TracePrimitive is a collection of methods for generating graphic
4891% primitives such as arcs, ellipses, paths, etc.
4892%
4893*/
4894
4895static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4896 const PointInfo end,const PointInfo degrees)
4897{
4898 PointInfo
4899 center,
4900 radii;
4901
4902 center.x=0.5*(end.x+start.x);
4903 center.y=0.5*(end.y+start.y);
4904 radii.x=fabs(center.x-start.x);
4905 radii.y=fabs(center.y-start.y);
4906 TraceEllipse(primitive_info,center,radii,degrees);
4907}
4908
4909static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
cristya19f1d72012-08-07 18:24:38 +00004910 const PointInfo end,const PointInfo arc,const double angle,
cristy3ed852e2009-09-05 21:47:34 +00004911 const MagickBooleanType large_arc,const MagickBooleanType sweep)
4912{
cristya19f1d72012-08-07 18:24:38 +00004913 double
cristy3ed852e2009-09-05 21:47:34 +00004914 alpha,
4915 beta,
4916 delta,
4917 factor,
4918 gamma,
4919 theta;
4920
4921 PointInfo
4922 center,
4923 points[3],
4924 radii;
4925
cristya19f1d72012-08-07 18:24:38 +00004926 register double
cristy3ed852e2009-09-05 21:47:34 +00004927 cosine,
4928 sine;
4929
4930 register PrimitiveInfo
4931 *p;
4932
cristybb503372010-05-27 20:51:26 +00004933 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004934 i;
4935
cristybb503372010-05-27 20:51:26 +00004936 size_t
cristy3ed852e2009-09-05 21:47:34 +00004937 arc_segments;
4938
4939 if ((start.x == end.x) && (start.y == end.y))
4940 {
4941 TracePoint(primitive_info,end);
4942 return;
4943 }
4944 radii.x=fabs(arc.x);
4945 radii.y=fabs(arc.y);
4946 if ((radii.x == 0.0) || (radii.y == 0.0))
4947 {
4948 TraceLine(primitive_info,start,end);
4949 return;
4950 }
4951 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
4952 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
4953 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
4954 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
4955 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
4956 (radii.y*radii.y);
cristy53aed702012-07-08 00:21:25 +00004957 if (delta < MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +00004958 {
4959 TraceLine(primitive_info,start,end);
4960 return;
4961 }
4962 if (delta > 1.0)
4963 {
4964 radii.x*=sqrt((double) delta);
4965 radii.y*=sqrt((double) delta);
4966 }
4967 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
4968 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
4969 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
4970 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
4971 alpha=points[1].x-points[0].x;
4972 beta=points[1].y-points[0].y;
cristy3e3ec3a2012-11-03 23:11:06 +00004973 factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25;
cristy3ed852e2009-09-05 21:47:34 +00004974 if (factor <= 0.0)
4975 factor=0.0;
4976 else
4977 {
4978 factor=sqrt((double) factor);
4979 if (sweep == large_arc)
4980 factor=(-factor);
4981 }
4982 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
4983 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
4984 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
4985 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
4986 if ((theta < 0.0) && (sweep != MagickFalse))
cristya19f1d72012-08-07 18:24:38 +00004987 theta+=(double) (2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00004988 else
4989 if ((theta > 0.0) && (sweep == MagickFalse))
cristya19f1d72012-08-07 18:24:38 +00004990 theta-=(double) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00004991 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
cristy53aed702012-07-08 00:21:25 +00004992 MagickEpsilon))));
cristy3ed852e2009-09-05 21:47:34 +00004993 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00004994 for (i=0; i < (ssize_t) arc_segments; i++)
cristy3ed852e2009-09-05 21:47:34 +00004995 {
4996 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
4997 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
4998 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
4999 sin(fmod((double) beta,DegreesToRadians(360.0)));
5000 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
5001 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
5002 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5003 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
5004 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
5005 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5006 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
5007 theta/arc_segments),DegreesToRadians(360.0))));
5008 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
5009 theta/arc_segments),DegreesToRadians(360.0))));
5010 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
5011 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5012 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
5013 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5014 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
5015 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
5016 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
5017 points[0].y);
5018 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
5019 points[0].y);
5020 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
5021 points[1].y);
5022 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
5023 points[1].y);
5024 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
5025 points[2].y);
5026 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
5027 points[2].y);
cristybb503372010-05-27 20:51:26 +00005028 if (i == (ssize_t) (arc_segments-1))
cristy3ed852e2009-09-05 21:47:34 +00005029 (p+3)->point=end;
5030 TraceBezier(p,4);
5031 p+=p->coordinates;
5032 }
cristybb503372010-05-27 20:51:26 +00005033 primitive_info->coordinates=(size_t) (p-primitive_info);
5034 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005035 {
5036 p->primitive=primitive_info->primitive;
5037 p--;
5038 }
5039}
5040
5041static void TraceBezier(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005042 const size_t number_coordinates)
cristy3ed852e2009-09-05 21:47:34 +00005043{
cristya19f1d72012-08-07 18:24:38 +00005044 double
cristy3ed852e2009-09-05 21:47:34 +00005045 alpha,
5046 *coefficients,
5047 weight;
5048
5049 PointInfo
5050 end,
5051 point,
5052 *points;
5053
cristy826a5472010-08-31 23:21:38 +00005054 register PrimitiveInfo
5055 *p;
5056
cristybb503372010-05-27 20:51:26 +00005057 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005058 i,
5059 j;
5060
cristybb503372010-05-27 20:51:26 +00005061 size_t
cristy3ed852e2009-09-05 21:47:34 +00005062 control_points,
5063 quantum;
5064
5065 /*
5066 Allocate coeficients.
5067 */
5068 quantum=number_coordinates;
cristybb503372010-05-27 20:51:26 +00005069 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005070 {
cristybb503372010-05-27 20:51:26 +00005071 for (j=i+1; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005072 {
5073 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
cristya19f1d72012-08-07 18:24:38 +00005074 if (alpha > (double) quantum)
cristybb503372010-05-27 20:51:26 +00005075 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005076 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
cristya19f1d72012-08-07 18:24:38 +00005077 if (alpha > (double) quantum)
cristybb503372010-05-27 20:51:26 +00005078 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005079 }
5080 }
cristybb503372010-05-27 20:51:26 +00005081 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
cristy3ed852e2009-09-05 21:47:34 +00005082 (double) BezierQuantum);
5083 control_points=quantum*number_coordinates;
cristya19f1d72012-08-07 18:24:38 +00005084 coefficients=(double *) AcquireQuantumMemory((size_t)
cristy3ed852e2009-09-05 21:47:34 +00005085 number_coordinates,sizeof(*coefficients));
5086 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5087 sizeof(*points));
cristya19f1d72012-08-07 18:24:38 +00005088 if ((coefficients == (double *) NULL) ||
cristy3ed852e2009-09-05 21:47:34 +00005089 (points == (PointInfo *) NULL))
5090 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5091 /*
5092 Compute bezier points.
5093 */
5094 end=primitive_info[number_coordinates-1].point;
cristybb503372010-05-27 20:51:26 +00005095 for (i=0; i < (ssize_t) number_coordinates; i++)
5096 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
cristy3ed852e2009-09-05 21:47:34 +00005097 weight=0.0;
cristybb503372010-05-27 20:51:26 +00005098 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005099 {
5100 p=primitive_info;
5101 point.x=0.0;
5102 point.y=0.0;
5103 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
cristybb503372010-05-27 20:51:26 +00005104 for (j=0; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005105 {
5106 point.x+=alpha*coefficients[j]*p->point.x;
5107 point.y+=alpha*coefficients[j]*p->point.y;
5108 alpha*=weight/(1.0-weight);
5109 p++;
5110 }
5111 points[i]=point;
5112 weight+=1.0/control_points;
5113 }
5114 /*
5115 Bezier curves are just short segmented polys.
5116 */
5117 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005118 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005119 {
5120 TracePoint(p,points[i]);
5121 p+=p->coordinates;
5122 }
5123 TracePoint(p,end);
5124 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005125 primitive_info->coordinates=(size_t) (p-primitive_info);
5126 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005127 {
5128 p->primitive=primitive_info->primitive;
5129 p--;
5130 }
5131 points=(PointInfo *) RelinquishMagickMemory(points);
cristya19f1d72012-08-07 18:24:38 +00005132 coefficients=(double *) RelinquishMagickMemory(coefficients);
cristy3ed852e2009-09-05 21:47:34 +00005133}
5134
5135static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5136 const PointInfo end)
5137{
cristya19f1d72012-08-07 18:24:38 +00005138 double
cristy3ed852e2009-09-05 21:47:34 +00005139 alpha,
5140 beta,
5141 radius;
5142
5143 PointInfo
5144 offset,
5145 degrees;
5146
5147 alpha=end.x-start.x;
5148 beta=end.y-start.y;
5149 radius=hypot((double) alpha,(double) beta);
5150 offset.x=(double) radius;
5151 offset.y=(double) radius;
5152 degrees.x=0.0;
5153 degrees.y=360.0;
5154 TraceEllipse(primitive_info,start,offset,degrees);
5155}
5156
5157static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5158 const PointInfo stop,const PointInfo degrees)
5159{
cristya19f1d72012-08-07 18:24:38 +00005160 double
cristy3ed852e2009-09-05 21:47:34 +00005161 delta,
5162 step,
5163 y;
5164
5165 PointInfo
5166 angle,
5167 point;
5168
5169 register PrimitiveInfo
5170 *p;
5171
cristybb503372010-05-27 20:51:26 +00005172 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005173 i;
5174
5175 /*
5176 Ellipses are just short segmented polys.
5177 */
5178 if ((stop.x == 0.0) && (stop.y == 0.0))
5179 {
5180 TracePoint(primitive_info,start);
5181 return;
5182 }
5183 delta=2.0/MagickMax(stop.x,stop.y);
cristya19f1d72012-08-07 18:24:38 +00005184 step=(double) (MagickPI/8.0);
5185 if ((delta >= 0.0) && (delta < (double) (MagickPI/8.0)))
5186 step=(double) (MagickPI/(4*(MagickPI/delta/2+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00005187 angle.x=DegreesToRadians(degrees.x);
5188 y=degrees.y;
5189 while (y < degrees.x)
5190 y+=360.0;
cristya855f3d2012-12-01 13:02:13 +00005191 angle.y=(double) DegreesToRadians(y);
cristy3ed852e2009-09-05 21:47:34 +00005192 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5193 {
5194 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5195 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5196 TracePoint(p,point);
5197 p+=p->coordinates;
5198 }
5199 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5200 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5201 TracePoint(p,point);
5202 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005203 primitive_info->coordinates=(size_t) (p-primitive_info);
5204 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005205 {
5206 p->primitive=primitive_info->primitive;
5207 p--;
5208 }
5209}
5210
5211static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5212 const PointInfo end)
5213{
5214 TracePoint(primitive_info,start);
cristy53aed702012-07-08 00:21:25 +00005215 if ((fabs(start.x-end.x) < MagickEpsilon) &&
5216 (fabs(start.y-end.y) < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +00005217 {
5218 primitive_info->primitive=PointPrimitive;
5219 primitive_info->coordinates=1;
5220 return;
5221 }
5222 TracePoint(primitive_info+1,end);
5223 (primitive_info+1)->primitive=primitive_info->primitive;
5224 primitive_info->coordinates=2;
5225}
5226
cristybb503372010-05-27 20:51:26 +00005227static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00005228{
5229 char
5230 token[MaxTextExtent];
5231
5232 const char
5233 *p;
5234
5235 int
5236 attribute,
5237 last_attribute;
5238
cristya19f1d72012-08-07 18:24:38 +00005239 double
cristy3ed852e2009-09-05 21:47:34 +00005240 x,
5241 y;
5242
5243 PointInfo
5244 end,
5245 points[4],
5246 point,
5247 start;
5248
5249 PrimitiveType
5250 primitive_type;
5251
5252 register PrimitiveInfo
5253 *q;
5254
cristybb503372010-05-27 20:51:26 +00005255 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005256 i;
5257
cristybb503372010-05-27 20:51:26 +00005258 size_t
cristy3ed852e2009-09-05 21:47:34 +00005259 number_coordinates,
5260 z_count;
5261
5262 attribute=0;
5263 point.x=0.0;
5264 point.y=0.0;
5265 start.x=0.0;
5266 start.y=0.0;
5267 number_coordinates=0;
5268 z_count=0;
5269 primitive_type=primitive_info->primitive;
5270 q=primitive_info;
5271 for (p=path; *p != '\0'; )
5272 {
5273 while (isspace((int) ((unsigned char) *p)) != 0)
5274 p++;
5275 if (*p == '\0')
5276 break;
5277 last_attribute=attribute;
5278 attribute=(int) (*p++);
5279 switch (attribute)
5280 {
5281 case 'a':
5282 case 'A':
5283 {
5284 MagickBooleanType
5285 large_arc,
5286 sweep;
5287
cristya19f1d72012-08-07 18:24:38 +00005288 double
cristy3ed852e2009-09-05 21:47:34 +00005289 angle;
5290
5291 PointInfo
5292 arc;
5293
5294 /*
5295 Compute arc points.
5296 */
5297 do
5298 {
5299 GetMagickToken(p,&p,token);
5300 if (*token == ',')
5301 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005302 arc.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005303 GetMagickToken(p,&p,token);
5304 if (*token == ',')
5305 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005306 arc.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005307 GetMagickToken(p,&p,token);
5308 if (*token == ',')
5309 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005310 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005311 GetMagickToken(p,&p,token);
5312 if (*token == ',')
5313 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005314 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005315 GetMagickToken(p,&p,token);
5316 if (*token == ',')
5317 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005318 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005319 GetMagickToken(p,&p,token);
5320 if (*token == ',')
5321 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005322 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005323 GetMagickToken(p,&p,token);
5324 if (*token == ',')
5325 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005326 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005327 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5328 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5329 TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5330 q+=q->coordinates;
5331 point=end;
cristy671fcfc2011-12-13 13:28:31 +00005332 while (isspace((int) ((unsigned char) *p)) != 0)
5333 p++;
5334 if (*p == ',')
5335 p++;
cristy3ed852e2009-09-05 21:47:34 +00005336 } while (IsPoint(p) != MagickFalse);
5337 break;
5338 }
5339 case 'c':
5340 case 'C':
5341 {
5342 /*
5343 Compute bezier points.
5344 */
5345 do
5346 {
5347 points[0]=point;
5348 for (i=1; i < 4; i++)
5349 {
5350 GetMagickToken(p,&p,token);
5351 if (*token == ',')
5352 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005353 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005354 GetMagickToken(p,&p,token);
5355 if (*token == ',')
5356 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005357 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005358 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5359 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5360 points[i]=end;
5361 }
5362 for (i=0; i < 4; i++)
5363 (q+i)->point=points[i];
5364 TraceBezier(q,4);
5365 q+=q->coordinates;
5366 point=end;
5367 } while (IsPoint(p) != MagickFalse);
5368 break;
5369 }
5370 case 'H':
5371 case 'h':
5372 {
5373 do
5374 {
5375 GetMagickToken(p,&p,token);
5376 if (*token == ',')
5377 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005378 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005379 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
5380 TracePoint(q,point);
5381 q+=q->coordinates;
5382 } while (IsPoint(p) != MagickFalse);
5383 break;
5384 }
5385 case 'l':
5386 case 'L':
5387 {
5388 do
5389 {
5390 GetMagickToken(p,&p,token);
5391 if (*token == ',')
5392 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005393 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005394 GetMagickToken(p,&p,token);
5395 if (*token == ',')
5396 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005397 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005398 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
5399 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
5400 TracePoint(q,point);
5401 q+=q->coordinates;
5402 } while (IsPoint(p) != MagickFalse);
5403 break;
5404 }
5405 case 'M':
5406 case 'm':
5407 {
5408 if (q != primitive_info)
5409 {
cristybb503372010-05-27 20:51:26 +00005410 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005411 number_coordinates+=primitive_info->coordinates;
5412 primitive_info=q;
5413 }
5414 i=0;
5415 do
5416 {
5417 GetMagickToken(p,&p,token);
5418 if (*token == ',')
5419 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005420 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005421 GetMagickToken(p,&p,token);
5422 if (*token == ',')
5423 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005424 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005425 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
5426 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
5427 if (i == 0)
5428 start=point;
5429 i++;
5430 TracePoint(q,point);
5431 q+=q->coordinates;
cristy826a5472010-08-31 23:21:38 +00005432 if ((i != 0) && (attribute == (int) 'M'))
cristy3ed852e2009-09-05 21:47:34 +00005433 {
5434 TracePoint(q,point);
5435 q+=q->coordinates;
5436 }
5437 } while (IsPoint(p) != MagickFalse);
5438 break;
5439 }
5440 case 'q':
5441 case 'Q':
5442 {
5443 /*
5444 Compute bezier points.
5445 */
5446 do
5447 {
5448 points[0]=point;
5449 for (i=1; i < 3; i++)
5450 {
5451 GetMagickToken(p,&p,token);
5452 if (*token == ',')
5453 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005454 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005455 GetMagickToken(p,&p,token);
5456 if (*token == ',')
5457 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005458 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005459 if (*p == ',')
5460 p++;
5461 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
5462 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
5463 points[i]=end;
5464 }
5465 for (i=0; i < 3; i++)
5466 (q+i)->point=points[i];
5467 TraceBezier(q,3);
5468 q+=q->coordinates;
5469 point=end;
5470 } while (IsPoint(p) != MagickFalse);
5471 break;
5472 }
5473 case 's':
5474 case 'S':
5475 {
5476 /*
5477 Compute bezier points.
5478 */
5479 do
5480 {
5481 points[0]=points[3];
5482 points[1].x=2.0*points[3].x-points[2].x;
5483 points[1].y=2.0*points[3].y-points[2].y;
5484 for (i=2; i < 4; i++)
5485 {
5486 GetMagickToken(p,&p,token);
5487 if (*token == ',')
5488 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005489 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005490 GetMagickToken(p,&p,token);
5491 if (*token == ',')
5492 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005493 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005494 if (*p == ',')
5495 p++;
5496 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
5497 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
5498 points[i]=end;
5499 }
5500 if (strchr("CcSs",last_attribute) == (char *) NULL)
5501 {
5502 points[0]=points[2];
5503 points[1]=points[3];
5504 }
5505 for (i=0; i < 4; i++)
5506 (q+i)->point=points[i];
5507 TraceBezier(q,4);
5508 q+=q->coordinates;
5509 point=end;
5510 } while (IsPoint(p) != MagickFalse);
5511 break;
5512 }
5513 case 't':
5514 case 'T':
5515 {
5516 /*
5517 Compute bezier points.
5518 */
5519 do
5520 {
5521 points[0]=points[2];
5522 points[1].x=2.0*points[2].x-points[1].x;
5523 points[1].y=2.0*points[2].y-points[1].y;
5524 for (i=2; i < 3; i++)
5525 {
5526 GetMagickToken(p,&p,token);
5527 if (*token == ',')
5528 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005529 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005530 GetMagickToken(p,&p,token);
5531 if (*token == ',')
5532 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005533 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005534 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
5535 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
5536 points[i]=end;
5537 }
5538 if (strchr("QqTt",last_attribute) == (char *) NULL)
5539 {
5540 points[0]=points[2];
5541 points[1]=points[3];
5542 }
5543 for (i=0; i < 3; i++)
5544 (q+i)->point=points[i];
5545 TraceBezier(q,3);
5546 q+=q->coordinates;
5547 point=end;
5548 } while (IsPoint(p) != MagickFalse);
5549 break;
5550 }
5551 case 'v':
5552 case 'V':
5553 {
5554 do
5555 {
5556 GetMagickToken(p,&p,token);
5557 if (*token == ',')
5558 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005559 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005560 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
5561 TracePoint(q,point);
5562 q+=q->coordinates;
5563 } while (IsPoint(p) != MagickFalse);
5564 break;
5565 }
5566 case 'z':
5567 case 'Z':
5568 {
5569 point=start;
5570 TracePoint(q,point);
5571 q+=q->coordinates;
cristybb503372010-05-27 20:51:26 +00005572 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005573 number_coordinates+=primitive_info->coordinates;
5574 primitive_info=q;
5575 z_count++;
5576 break;
5577 }
5578 default:
5579 {
5580 if (isalpha((int) ((unsigned char) attribute)) != 0)
cristy1e604812011-05-19 18:07:50 +00005581 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n",
5582 attribute);
cristy3ed852e2009-09-05 21:47:34 +00005583 break;
5584 }
5585 }
5586 }
cristybb503372010-05-27 20:51:26 +00005587 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005588 number_coordinates+=primitive_info->coordinates;
cristybb503372010-05-27 20:51:26 +00005589 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005590 {
5591 q--;
5592 q->primitive=primitive_type;
5593 if (z_count > 1)
5594 q->method=FillToBorderMethod;
5595 }
5596 q=primitive_info;
5597 return(number_coordinates);
5598}
5599
5600static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
5601 const PointInfo end)
5602{
5603 PointInfo
5604 point;
5605
5606 register PrimitiveInfo
5607 *p;
5608
cristybb503372010-05-27 20:51:26 +00005609 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005610 i;
5611
5612 p=primitive_info;
5613 TracePoint(p,start);
5614 p+=p->coordinates;
5615 point.x=start.x;
5616 point.y=end.y;
5617 TracePoint(p,point);
5618 p+=p->coordinates;
5619 TracePoint(p,end);
5620 p+=p->coordinates;
5621 point.x=end.x;
5622 point.y=start.y;
5623 TracePoint(p,point);
5624 p+=p->coordinates;
5625 TracePoint(p,start);
5626 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005627 primitive_info->coordinates=(size_t) (p-primitive_info);
5628 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005629 {
5630 p->primitive=primitive_info->primitive;
5631 p--;
5632 }
5633}
5634
5635static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
5636 const PointInfo start,const PointInfo end,PointInfo arc)
5637{
5638 PointInfo
5639 degrees,
5640 offset,
5641 point;
5642
5643 register PrimitiveInfo
5644 *p;
5645
cristybb503372010-05-27 20:51:26 +00005646 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005647 i;
5648
5649 p=primitive_info;
5650 offset.x=fabs(end.x-start.x);
5651 offset.y=fabs(end.y-start.y);
5652 if (arc.x > (0.5*offset.x))
5653 arc.x=0.5*offset.x;
5654 if (arc.y > (0.5*offset.y))
5655 arc.y=0.5*offset.y;
5656 point.x=start.x+offset.x-arc.x;
5657 point.y=start.y+arc.y;
5658 degrees.x=270.0;
5659 degrees.y=360.0;
5660 TraceEllipse(p,point,arc,degrees);
5661 p+=p->coordinates;
5662 point.x=start.x+offset.x-arc.x;
5663 point.y=start.y+offset.y-arc.y;
5664 degrees.x=0.0;
5665 degrees.y=90.0;
5666 TraceEllipse(p,point,arc,degrees);
5667 p+=p->coordinates;
5668 point.x=start.x+arc.x;
5669 point.y=start.y+offset.y-arc.y;
5670 degrees.x=90.0;
5671 degrees.y=180.0;
5672 TraceEllipse(p,point,arc,degrees);
5673 p+=p->coordinates;
5674 point.x=start.x+arc.x;
5675 point.y=start.y+arc.y;
5676 degrees.x=180.0;
5677 degrees.y=270.0;
5678 TraceEllipse(p,point,arc,degrees);
5679 p+=p->coordinates;
5680 TracePoint(p,primitive_info->point);
5681 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005682 primitive_info->coordinates=(size_t) (p-primitive_info);
5683 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005684 {
5685 p->primitive=primitive_info->primitive;
5686 p--;
5687 }
5688}
5689
5690static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
cristya19f1d72012-08-07 18:24:38 +00005691 const size_t number_vertices,const double offset)
cristy3ed852e2009-09-05 21:47:34 +00005692{
cristya19f1d72012-08-07 18:24:38 +00005693 double
cristy3ed852e2009-09-05 21:47:34 +00005694 distance;
5695
cristya19f1d72012-08-07 18:24:38 +00005696 register double
cristy3ed852e2009-09-05 21:47:34 +00005697 dx,
5698 dy;
5699
cristybb503372010-05-27 20:51:26 +00005700 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005701 i;
5702
cristy826a5472010-08-31 23:21:38 +00005703 ssize_t
5704 j;
5705
cristy3ed852e2009-09-05 21:47:34 +00005706 dx=0.0;
5707 dy=0.0;
cristybb503372010-05-27 20:51:26 +00005708 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005709 {
5710 dx=primitive_info[0].point.x-primitive_info[i].point.x;
5711 dy=primitive_info[0].point.y-primitive_info[i].point.y;
cristy53aed702012-07-08 00:21:25 +00005712 if ((fabs((double) dx) >= MagickEpsilon) ||
5713 (fabs((double) dy) >= MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +00005714 break;
5715 }
cristybb503372010-05-27 20:51:26 +00005716 if (i == (ssize_t) number_vertices)
5717 i=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005718 distance=hypot((double) dx,(double) dy);
5719 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
5720 dx*(distance+offset)/distance);
5721 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
5722 dy*(distance+offset)/distance);
cristybb503372010-05-27 20:51:26 +00005723 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00005724 {
5725 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
5726 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
cristy53aed702012-07-08 00:21:25 +00005727 if ((fabs((double) dx) >= MagickEpsilon) ||
5728 (fabs((double) dy) >= MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +00005729 break;
5730 }
5731 distance=hypot((double) dx,(double) dy);
5732 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
5733 dx*(distance+offset)/distance);
5734 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
5735 dy*(distance+offset)/distance);
5736}
5737
cristya19f1d72012-08-07 18:24:38 +00005738static inline double DrawEpsilonReciprocal(const double x)
cristy53aed702012-07-08 00:21:25 +00005739{
cristya19f1d72012-08-07 18:24:38 +00005740#define DrawEpsilon ((double) 1.0e-10)
cristy53aed702012-07-08 00:21:25 +00005741
cristya19f1d72012-08-07 18:24:38 +00005742 double sign = x < (double) 0.0 ? (double) -1.0 :
5743 (double) 1.0;
5744 return((sign*x) >= DrawEpsilon ? (double) 1.0/x : sign*(
5745 (double) 1.0/DrawEpsilon));
cristy53aed702012-07-08 00:21:25 +00005746}
5747
cristy3ed852e2009-09-05 21:47:34 +00005748static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
5749 const PrimitiveInfo *primitive_info)
5750{
5751 typedef struct _LineSegment
5752 {
5753 double
5754 p,
5755 q;
5756 } LineSegment;
5757
5758 LineSegment
5759 dx,
5760 dy,
5761 inverse_slope,
5762 slope,
5763 theta;
5764
cristy3ed852e2009-09-05 21:47:34 +00005765 MagickBooleanType
5766 closed_path;
5767
cristya19f1d72012-08-07 18:24:38 +00005768 double
cristy3ed852e2009-09-05 21:47:34 +00005769 delta_theta,
5770 dot_product,
5771 mid,
5772 miterlimit;
5773
5774 PointInfo
5775 box_p[5],
5776 box_q[5],
5777 center,
5778 offset,
5779 *path_p,
5780 *path_q;
5781
5782 PrimitiveInfo
5783 *polygon_primitive,
5784 *stroke_polygon;
5785
cristybb503372010-05-27 20:51:26 +00005786 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005787 i;
5788
cristybb503372010-05-27 20:51:26 +00005789 size_t
cristy3ed852e2009-09-05 21:47:34 +00005790 arc_segments,
5791 max_strokes,
5792 number_vertices;
5793
cristy826a5472010-08-31 23:21:38 +00005794 ssize_t
5795 j,
5796 n,
5797 p,
5798 q;
5799
cristy3ed852e2009-09-05 21:47:34 +00005800 /*
5801 Allocate paths.
5802 */
5803 number_vertices=primitive_info->coordinates;
5804 max_strokes=2*number_vertices+6*BezierQuantum+360;
5805 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5806 sizeof(*path_p));
5807 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5808 sizeof(*path_q));
5809 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
5810 number_vertices+2UL,sizeof(*polygon_primitive));
5811 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
5812 (polygon_primitive == (PrimitiveInfo *) NULL))
5813 return((PrimitiveInfo *) NULL);
5814 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
5815 number_vertices*sizeof(*polygon_primitive));
5816 closed_path=
5817 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
5818 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
5819 MagickTrue : MagickFalse;
5820 if ((draw_info->linejoin == RoundJoin) ||
5821 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
5822 {
5823 polygon_primitive[number_vertices]=primitive_info[1];
5824 number_vertices++;
5825 }
5826 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
5827 /*
5828 Compute the slope for the first line segment, p.
5829 */
5830 dx.p=0.0;
5831 dy.p=0.0;
cristybb503372010-05-27 20:51:26 +00005832 for (n=1; n < (ssize_t) number_vertices; n++)
cristy3ed852e2009-09-05 21:47:34 +00005833 {
5834 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
5835 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
cristy53aed702012-07-08 00:21:25 +00005836 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +00005837 break;
5838 }
cristybb503372010-05-27 20:51:26 +00005839 if (n == (ssize_t) number_vertices)
5840 n=(ssize_t) number_vertices-1L;
cristy66fce5b2012-06-29 15:58:17 +00005841 slope.p=DrawEpsilonReciprocal(dx.p)*dy.p;
5842 inverse_slope.p=(-1.0*DrawEpsilonReciprocal(slope.p));
cristy3ed852e2009-09-05 21:47:34 +00005843 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
cristya19f1d72012-08-07 18:24:38 +00005844 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*
cristy3ed852e2009-09-05 21:47:34 +00005845 mid*mid);
5846 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
5847 TraceSquareLinecap(polygon_primitive,number_vertices,mid);
5848 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
5849 offset.y=(double) (offset.x*inverse_slope.p);
5850 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
5851 {
5852 box_p[0].x=polygon_primitive[0].point.x-offset.x;
5853 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
5854 box_p[1].x=polygon_primitive[n].point.x-offset.x;
5855 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
5856 box_q[0].x=polygon_primitive[0].point.x+offset.x;
5857 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
5858 box_q[1].x=polygon_primitive[n].point.x+offset.x;
5859 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
5860 }
5861 else
5862 {
5863 box_p[0].x=polygon_primitive[0].point.x+offset.x;
5864 box_p[0].y=polygon_primitive[0].point.y+offset.y;
5865 box_p[1].x=polygon_primitive[n].point.x+offset.x;
5866 box_p[1].y=polygon_primitive[n].point.y+offset.y;
5867 box_q[0].x=polygon_primitive[0].point.x-offset.x;
5868 box_q[0].y=polygon_primitive[0].point.y-offset.y;
5869 box_q[1].x=polygon_primitive[n].point.x-offset.x;
5870 box_q[1].y=polygon_primitive[n].point.y-offset.y;
5871 }
5872 /*
5873 Create strokes for the line join attribute: bevel, miter, round.
5874 */
5875 p=0;
5876 q=0;
5877 path_q[p++]=box_q[0];
5878 path_p[q++]=box_p[0];
cristybb503372010-05-27 20:51:26 +00005879 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005880 {
5881 /*
5882 Compute the slope for this line segment, q.
5883 */
5884 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
5885 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
5886 dot_product=dx.q*dx.q+dy.q*dy.q;
5887 if (dot_product < 0.25)
5888 continue;
cristy66fce5b2012-06-29 15:58:17 +00005889 slope.q=DrawEpsilonReciprocal(dx.q)*dy.q;
5890 inverse_slope.q=(-1.0*DrawEpsilonReciprocal(slope.q));
cristy3ed852e2009-09-05 21:47:34 +00005891 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
5892 offset.y=(double) (offset.x*inverse_slope.q);
5893 dot_product=dy.q*offset.x-dx.q*offset.y;
5894 if (dot_product > 0.0)
5895 {
5896 box_p[2].x=polygon_primitive[n].point.x-offset.x;
5897 box_p[2].y=polygon_primitive[n].point.y-offset.y;
5898 box_p[3].x=polygon_primitive[i].point.x-offset.x;
5899 box_p[3].y=polygon_primitive[i].point.y-offset.y;
5900 box_q[2].x=polygon_primitive[n].point.x+offset.x;
5901 box_q[2].y=polygon_primitive[n].point.y+offset.y;
5902 box_q[3].x=polygon_primitive[i].point.x+offset.x;
5903 box_q[3].y=polygon_primitive[i].point.y+offset.y;
5904 }
5905 else
5906 {
5907 box_p[2].x=polygon_primitive[n].point.x+offset.x;
5908 box_p[2].y=polygon_primitive[n].point.y+offset.y;
5909 box_p[3].x=polygon_primitive[i].point.x+offset.x;
5910 box_p[3].y=polygon_primitive[i].point.y+offset.y;
5911 box_q[2].x=polygon_primitive[n].point.x-offset.x;
5912 box_q[2].y=polygon_primitive[n].point.y-offset.y;
5913 box_q[3].x=polygon_primitive[i].point.x-offset.x;
5914 box_q[3].y=polygon_primitive[i].point.y-offset.y;
5915 }
cristy53aed702012-07-08 00:21:25 +00005916 if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +00005917 {
5918 box_p[4]=box_p[1];
5919 box_q[4]=box_q[1];
5920 }
5921 else
5922 {
5923 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
5924 box_p[3].y)/(slope.p-slope.q));
5925 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
5926 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
5927 box_q[3].y)/(slope.p-slope.q));
5928 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
5929 }
cristybb503372010-05-27 20:51:26 +00005930 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00005931 {
5932 max_strokes+=6*BezierQuantum+360;
5933 path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
5934 sizeof(*path_p));
5935 path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
5936 sizeof(*path_q));
5937 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
5938 {
5939 polygon_primitive=(PrimitiveInfo *)
5940 RelinquishMagickMemory(polygon_primitive);
5941 return((PrimitiveInfo *) NULL);
5942 }
5943 }
5944 dot_product=dx.q*dy.p-dx.p*dy.q;
5945 if (dot_product <= 0.0)
5946 switch (draw_info->linejoin)
5947 {
5948 case BevelJoin:
5949 {
5950 path_q[q++]=box_q[1];
5951 path_q[q++]=box_q[2];
5952 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5953 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5954 if (dot_product <= miterlimit)
5955 path_p[p++]=box_p[4];
5956 else
5957 {
5958 path_p[p++]=box_p[1];
5959 path_p[p++]=box_p[2];
5960 }
5961 break;
5962 }
5963 case MiterJoin:
5964 {
5965 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5966 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5967 if (dot_product <= miterlimit)
5968 {
5969 path_q[q++]=box_q[4];
5970 path_p[p++]=box_p[4];
5971 }
5972 else
5973 {
5974 path_q[q++]=box_q[1];
5975 path_q[q++]=box_q[2];
5976 path_p[p++]=box_p[1];
5977 path_p[p++]=box_p[2];
5978 }
5979 break;
5980 }
5981 case RoundJoin:
5982 {
5983 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5984 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5985 if (dot_product <= miterlimit)
5986 path_p[p++]=box_p[4];
5987 else
5988 {
5989 path_p[p++]=box_p[1];
5990 path_p[p++]=box_p[2];
5991 }
5992 center=polygon_primitive[n].point;
5993 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
5994 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
5995 if (theta.q < theta.p)
cristya19f1d72012-08-07 18:24:38 +00005996 theta.q+=(double) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00005997 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
cristy3ed852e2009-09-05 21:47:34 +00005998 (2.0*sqrt((double) (1.0/mid)))));
5999 path_q[q].x=box_q[1].x;
6000 path_q[q].y=box_q[1].y;
6001 q++;
cristybb503372010-05-27 20:51:26 +00006002 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006003 {
cristya19f1d72012-08-07 18:24:38 +00006004 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
cristy3ed852e2009-09-05 21:47:34 +00006005 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
6006 (theta.p+delta_theta),DegreesToRadians(360.0))));
6007 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
6008 (theta.p+delta_theta),DegreesToRadians(360.0))));
6009 q++;
6010 }
6011 path_q[q++]=box_q[2];
6012 break;
6013 }
6014 default:
6015 break;
6016 }
6017 else
6018 switch (draw_info->linejoin)
6019 {
6020 case BevelJoin:
6021 {
6022 path_p[p++]=box_p[1];
6023 path_p[p++]=box_p[2];
6024 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6025 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6026 if (dot_product <= miterlimit)
6027 path_q[q++]=box_q[4];
6028 else
6029 {
6030 path_q[q++]=box_q[1];
6031 path_q[q++]=box_q[2];
6032 }
6033 break;
6034 }
6035 case MiterJoin:
6036 {
6037 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6038 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6039 if (dot_product <= miterlimit)
6040 {
6041 path_q[q++]=box_q[4];
6042 path_p[p++]=box_p[4];
6043 }
6044 else
6045 {
6046 path_q[q++]=box_q[1];
6047 path_q[q++]=box_q[2];
6048 path_p[p++]=box_p[1];
6049 path_p[p++]=box_p[2];
6050 }
6051 break;
6052 }
6053 case RoundJoin:
6054 {
6055 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6056 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6057 if (dot_product <= miterlimit)
6058 path_q[q++]=box_q[4];
6059 else
6060 {
6061 path_q[q++]=box_q[1];
6062 path_q[q++]=box_q[2];
6063 }
6064 center=polygon_primitive[n].point;
6065 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
6066 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
6067 if (theta.p < theta.q)
cristya19f1d72012-08-07 18:24:38 +00006068 theta.p+=(double) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006069 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
cristy3ed852e2009-09-05 21:47:34 +00006070 (2.0*sqrt((double) (1.0/mid)))));
6071 path_p[p++]=box_p[1];
cristybb503372010-05-27 20:51:26 +00006072 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006073 {
cristya19f1d72012-08-07 18:24:38 +00006074 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
cristy3ed852e2009-09-05 21:47:34 +00006075 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
6076 (theta.p+delta_theta),DegreesToRadians(360.0))));
6077 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
6078 (theta.p+delta_theta),DegreesToRadians(360.0))));
6079 p++;
6080 }
6081 path_p[p++]=box_p[2];
6082 break;
6083 }
6084 default:
6085 break;
6086 }
6087 slope.p=slope.q;
6088 inverse_slope.p=inverse_slope.q;
6089 box_p[0]=box_p[2];
6090 box_p[1]=box_p[3];
6091 box_q[0]=box_q[2];
6092 box_q[1]=box_q[3];
6093 dx.p=dx.q;
6094 dy.p=dy.q;
6095 n=i;
6096 }
6097 path_p[p++]=box_p[1];
6098 path_q[q++]=box_q[1];
6099 /*
6100 Trace stroked polygon.
6101 */
6102 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
6103 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
6104 if (stroke_polygon != (PrimitiveInfo *) NULL)
6105 {
cristybb503372010-05-27 20:51:26 +00006106 for (i=0; i < (ssize_t) p; i++)
cristy3ed852e2009-09-05 21:47:34 +00006107 {
6108 stroke_polygon[i]=polygon_primitive[0];
6109 stroke_polygon[i].point=path_p[i];
6110 }
6111 if (closed_path != MagickFalse)
6112 {
6113 stroke_polygon[i]=polygon_primitive[0];
6114 stroke_polygon[i].point=stroke_polygon[0].point;
6115 i++;
6116 }
cristybb503372010-05-27 20:51:26 +00006117 for ( ; i < (ssize_t) (p+q+closed_path); i++)
cristy3ed852e2009-09-05 21:47:34 +00006118 {
6119 stroke_polygon[i]=polygon_primitive[0];
6120 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
6121 }
6122 if (closed_path != MagickFalse)
6123 {
6124 stroke_polygon[i]=polygon_primitive[0];
6125 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
6126 i++;
6127 }
6128 stroke_polygon[i]=polygon_primitive[0];
6129 stroke_polygon[i].point=stroke_polygon[0].point;
6130 i++;
6131 stroke_polygon[i].primitive=UndefinedPrimitive;
cristybb503372010-05-27 20:51:26 +00006132 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
cristy3ed852e2009-09-05 21:47:34 +00006133 }
6134 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
6135 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
6136 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
6137 return(stroke_polygon);
6138}