blob: f874ab5e4bf904d6c057d466eab164e92b93a8c9 [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% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 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"
74#include "MagickCore/property.h"
75#include "MagickCore/resample.h"
76#include "MagickCore/resample-private.h"
cristyac245f82012-05-05 17:13:57 +000077#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/thread-private.h"
81#include "MagickCore/token.h"
82#include "MagickCore/transform.h"
83#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000084
85/*
86 Define declarations.
87*/
88#define BezierQuantum 200
89
90/*
91 Typedef declarations.
92*/
93typedef struct _EdgeInfo
94{
95 SegmentInfo
96 bounds;
97
98 MagickRealType
99 scanline;
100
101 PointInfo
102 *points;
103
cristybb503372010-05-27 20:51:26 +0000104 size_t
cristy3ed852e2009-09-05 21:47:34 +0000105 number_points;
106
cristybb503372010-05-27 20:51:26 +0000107 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000108 direction;
109
110 MagickBooleanType
111 ghostline;
112
cristybb503372010-05-27 20:51:26 +0000113 size_t
cristy3ed852e2009-09-05 21:47:34 +0000114 highwater;
115} EdgeInfo;
116
117typedef struct _ElementInfo
118{
119 MagickRealType
120 cx,
121 cy,
122 major,
123 minor,
124 angle;
125} ElementInfo;
126
127typedef struct _PolygonInfo
128{
129 EdgeInfo
130 *edges;
131
cristybb503372010-05-27 20:51:26 +0000132 size_t
cristy3ed852e2009-09-05 21:47:34 +0000133 number_edges;
134} PolygonInfo;
135
136typedef enum
137{
138 MoveToCode,
139 OpenCode,
140 GhostlineCode,
141 LineToCode,
142 EndCode
143} PathInfoCode;
144
145typedef struct _PathInfo
146{
147 PointInfo
148 point;
149
150 PathInfoCode
151 code;
152} PathInfo;
153
154/*
155 Forward declarations.
156*/
157static MagickBooleanType
cristy947cb4c2011-10-20 18:41:46 +0000158 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
159 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000160
161static PrimitiveInfo
162 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
163
cristybb503372010-05-27 20:51:26 +0000164static size_t
cristy3ed852e2009-09-05 21:47:34 +0000165 TracePath(PrimitiveInfo *,const char *);
166
167static void
168 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
169 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo,
170 const MagickRealType,const MagickBooleanType,const MagickBooleanType),
cristybb503372010-05-27 20:51:26 +0000171 TraceBezier(PrimitiveInfo *,const size_t),
cristy3ed852e2009-09-05 21:47:34 +0000172 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo),
173 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
174 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
175 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
176 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo,
177 PointInfo),
cristybb503372010-05-27 20:51:26 +0000178 TraceSquareLinecap(PrimitiveInfo *,const size_t,const MagickRealType);
cristy3ed852e2009-09-05 21:47:34 +0000179
180/*
181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182% %
183% %
184% %
185% A c q u i r e D r a w I n f o %
186% %
187% %
188% %
189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190%
191% AcquireDrawInfo() returns a DrawInfo structure properly initialized.
192%
193% The format of the AcquireDrawInfo method is:
194%
195% DrawInfo *AcquireDrawInfo(void)
196%
197*/
198MagickExport DrawInfo *AcquireDrawInfo(void)
199{
200 DrawInfo
201 *draw_info;
202
cristy73bd4a52010-10-05 11:24:23 +0000203 draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +0000204 if (draw_info == (DrawInfo *) NULL)
205 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
206 GetDrawInfo((ImageInfo *) NULL,draw_info);
207 return(draw_info);
208}
209
210/*
211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212% %
213% %
214% %
215% C l o n e D r a w I n f o %
216% %
217% %
218% %
219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220%
anthony36fe1752011-09-29 11:28:37 +0000221% CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
222% is specified, a new draw_info structure is created initialized to
223% default values, according to the given image_info.
cristy3ed852e2009-09-05 21:47:34 +0000224%
225% The format of the CloneDrawInfo method is:
226%
227% DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
228% const DrawInfo *draw_info)
229%
230% A description of each parameter follows:
231%
232% o image_info: the image info.
233%
234% o draw_info: the draw info.
235%
236*/
237MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
238 const DrawInfo *draw_info)
239{
240 DrawInfo
241 *clone_info;
242
cristy947cb4c2011-10-20 18:41:46 +0000243 ExceptionInfo
244 *exception;
245
cristy73bd4a52010-10-05 11:24:23 +0000246 clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info));
cristy3ed852e2009-09-05 21:47:34 +0000247 if (clone_info == (DrawInfo *) NULL)
248 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
249 GetDrawInfo(image_info,clone_info);
250 if (draw_info == (DrawInfo *) NULL)
251 return(clone_info);
cristy947cb4c2011-10-20 18:41:46 +0000252 exception=AcquireExceptionInfo();
anthony42f6c202011-10-23 10:28:55 +0000253 (void) CloneString(&clone_info->primitive,draw_info->primitive);
254 (void) CloneString(&clone_info->geometry,draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000255 clone_info->viewbox=draw_info->viewbox;
256 clone_info->affine=draw_info->affine;
257 clone_info->gravity=draw_info->gravity;
258 clone_info->fill=draw_info->fill;
259 clone_info->stroke=draw_info->stroke;
260 clone_info->stroke_width=draw_info->stroke_width;
261 if (draw_info->fill_pattern != (Image *) NULL)
262 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
cristy947cb4c2011-10-20 18:41:46 +0000263 exception);
cristy3ed852e2009-09-05 21:47:34 +0000264 if (draw_info->stroke_pattern != (Image *) NULL)
265 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
cristy947cb4c2011-10-20 18:41:46 +0000266 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000267 clone_info->stroke_antialias=draw_info->stroke_antialias;
268 clone_info->text_antialias=draw_info->text_antialias;
269 clone_info->fill_rule=draw_info->fill_rule;
270 clone_info->linecap=draw_info->linecap;
271 clone_info->linejoin=draw_info->linejoin;
272 clone_info->miterlimit=draw_info->miterlimit;
273 clone_info->dash_offset=draw_info->dash_offset;
274 clone_info->decorate=draw_info->decorate;
275 clone_info->compose=draw_info->compose;
anthony42f6c202011-10-23 10:28:55 +0000276 (void) CloneString(&clone_info->text,draw_info->text);
277 (void) CloneString(&clone_info->font,draw_info->font);
278 (void) CloneString(&clone_info->metrics,draw_info->metrics);
279 (void) CloneString(&clone_info->family,draw_info->family);
cristy3ed852e2009-09-05 21:47:34 +0000280 clone_info->style=draw_info->style;
281 clone_info->stretch=draw_info->stretch;
282 clone_info->weight=draw_info->weight;
anthony42f6c202011-10-23 10:28:55 +0000283 (void) CloneString(&clone_info->encoding,draw_info->encoding);
cristy3ed852e2009-09-05 21:47:34 +0000284 clone_info->pointsize=draw_info->pointsize;
285 clone_info->kerning=draw_info->kerning;
cristyb32b90a2009-09-07 21:45:48 +0000286 clone_info->interline_spacing=draw_info->interline_spacing;
cristy3ed852e2009-09-05 21:47:34 +0000287 clone_info->interword_spacing=draw_info->interword_spacing;
cristyc9b12952010-03-28 01:12:28 +0000288 clone_info->direction=draw_info->direction;
anthony42f6c202011-10-23 10:28:55 +0000289 (void) CloneString(&clone_info->density,draw_info->density);
cristy3ed852e2009-09-05 21:47:34 +0000290 clone_info->align=draw_info->align;
291 clone_info->undercolor=draw_info->undercolor;
292 clone_info->border_color=draw_info->border_color;
anthony42f6c202011-10-23 10:28:55 +0000293 (void) CloneString(&clone_info->server_name,draw_info->server_name);
cristy3ed852e2009-09-05 21:47:34 +0000294 if (draw_info->dash_pattern != (double *) NULL)
295 {
cristybb503372010-05-27 20:51:26 +0000296 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000297 x;
298
299 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
300 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
301 sizeof(*clone_info->dash_pattern));
302 if (clone_info->dash_pattern == (double *) NULL)
303 ThrowFatalException(ResourceLimitFatalError,
304 "UnableToAllocateDashPattern");
305 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
306 (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
307 }
308 clone_info->gradient=draw_info->gradient;
309 if (draw_info->gradient.stops != (StopInfo *) NULL)
310 {
cristybb503372010-05-27 20:51:26 +0000311 size_t
cristy3ed852e2009-09-05 21:47:34 +0000312 number_stops;
313
314 number_stops=clone_info->gradient.number_stops;
315 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
316 number_stops,sizeof(*clone_info->gradient.stops));
317 if (clone_info->gradient.stops == (StopInfo *) NULL)
318 ThrowFatalException(ResourceLimitFatalError,
319 "UnableToAllocateDashPattern");
320 (void) CopyMagickMemory(clone_info->gradient.stops,
321 draw_info->gradient.stops,(size_t) number_stops*
322 sizeof(*clone_info->gradient.stops));
323 }
anthony42f6c202011-10-23 10:28:55 +0000324 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
cristy3ed852e2009-09-05 21:47:34 +0000325 clone_info->bounds=draw_info->bounds;
326 clone_info->clip_units=draw_info->clip_units;
327 clone_info->render=draw_info->render;
cristy4c08aed2011-07-01 19:47:50 +0000328 clone_info->alpha=draw_info->alpha;
cristy3ed852e2009-09-05 21:47:34 +0000329 clone_info->element_reference=draw_info->element_reference;
330 clone_info->debug=IsEventLogging();
cristy947cb4c2011-10-20 18:41:46 +0000331 exception=DestroyExceptionInfo(exception);
cristy3ed852e2009-09-05 21:47:34 +0000332 return(clone_info);
333}
334
335/*
336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337% %
338% %
339% %
340+ C o n v e r t P a t h T o P o l y g o n %
341% %
342% %
343% %
344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345%
346% ConvertPathToPolygon() converts a path to the more efficient sorted
347% rendering form.
348%
349% The format of the ConvertPathToPolygon method is:
350%
351% PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
352% const PathInfo *path_info)
353%
354% A description of each parameter follows:
355%
356% o Method ConvertPathToPolygon returns the path in a more efficient sorted
357% rendering form of type PolygonInfo.
358%
359% o draw_info: Specifies a pointer to an DrawInfo structure.
360%
361% o path_info: Specifies a pointer to an PathInfo structure.
362%
363%
364*/
365
366#if defined(__cplusplus) || defined(c_plusplus)
367extern "C" {
368#endif
369
370static int CompareEdges(const void *x,const void *y)
371{
372 register const EdgeInfo
373 *p,
374 *q;
375
376 /*
377 Compare two edges.
378 */
379 p=(const EdgeInfo *) x;
380 q=(const EdgeInfo *) y;
381 if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
382 return(1);
383 if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
384 return(-1);
385 if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
386 return(1);
387 if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
388 return(-1);
389 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
390 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
391 return(1);
392 return(-1);
393}
394
395#if defined(__cplusplus) || defined(c_plusplus)
396}
397#endif
398
399static void LogPolygonInfo(const PolygonInfo *polygon_info)
400{
401 register EdgeInfo
402 *p;
403
cristybb503372010-05-27 20:51:26 +0000404 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000405 i,
406 j;
407
408 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
409 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +0000410 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000411 {
cristye8c25f92010-06-03 00:53:06 +0000412 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
413 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000414 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
415 p->direction != MagickFalse ? "down" : "up");
416 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
417 p->ghostline != MagickFalse ? "transparent" : "opaque");
418 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000419 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000420 p->bounds.x2,p->bounds.y2);
cristybb503372010-05-27 20:51:26 +0000421 for (j=0; j < (ssize_t) p->number_points; j++)
cristy14388de2011-05-15 14:57:16 +0000422 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g",
cristy3ed852e2009-09-05 21:47:34 +0000423 p->points[j].x,p->points[j].y);
424 p++;
425 }
426 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
427}
428
cristybb503372010-05-27 20:51:26 +0000429static void ReversePoints(PointInfo *points,const size_t number_points)
cristy3ed852e2009-09-05 21:47:34 +0000430{
431 PointInfo
432 point;
433
cristybb503372010-05-27 20:51:26 +0000434 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000435 i;
436
cristybb503372010-05-27 20:51:26 +0000437 for (i=0; i < (ssize_t) (number_points >> 1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000438 {
439 point=points[i];
440 points[i]=points[number_points-(i+1)];
441 points[number_points-(i+1)]=point;
442 }
443}
444
445static PolygonInfo *ConvertPathToPolygon(
446 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
447{
cristycee97112010-05-28 00:44:52 +0000448 long
cristy3ed852e2009-09-05 21:47:34 +0000449 direction,
450 next_direction;
451
452 PointInfo
453 point,
454 *points;
455
456 PolygonInfo
457 *polygon_info;
458
459 SegmentInfo
460 bounds;
461
cristybb503372010-05-27 20:51:26 +0000462 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000463 i,
464 n;
465
466 MagickBooleanType
467 ghostline;
468
cristybb503372010-05-27 20:51:26 +0000469 size_t
cristy3ed852e2009-09-05 21:47:34 +0000470 edge,
471 number_edges,
472 number_points;
473
474 /*
475 Convert a path to the more efficient sorted rendering form.
476 */
cristy73bd4a52010-10-05 11:24:23 +0000477 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +0000478 if (polygon_info == (PolygonInfo *) NULL)
479 return((PolygonInfo *) NULL);
480 number_edges=16;
481 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
482 sizeof(*polygon_info->edges));
483 if (polygon_info->edges == (EdgeInfo *) NULL)
484 return((PolygonInfo *) NULL);
485 direction=0;
486 edge=0;
487 ghostline=MagickFalse;
488 n=0;
489 number_points=0;
490 points=(PointInfo *) NULL;
491 (void) ResetMagickMemory(&point,0,sizeof(point));
492 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
493 for (i=0; path_info[i].code != EndCode; i++)
494 {
495 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
496 (path_info[i].code == GhostlineCode))
497 {
498 /*
499 Move to.
500 */
501 if ((points != (PointInfo *) NULL) && (n >= 2))
502 {
503 if (edge == number_edges)
504 {
505 number_edges<<=1;
506 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
507 polygon_info->edges,(size_t) number_edges,
508 sizeof(*polygon_info->edges));
509 if (polygon_info->edges == (EdgeInfo *) NULL)
510 return((PolygonInfo *) NULL);
511 }
cristybb503372010-05-27 20:51:26 +0000512 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000513 polygon_info->edges[edge].scanline=(-1.0);
514 polygon_info->edges[edge].highwater=0;
515 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000516 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000517 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000518 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000519 polygon_info->edges[edge].points=points;
520 polygon_info->edges[edge].bounds=bounds;
521 polygon_info->edges[edge].bounds.y1=points[0].y;
522 polygon_info->edges[edge].bounds.y2=points[n-1].y;
523 points=(PointInfo *) NULL;
524 ghostline=MagickFalse;
525 edge++;
526 }
527 if (points == (PointInfo *) NULL)
528 {
529 number_points=16;
530 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
531 sizeof(*points));
532 if (points == (PointInfo *) NULL)
533 return((PolygonInfo *) NULL);
534 }
535 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
536 point=path_info[i].point;
537 points[0]=point;
538 bounds.x1=point.x;
539 bounds.x2=point.x;
540 direction=0;
541 n=1;
542 continue;
543 }
544 /*
545 Line to.
546 */
547 next_direction=((path_info[i].point.y > point.y) ||
548 ((path_info[i].point.y == point.y) &&
549 (path_info[i].point.x > point.x))) ? 1 : -1;
550 if ((direction != 0) && (direction != next_direction))
551 {
552 /*
553 New edge.
554 */
555 point=points[n-1];
556 if (edge == number_edges)
557 {
558 number_edges<<=1;
559 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
560 polygon_info->edges,(size_t) number_edges,
561 sizeof(*polygon_info->edges));
562 if (polygon_info->edges == (EdgeInfo *) NULL)
563 return((PolygonInfo *) NULL);
564 }
cristybb503372010-05-27 20:51:26 +0000565 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000566 polygon_info->edges[edge].scanline=(-1.0);
567 polygon_info->edges[edge].highwater=0;
568 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000569 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000570 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000571 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000572 polygon_info->edges[edge].points=points;
573 polygon_info->edges[edge].bounds=bounds;
574 polygon_info->edges[edge].bounds.y1=points[0].y;
575 polygon_info->edges[edge].bounds.y2=points[n-1].y;
576 number_points=16;
577 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
578 sizeof(*points));
579 if (points == (PointInfo *) NULL)
580 return((PolygonInfo *) NULL);
581 n=1;
582 ghostline=MagickFalse;
583 points[0]=point;
584 bounds.x1=point.x;
585 bounds.x2=point.x;
586 edge++;
587 }
588 direction=next_direction;
589 if (points == (PointInfo *) NULL)
590 continue;
cristybb503372010-05-27 20:51:26 +0000591 if (n == (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +0000592 {
593 number_points<<=1;
594 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
595 sizeof(*points));
596 if (points == (PointInfo *) NULL)
597 return((PolygonInfo *) NULL);
598 }
599 point=path_info[i].point;
600 points[n]=point;
601 if (point.x < bounds.x1)
602 bounds.x1=point.x;
603 if (point.x > bounds.x2)
604 bounds.x2=point.x;
605 n++;
606 }
607 if (points != (PointInfo *) NULL)
608 {
609 if (n < 2)
610 points=(PointInfo *) RelinquishMagickMemory(points);
611 else
612 {
613 if (edge == number_edges)
614 {
615 number_edges<<=1;
616 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
617 polygon_info->edges,(size_t) number_edges,
618 sizeof(*polygon_info->edges));
619 if (polygon_info->edges == (EdgeInfo *) NULL)
620 return((PolygonInfo *) NULL);
621 }
cristybb503372010-05-27 20:51:26 +0000622 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000623 polygon_info->edges[edge].scanline=(-1.0);
624 polygon_info->edges[edge].highwater=0;
625 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000626 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000627 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000628 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000629 polygon_info->edges[edge].points=points;
630 polygon_info->edges[edge].bounds=bounds;
631 polygon_info->edges[edge].bounds.y1=points[0].y;
632 polygon_info->edges[edge].bounds.y2=points[n-1].y;
633 ghostline=MagickFalse;
634 edge++;
635 }
636 }
637 polygon_info->number_edges=edge;
638 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
639 sizeof(*polygon_info->edges),CompareEdges);
640 if (IsEventLogging() != MagickFalse)
641 LogPolygonInfo(polygon_info);
642 return(polygon_info);
643}
644
645/*
646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
647% %
648% %
649% %
650+ C o n v e r t P r i m i t i v e T o P a t h %
651% %
652% %
653% %
654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655%
656% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
657% path structure.
658%
659% The format of the ConvertPrimitiveToPath method is:
660%
661% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
662% const PrimitiveInfo *primitive_info)
663%
664% A description of each parameter follows:
665%
666% o Method ConvertPrimitiveToPath returns a vector path structure of type
667% PathInfo.
668%
669% o draw_info: a structure of type DrawInfo.
670%
671% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
672%
673%
674*/
675
676static void LogPathInfo(const PathInfo *path_info)
677{
678 register const PathInfo
679 *p;
680
681 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
682 for (p=path_info; p->code != EndCode; p++)
683 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000684 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
cristy3ed852e2009-09-05 21:47:34 +0000685 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
686 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
687 "?");
688 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
689}
690
691static PathInfo *ConvertPrimitiveToPath(
692 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
693{
cristy3ed852e2009-09-05 21:47:34 +0000694 PathInfo
695 *path_info;
696
697 PathInfoCode
698 code;
699
700 PointInfo
701 p,
702 q;
703
cristybb503372010-05-27 20:51:26 +0000704 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000705 i,
706 n;
707
cristy826a5472010-08-31 23:21:38 +0000708 ssize_t
709 coordinates,
710 start;
711
cristy3ed852e2009-09-05 21:47:34 +0000712 /*
713 Converts a PrimitiveInfo structure into a vector path structure.
714 */
715 switch (primitive_info->primitive)
716 {
717 case PointPrimitive:
718 case ColorPrimitive:
719 case MattePrimitive:
720 case TextPrimitive:
721 case ImagePrimitive:
722 return((PathInfo *) NULL);
723 default:
724 break;
725 }
726 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
727 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
728 sizeof(*path_info));
729 if (path_info == (PathInfo *) NULL)
730 return((PathInfo *) NULL);
731 coordinates=0;
732 n=0;
733 p.x=(-1.0);
734 p.y=(-1.0);
735 q.x=(-1.0);
736 q.y=(-1.0);
737 start=0;
738 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
739 {
740 code=LineToCode;
741 if (coordinates <= 0)
742 {
cristybb503372010-05-27 20:51:26 +0000743 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +0000744 p=primitive_info[i].point;
745 start=n;
746 code=MoveToCode;
747 }
748 coordinates--;
749 /*
750 Eliminate duplicate points.
751 */
752 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
753 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
754 {
755 path_info[n].code=code;
756 path_info[n].point=primitive_info[i].point;
757 q=primitive_info[i].point;
758 n++;
759 }
760 if (coordinates > 0)
761 continue;
762 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
763 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
764 continue;
765 /*
766 Mark the p point as open if it does not match the q.
767 */
768 path_info[start].code=OpenCode;
769 path_info[n].code=GhostlineCode;
770 path_info[n].point=primitive_info[i].point;
771 n++;
772 path_info[n].code=LineToCode;
773 path_info[n].point=p;
774 n++;
775 }
776 path_info[n].code=EndCode;
777 path_info[n].point.x=0.0;
778 path_info[n].point.y=0.0;
779 if (IsEventLogging() != MagickFalse)
780 LogPathInfo(path_info);
781 return(path_info);
782}
783
784/*
785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786% %
787% %
788% %
789% D e s t r o y D r a w I n f o %
790% %
791% %
792% %
793%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
794%
795% DestroyDrawInfo() deallocates memory associated with an DrawInfo
796% structure.
797%
798% The format of the DestroyDrawInfo method is:
799%
800% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
801%
802% A description of each parameter follows:
803%
804% o draw_info: the draw info.
805%
806*/
807MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
808{
809 if (draw_info->debug != MagickFalse)
810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
811 assert(draw_info != (DrawInfo *) NULL);
812 assert(draw_info->signature == MagickSignature);
813 if (draw_info->primitive != (char *) NULL)
814 draw_info->primitive=DestroyString(draw_info->primitive);
815 if (draw_info->text != (char *) NULL)
816 draw_info->text=DestroyString(draw_info->text);
817 if (draw_info->geometry != (char *) NULL)
818 draw_info->geometry=DestroyString(draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000819 if (draw_info->fill_pattern != (Image *) NULL)
820 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
821 if (draw_info->stroke_pattern != (Image *) NULL)
822 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
823 if (draw_info->font != (char *) NULL)
824 draw_info->font=DestroyString(draw_info->font);
825 if (draw_info->metrics != (char *) NULL)
826 draw_info->metrics=DestroyString(draw_info->metrics);
827 if (draw_info->family != (char *) NULL)
828 draw_info->family=DestroyString(draw_info->family);
829 if (draw_info->encoding != (char *) NULL)
830 draw_info->encoding=DestroyString(draw_info->encoding);
831 if (draw_info->density != (char *) NULL)
832 draw_info->density=DestroyString(draw_info->density);
833 if (draw_info->server_name != (char *) NULL)
834 draw_info->server_name=(char *)
835 RelinquishMagickMemory(draw_info->server_name);
836 if (draw_info->dash_pattern != (double *) NULL)
837 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
838 draw_info->dash_pattern);
839 if (draw_info->gradient.stops != (StopInfo *) NULL)
840 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
841 draw_info->gradient.stops);
842 if (draw_info->clip_mask != (char *) NULL)
843 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
844 draw_info->signature=(~MagickSignature);
845 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
846 return(draw_info);
847}
848
849/*
850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851% %
852% %
853% %
854+ D e s t r o y E d g e %
855% %
856% %
857% %
858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859%
860% DestroyEdge() destroys the specified polygon edge.
861%
862% The format of the DestroyEdge method is:
863%
cristybb503372010-05-27 20:51:26 +0000864% ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
cristy3ed852e2009-09-05 21:47:34 +0000865%
866% A description of each parameter follows:
867%
868% o polygon_info: Specifies a pointer to an PolygonInfo structure.
869%
870% o edge: the polygon edge number to destroy.
871%
872*/
cristybb503372010-05-27 20:51:26 +0000873static size_t DestroyEdge(PolygonInfo *polygon_info,
874 const size_t edge)
cristy3ed852e2009-09-05 21:47:34 +0000875{
876 assert(edge < polygon_info->number_edges);
877 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
878 polygon_info->edges[edge].points);
879 polygon_info->number_edges--;
880 if (edge < polygon_info->number_edges)
881 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
882 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
883 return(polygon_info->number_edges);
884}
885
886/*
887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888% %
889% %
890% %
891+ D e s t r o y P o l y g o n I n f o %
892% %
893% %
894% %
895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
896%
897% DestroyPolygonInfo() destroys the PolygonInfo data structure.
898%
899% The format of the DestroyPolygonInfo method is:
900%
901% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
902%
903% A description of each parameter follows:
904%
905% o polygon_info: Specifies a pointer to an PolygonInfo structure.
906%
907*/
908static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
909{
cristybb503372010-05-27 20:51:26 +0000910 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000911 i;
912
cristybb503372010-05-27 20:51:26 +0000913 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000914 polygon_info->edges[i].points=(PointInfo *)
915 RelinquishMagickMemory(polygon_info->edges[i].points);
916 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
917 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
918}
919
920/*
921%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
922% %
923% %
924% %
925% D r a w A f f i n e I m a g e %
926% %
927% %
928% %
929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
930%
931% DrawAffineImage() composites the source over the destination image as
932% dictated by the affine transform.
933%
934% The format of the DrawAffineImage method is:
935%
936% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
cristy947cb4c2011-10-20 18:41:46 +0000937% const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000938%
939% A description of each parameter follows:
940%
941% o image: the image.
942%
943% o source: the source image.
944%
945% o affine: the affine transform.
946%
cristy947cb4c2011-10-20 18:41:46 +0000947% o exception: return any errors or warnings in this structure.
948%
cristy3ed852e2009-09-05 21:47:34 +0000949*/
cristyefa9e8a2011-11-07 01:56:51 +0000950
cristy3ed852e2009-09-05 21:47:34 +0000951static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
952 const double y,const SegmentInfo *edge)
953{
954 double
955 intercept,
956 z;
957
958 register double
959 x;
960
961 SegmentInfo
962 inverse_edge;
963
964 /*
965 Determine left and right edges.
966 */
967 inverse_edge.x1=edge->x1;
968 inverse_edge.y1=edge->y1;
969 inverse_edge.x2=edge->x2;
970 inverse_edge.y2=edge->y2;
971 z=affine->ry*y+affine->tx;
972 if (affine->sx > MagickEpsilon)
973 {
974 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000975 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000976 if (x > inverse_edge.x1)
977 inverse_edge.x1=x;
978 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000979 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000980 if (x < inverse_edge.x2)
981 inverse_edge.x2=x;
982 }
983 else
984 if (affine->sx < -MagickEpsilon)
985 {
986 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000987 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000988 if (x > inverse_edge.x1)
989 inverse_edge.x1=x;
990 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000991 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000992 if (x < inverse_edge.x2)
993 inverse_edge.x2=x;
994 }
995 else
cristybb503372010-05-27 20:51:26 +0000996 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
cristy3ed852e2009-09-05 21:47:34 +0000997 {
998 inverse_edge.x2=edge->x1;
999 return(inverse_edge);
1000 }
1001 /*
1002 Determine top and bottom edges.
1003 */
1004 z=affine->sy*y+affine->ty;
1005 if (affine->rx > MagickEpsilon)
1006 {
1007 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001008 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001009 if (x > inverse_edge.x1)
1010 inverse_edge.x1=x;
1011 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001012 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001013 if (x < inverse_edge.x2)
1014 inverse_edge.x2=x;
1015 }
1016 else
1017 if (affine->rx < -MagickEpsilon)
1018 {
1019 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001020 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001021 if (x > inverse_edge.x1)
1022 inverse_edge.x1=x;
1023 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001024 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001025 if (x < inverse_edge.x2)
1026 inverse_edge.x2=x;
1027 }
1028 else
cristybb503372010-05-27 20:51:26 +00001029 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001030 {
1031 inverse_edge.x2=edge->x2;
1032 return(inverse_edge);
1033 }
1034 return(inverse_edge);
1035}
1036
1037static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1038{
1039 AffineMatrix
1040 inverse_affine;
1041
1042 double
1043 determinant;
1044
1045 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
1046 inverse_affine.sx=determinant*affine->sy;
1047 inverse_affine.rx=determinant*(-affine->rx);
1048 inverse_affine.ry=determinant*(-affine->ry);
1049 inverse_affine.sy=determinant*affine->sx;
1050 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1051 inverse_affine.ry;
1052 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1053 inverse_affine.sy;
1054 return(inverse_affine);
1055}
1056
cristybb503372010-05-27 20:51:26 +00001057static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +00001058{
1059 if (x < 0)
1060 return(-x);
1061 return(x);
1062}
1063
1064static inline double MagickMax(const double x,const double y)
1065{
1066 if (x > y)
1067 return(x);
1068 return(y);
1069}
1070
1071static inline double MagickMin(const double x,const double y)
1072{
1073 if (x < y)
1074 return(x);
1075 return(y);
1076}
1077
1078MagickExport MagickBooleanType DrawAffineImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00001079 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001080{
1081 AffineMatrix
1082 inverse_affine;
1083
cristyfa112112010-01-04 17:48:07 +00001084 CacheView
1085 *image_view,
1086 *source_view;
1087
cristy3ed852e2009-09-05 21:47:34 +00001088 MagickBooleanType
1089 status;
1090
cristy4c08aed2011-07-01 19:47:50 +00001091 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001092 zero;
1093
1094 PointInfo
1095 extent[4],
1096 min,
1097 max,
1098 point;
1099
cristybb503372010-05-27 20:51:26 +00001100 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001101 i;
1102
cristy3ed852e2009-09-05 21:47:34 +00001103 SegmentInfo
1104 edge;
1105
cristyac245f82012-05-05 17:13:57 +00001106 size_t
1107 height,
1108 width;
1109
cristy826a5472010-08-31 23:21:38 +00001110 ssize_t
cristy564a5692012-01-20 23:56:26 +00001111 start,
1112 stop,
cristy826a5472010-08-31 23:21:38 +00001113 y;
1114
cristy3ed852e2009-09-05 21:47:34 +00001115 /*
1116 Determine bounding box.
1117 */
1118 assert(image != (Image *) NULL);
1119 assert(image->signature == MagickSignature);
1120 if (image->debug != MagickFalse)
1121 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1122 assert(source != (const Image *) NULL);
1123 assert(source->signature == MagickSignature);
1124 assert(affine != (AffineMatrix *) NULL);
1125 extent[0].x=0.0;
1126 extent[0].y=0.0;
1127 extent[1].x=(double) source->columns-1.0;
1128 extent[1].y=0.0;
1129 extent[2].x=(double) source->columns-1.0;
1130 extent[2].y=(double) source->rows-1.0;
1131 extent[3].x=0.0;
1132 extent[3].y=(double) source->rows-1.0;
1133 for (i=0; i < 4; i++)
1134 {
1135 point=extent[i];
1136 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1137 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1138 }
1139 min=extent[0];
1140 max=extent[0];
1141 for (i=1; i < 4; i++)
1142 {
1143 if (min.x > extent[i].x)
1144 min.x=extent[i].x;
1145 if (min.y > extent[i].y)
1146 min.y=extent[i].y;
1147 if (max.x < extent[i].x)
1148 max.x=extent[i].x;
1149 if (max.y < extent[i].y)
1150 max.y=extent[i].y;
1151 }
1152 /*
1153 Affine transform image.
1154 */
cristy574cc262011-08-05 01:23:58 +00001155 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
1157 status=MagickTrue;
1158 edge.x1=MagickMax(min.x,0.0);
1159 edge.y1=MagickMax(min.y,0.0);
1160 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1161 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1162 inverse_affine=InverseAffineMatrix(affine);
cristy4c08aed2011-07-01 19:47:50 +00001163 GetPixelInfo(image,&zero);
cristy564a5692012-01-20 23:56:26 +00001164 start=(ssize_t) ceil(edge.y1-0.5);
1165 stop=(ssize_t) ceil(edge.y2-0.5);
cristyac245f82012-05-05 17:13:57 +00001166 height=(size_t) (floor(edge.y2+0.5)-ceil(edge.y1-0.5));
1167 width=(size_t) (floor(edge.x2+0.5)-ceil(edge.x1-0.5));
cristydb070952012-04-20 14:33:00 +00001168 source_view=AcquireVirtualCacheView(source,exception);
1169 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001170#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001171 #pragma omp parallel for schedule(static) shared(status) \
1172 if ((height*width) > 8192) \
1173 num_threads(GetMagickResourceLimit(ThreadResource))
cristy3ed852e2009-09-05 21:47:34 +00001174#endif
cristy564a5692012-01-20 23:56:26 +00001175 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00001176 {
cristy4c08aed2011-07-01 19:47:50 +00001177 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001178 composite,
1179 pixel;
1180
1181 PointInfo
1182 point;
1183
cristybb503372010-05-27 20:51:26 +00001184 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001185 x;
1186
cristy4c08aed2011-07-01 19:47:50 +00001187 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001188 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001189
1190 SegmentInfo
1191 inverse_edge;
1192
cristy826a5472010-08-31 23:21:38 +00001193 ssize_t
1194 x_offset;
1195
cristy3ed852e2009-09-05 21:47:34 +00001196 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1197 if (inverse_edge.x2 < inverse_edge.x1)
1198 continue;
cristy6ebe97c2010-07-03 01:17:28 +00001199 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
1200 0.5),y,(size_t) ((ssize_t) floor(inverse_edge.x2+0.5)-(ssize_t) floor(
cristy06609ee2010-03-17 20:21:27 +00001201 inverse_edge.x1+0.5)+1),1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001202 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001203 continue;
cristy3ed852e2009-09-05 21:47:34 +00001204 pixel=zero;
1205 composite=zero;
1206 x_offset=0;
cristybb503372010-05-27 20:51:26 +00001207 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 +00001208 {
1209 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1210 inverse_affine.tx;
1211 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1212 inverse_affine.ty;
cristy4c08aed2011-07-01 19:47:50 +00001213 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1214 point.x,point.y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00001215 GetPixelInfoPixel(image,q,&composite);
cristy4c08aed2011-07-01 19:47:50 +00001216 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1217 &composite);
cristy803640d2011-11-17 02:11:32 +00001218 SetPixelInfoPixel(image,&composite,q);
cristy3ed852e2009-09-05 21:47:34 +00001219 x_offset++;
cristyed231572011-07-14 02:18:59 +00001220 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001221 }
1222 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1223 status=MagickFalse;
1224 }
cristy3ed852e2009-09-05 21:47:34 +00001225 source_view=DestroyCacheView(source_view);
1226 image_view=DestroyCacheView(image_view);
1227 return(status);
1228}
1229
1230/*
1231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232% %
1233% %
1234% %
1235+ D r a w B o u n d i n g R e c t a n g l e s %
1236% %
1237% %
1238% %
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240%
1241% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1242% is only useful for developers debugging the rendering algorithm.
1243%
1244% The format of the DrawBoundingRectangles method is:
1245%
1246% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001247% PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001248%
1249% A description of each parameter follows:
1250%
1251% o image: the image.
1252%
1253% o draw_info: the draw info.
1254%
1255% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1256%
cristy947cb4c2011-10-20 18:41:46 +00001257% o exception: return any errors or warnings in this structure.
1258%
cristy3ed852e2009-09-05 21:47:34 +00001259*/
1260static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001261 const PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001262{
1263 DrawInfo
1264 *clone_info;
1265
cristy3ed852e2009-09-05 21:47:34 +00001266 MagickRealType
1267 mid;
1268
1269 PointInfo
1270 end,
1271 resolution,
1272 start;
1273
1274 PrimitiveInfo
1275 primitive_info[6];
1276
cristybb503372010-05-27 20:51:26 +00001277 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001278 i;
1279
1280 SegmentInfo
1281 bounds;
1282
cristy826a5472010-08-31 23:21:38 +00001283 ssize_t
1284 coordinates;
1285
cristy3ed852e2009-09-05 21:47:34 +00001286 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
cristyfad60c92012-01-19 18:32:39 +00001287 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill,
cristy947cb4c2011-10-20 18:41:46 +00001288 exception);
cristy3ed852e2009-09-05 21:47:34 +00001289 resolution.x=DefaultResolution;
1290 resolution.y=DefaultResolution;
1291 if (clone_info->density != (char *) NULL)
1292 {
1293 GeometryInfo
1294 geometry_info;
1295
1296 MagickStatusType
1297 flags;
1298
1299 flags=ParseGeometry(clone_info->density,&geometry_info);
1300 resolution.x=geometry_info.rho;
1301 resolution.y=geometry_info.sigma;
1302 if ((flags & SigmaValue) == MagickFalse)
1303 resolution.y=resolution.x;
1304 }
1305 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1306 clone_info->stroke_width/2.0;
1307 bounds.x1=0.0;
1308 bounds.y1=0.0;
1309 bounds.x2=0.0;
1310 bounds.y2=0.0;
1311 if (polygon_info != (PolygonInfo *) NULL)
1312 {
1313 bounds=polygon_info->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00001314 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001315 {
1316 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1317 bounds.x1=polygon_info->edges[i].bounds.x1;
1318 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1319 bounds.y1=polygon_info->edges[i].bounds.y1;
1320 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1321 bounds.x2=polygon_info->edges[i].bounds.x2;
1322 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1323 bounds.y2=polygon_info->edges[i].bounds.y2;
1324 }
1325 bounds.x1-=mid;
1326 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1327 image->columns ? (double) image->columns-1 : bounds.x1;
1328 bounds.y1-=mid;
1329 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1330 image->rows ? (double) image->rows-1 : bounds.y1;
1331 bounds.x2+=mid;
1332 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1333 image->columns ? (double) image->columns-1 : bounds.x2;
1334 bounds.y2+=mid;
1335 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1336 image->rows ? (double) image->rows-1 : bounds.y2;
cristybb503372010-05-27 20:51:26 +00001337 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001338 {
1339 if (polygon_info->edges[i].direction != 0)
cristy9950d572011-10-01 18:22:35 +00001340 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001341 exception);
cristy3ed852e2009-09-05 21:47:34 +00001342 else
cristy9950d572011-10-01 18:22:35 +00001343 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001344 exception);
cristy3ed852e2009-09-05 21:47:34 +00001345 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1346 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1347 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1348 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1349 primitive_info[0].primitive=RectanglePrimitive;
1350 TraceRectangle(primitive_info,start,end);
1351 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001352 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001353 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001354 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001355 }
1356 }
cristy9950d572011-10-01 18:22:35 +00001357 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001358 exception);
cristy3ed852e2009-09-05 21:47:34 +00001359 start.x=(double) (bounds.x1-mid);
1360 start.y=(double) (bounds.y1-mid);
1361 end.x=(double) (bounds.x2+mid);
1362 end.y=(double) (bounds.y2+mid);
1363 primitive_info[0].primitive=RectanglePrimitive;
1364 TraceRectangle(primitive_info,start,end);
1365 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001366 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001367 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001368 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001369 clone_info=DestroyDrawInfo(clone_info);
1370}
1371
1372/*
1373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1374% %
1375% %
1376% %
1377% D r a w C l i p P a t h %
1378% %
1379% %
1380% %
1381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1382%
1383% DrawClipPath() draws the clip path on the image mask.
1384%
1385% The format of the DrawClipPath method is:
1386%
1387% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00001388% const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001389%
1390% A description of each parameter follows:
1391%
1392% o image: the image.
1393%
1394% o draw_info: the draw info.
1395%
1396% o name: the name of the clip path.
1397%
cristy018f07f2011-09-04 21:15:19 +00001398% o exception: return any errors or warnings in this structure.
1399%
cristy3ed852e2009-09-05 21:47:34 +00001400*/
1401MagickExport MagickBooleanType DrawClipPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00001402 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001403{
1404 char
cristy10a6c612012-01-29 21:41:05 +00001405 filename[MaxTextExtent];
1406
1407 Image
1408 *clip_mask;
cristy3ed852e2009-09-05 21:47:34 +00001409
1410 const char
1411 *value;
1412
1413 DrawInfo
1414 *clone_info;
1415
1416 MagickStatusType
1417 status;
1418
1419 assert(image != (Image *) NULL);
1420 assert(image->signature == MagickSignature);
1421 if (image->debug != MagickFalse)
1422 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1423 assert(draw_info != (const DrawInfo *) NULL);
cristy10a6c612012-01-29 21:41:05 +00001424 (void) FormatLocaleString(filename,MaxTextExtent,"%s",name);
1425 value=GetImageArtifact(image,filename);
cristy3ed852e2009-09-05 21:47:34 +00001426 if (value == (const char *) NULL)
1427 return(MagickFalse);
cristy10a6c612012-01-29 21:41:05 +00001428 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1429 if (clip_mask == (Image *) NULL)
1430 return(MagickFalse);
cristyfad60c92012-01-19 18:32:39 +00001431 (void) QueryColorCompliance("#0000",AllCompliance,
cristy10a6c612012-01-29 21:41:05 +00001432 &clip_mask->background_color,exception);
1433 clip_mask->background_color.alpha=(Quantum) TransparentAlpha;
1434 (void) SetImageBackgroundColor(clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001435 if (image->debug != MagickFalse)
1436 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1437 draw_info->clip_mask);
1438 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1439 (void) CloneString(&clone_info->primitive,value);
cristy9950d572011-10-01 18:22:35 +00001440 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
cristyea1a8aa2011-10-20 13:24:06 +00001441 exception);
cristy3ed852e2009-09-05 21:47:34 +00001442 clone_info->clip_mask=(char *) NULL;
cristy0c901882012-01-30 15:58:59 +00001443 status=NegateImage(clip_mask,MagickFalse,exception);
cristy10a6c612012-01-29 21:41:05 +00001444 (void) SetImageMask(image,clip_mask,exception);
1445 clip_mask=DestroyImage(clip_mask);
cristy3ed852e2009-09-05 21:47:34 +00001446 clone_info=DestroyDrawInfo(clone_info);
cristy10a6c612012-01-29 21:41:05 +00001447 status=DrawImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001448 if (image->debug != MagickFalse)
1449 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1450 return(status != 0 ? MagickTrue : MagickFalse);
1451}
1452
1453/*
1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455% %
1456% %
1457% %
1458+ D r a w D a s h P o l y g o n %
1459% %
1460% %
1461% %
1462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463%
1464% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1465% image while respecting the dash offset and dash pattern attributes.
1466%
1467% The format of the DrawDashPolygon method is:
1468%
1469% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001470% const PrimitiveInfo *primitive_info,Image *image,
1471% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001472%
1473% A description of each parameter follows:
1474%
1475% o draw_info: the draw info.
1476%
1477% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1478%
1479% o image: the image.
1480%
cristy947cb4c2011-10-20 18:41:46 +00001481% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001482%
1483*/
1484static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001485 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001486{
1487 DrawInfo
1488 *clone_info;
1489
cristy3ed852e2009-09-05 21:47:34 +00001490 MagickRealType
1491 length,
1492 maximum_length,
1493 offset,
1494 scale,
1495 total_length;
1496
1497 MagickStatusType
1498 status;
1499
1500 PrimitiveInfo
1501 *dash_polygon;
1502
cristybb503372010-05-27 20:51:26 +00001503 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001504 i;
1505
1506 register MagickRealType
1507 dx,
1508 dy;
1509
cristybb503372010-05-27 20:51:26 +00001510 size_t
cristy3ed852e2009-09-05 21:47:34 +00001511 number_vertices;
1512
cristy826a5472010-08-31 23:21:38 +00001513 ssize_t
1514 j,
1515 n;
1516
cristy3ed852e2009-09-05 21:47:34 +00001517 assert(draw_info != (const DrawInfo *) NULL);
1518 if (image->debug != MagickFalse)
1519 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1520 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1521 clone_info->miterlimit=0;
1522 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
cristybb503372010-05-27 20:51:26 +00001523 number_vertices=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001524 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1525 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
1526 if (dash_polygon == (PrimitiveInfo *) NULL)
1527 return(MagickFalse);
1528 dash_polygon[0]=primitive_info[0];
1529 scale=ExpandAffine(&draw_info->affine);
1530 length=scale*(draw_info->dash_pattern[0]-0.5);
1531 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
1532 j=1;
1533 for (n=0; offset > 0.0; j=0)
1534 {
1535 if (draw_info->dash_pattern[n] <= 0.0)
1536 break;
1537 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1538 if (offset > length)
1539 {
1540 offset-=length;
1541 n++;
1542 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1543 continue;
1544 }
1545 if (offset < length)
1546 {
1547 length-=offset;
1548 offset=0.0;
1549 break;
1550 }
1551 offset=0.0;
1552 n++;
1553 }
1554 status=MagickTrue;
1555 maximum_length=0.0;
1556 total_length=0.0;
cristybb503372010-05-27 20:51:26 +00001557 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00001558 {
1559 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1560 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1561 maximum_length=hypot((double) dx,dy);
1562 if (length == 0.0)
1563 {
1564 n++;
1565 if (draw_info->dash_pattern[n] == 0.0)
1566 n=0;
1567 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1568 }
1569 for (total_length=0.0; (total_length+length) < maximum_length; )
1570 {
1571 total_length+=length;
1572 if ((n & 0x01) != 0)
1573 {
1574 dash_polygon[0]=primitive_info[0];
1575 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1576 total_length/maximum_length);
1577 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1578 total_length/maximum_length);
1579 j=1;
1580 }
1581 else
1582 {
cristybb503372010-05-27 20:51:26 +00001583 if ((j+1) > (ssize_t) (2*number_vertices))
cristy3ed852e2009-09-05 21:47:34 +00001584 break;
1585 dash_polygon[j]=primitive_info[i-1];
1586 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1587 total_length/maximum_length);
1588 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1589 total_length/maximum_length);
1590 dash_polygon[j].coordinates=1;
1591 j++;
cristybb503372010-05-27 20:51:26 +00001592 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001593 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001594 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001595 }
1596 n++;
1597 if (draw_info->dash_pattern[n] == 0.0)
1598 n=0;
1599 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1600 }
1601 length-=(maximum_length-total_length);
1602 if ((n & 0x01) != 0)
1603 continue;
1604 dash_polygon[j]=primitive_info[i];
1605 dash_polygon[j].coordinates=1;
1606 j++;
1607 }
1608 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
1609 {
1610 dash_polygon[j]=primitive_info[i-1];
1611 dash_polygon[j].point.x+=MagickEpsilon;
1612 dash_polygon[j].point.y+=MagickEpsilon;
1613 dash_polygon[j].coordinates=1;
1614 j++;
cristybb503372010-05-27 20:51:26 +00001615 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001616 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001617 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001618 }
1619 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1620 clone_info=DestroyDrawInfo(clone_info);
1621 if (image->debug != MagickFalse)
1622 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1623 return(status != 0 ? MagickTrue : MagickFalse);
1624}
1625
1626/*
1627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628% %
1629% %
1630% %
1631% D r a w I m a g e %
1632% %
1633% %
1634% %
1635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636%
1637% DrawImage() draws a graphic primitive on your image. The primitive
1638% may be represented as a string or filename. Precede the filename with an
1639% "at" sign (@) and the contents of the file are drawn on the image. You
1640% can affect how text is drawn by setting one or more members of the draw
1641% info structure.
1642%
1643% The format of the DrawImage method is:
1644%
cristy018f07f2011-09-04 21:15:19 +00001645% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1646% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001647%
1648% A description of each parameter follows:
1649%
1650% o image: the image.
1651%
1652% o draw_info: the draw info.
1653%
cristy018f07f2011-09-04 21:15:19 +00001654% o exception: return any errors or warnings in this structure.
1655%
cristy3ed852e2009-09-05 21:47:34 +00001656*/
1657
1658static inline MagickBooleanType IsPoint(const char *point)
1659{
1660 char
1661 *p;
1662
1663 double
1664 value;
1665
cristydbdd0e32011-11-04 23:29:40 +00001666 value=StringToDouble(point,&p);
cristy3ed852e2009-09-05 21:47:34 +00001667 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1668}
1669
1670static inline void TracePoint(PrimitiveInfo *primitive_info,
1671 const PointInfo point)
1672{
1673 primitive_info->coordinates=1;
1674 primitive_info->point=point;
1675}
1676
cristy018f07f2011-09-04 21:15:19 +00001677MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1678 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001679{
1680#define RenderImageTag "Render/Image"
1681
1682 AffineMatrix
1683 affine,
1684 current;
1685
1686 char
1687 key[2*MaxTextExtent],
1688 keyword[MaxTextExtent],
1689 geometry[MaxTextExtent],
1690 name[MaxTextExtent],
1691 pattern[MaxTextExtent],
1692 *primitive,
1693 *token;
1694
1695 const char
1696 *q;
1697
1698 DrawInfo
1699 **graphic_context;
1700
cristy3ed852e2009-09-05 21:47:34 +00001701 MagickBooleanType
1702 proceed,
1703 status;
1704
1705 MagickRealType
1706 angle,
1707 factor,
1708 primitive_extent;
1709
1710 PointInfo
1711 point;
1712
cristy101ab702011-10-13 13:06:32 +00001713 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001714 start_color;
1715
1716 PrimitiveInfo
1717 *primitive_info;
1718
1719 PrimitiveType
1720 primitive_type;
1721
1722 register const char
1723 *p;
1724
cristybb503372010-05-27 20:51:26 +00001725 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001726 i,
1727 x;
1728
1729 SegmentInfo
1730 bounds;
1731
1732 size_t
cristy826a5472010-08-31 23:21:38 +00001733 length,
cristy3ed852e2009-09-05 21:47:34 +00001734 number_points;
1735
cristy826a5472010-08-31 23:21:38 +00001736 ssize_t
1737 j,
1738 k,
1739 n;
1740
cristy3ed852e2009-09-05 21:47:34 +00001741 /*
1742 Ensure the annotation info is valid.
1743 */
1744 assert(image != (Image *) NULL);
1745 assert(image->signature == MagickSignature);
1746 if (image->debug != MagickFalse)
1747 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1748 assert(draw_info != (DrawInfo *) NULL);
1749 assert(draw_info->signature == MagickSignature);
1750 if (image->debug != MagickFalse)
1751 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1752 if ((draw_info->primitive == (char *) NULL) ||
1753 (*draw_info->primitive == '\0'))
1754 return(MagickFalse);
1755 if (image->debug != MagickFalse)
1756 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
1757 if (*draw_info->primitive != '@')
1758 primitive=AcquireString(draw_info->primitive);
1759 else
cristy947cb4c2011-10-20 18:41:46 +00001760 primitive=FileToString(draw_info->primitive+1,~0,exception);
cristy3ed852e2009-09-05 21:47:34 +00001761 if (primitive == (char *) NULL)
1762 return(MagickFalse);
1763 primitive_extent=(MagickRealType) strlen(primitive);
1764 (void) SetImageArtifact(image,"MVG",primitive);
1765 n=0;
1766 /*
1767 Allocate primitive info memory.
1768 */
cristy73bd4a52010-10-05 11:24:23 +00001769 graphic_context=(DrawInfo **) AcquireMagickMemory(
cristyed110712010-03-23 01:16:38 +00001770 sizeof(*graphic_context));
cristy3ed852e2009-09-05 21:47:34 +00001771 if (graphic_context == (DrawInfo **) NULL)
1772 {
1773 primitive=DestroyString(primitive);
1774 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1775 image->filename);
1776 }
1777 number_points=2047;
1778 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
1779 sizeof(*primitive_info));
1780 if (primitive_info == (PrimitiveInfo *) NULL)
1781 {
1782 primitive=DestroyString(primitive);
1783 for ( ; n >= 0; n--)
1784 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
1785 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
1786 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1787 image->filename);
1788 }
1789 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1790 graphic_context[n]->viewbox=image->page;
1791 if ((image->page.width == 0) || (image->page.height == 0))
1792 {
1793 graphic_context[n]->viewbox.width=image->columns;
1794 graphic_context[n]->viewbox.height=image->rows;
1795 }
1796 token=AcquireString(primitive);
cristy9950d572011-10-01 18:22:35 +00001797 (void) QueryColorCompliance("#000000",AllCompliance,&start_color,
cristy947cb4c2011-10-20 18:41:46 +00001798 exception);
1799 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001800 return(MagickFalse);
cristy9b7a4fc2012-04-08 22:26:56 +00001801 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1802 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001803 status=MagickTrue;
1804 for (q=primitive; *q != '\0'; )
1805 {
1806 /*
1807 Interpret graphic primitive.
1808 */
1809 GetMagickToken(q,&q,keyword);
1810 if (*keyword == '\0')
1811 break;
1812 if (*keyword == '#')
1813 {
1814 /*
1815 Comment.
1816 */
1817 while ((*q != '\n') && (*q != '\0'))
1818 q++;
1819 continue;
1820 }
1821 p=q-strlen(keyword)-1;
1822 primitive_type=UndefinedPrimitive;
1823 current=graphic_context[n]->affine;
1824 GetAffineMatrix(&affine);
1825 switch (*keyword)
1826 {
1827 case ';':
1828 break;
1829 case 'a':
1830 case 'A':
1831 {
1832 if (LocaleCompare("affine",keyword) == 0)
1833 {
1834 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001835 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001836 GetMagickToken(q,&q,token);
1837 if (*token == ',')
1838 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001839 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001840 GetMagickToken(q,&q,token);
1841 if (*token == ',')
1842 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001843 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001844 GetMagickToken(q,&q,token);
1845 if (*token == ',')
1846 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001847 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001848 GetMagickToken(q,&q,token);
1849 if (*token == ',')
1850 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001851 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001852 GetMagickToken(q,&q,token);
1853 if (*token == ',')
1854 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001855 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001856 break;
1857 }
1858 if (LocaleCompare("arc",keyword) == 0)
1859 {
1860 primitive_type=ArcPrimitive;
1861 break;
1862 }
1863 status=MagickFalse;
1864 break;
1865 }
1866 case 'b':
1867 case 'B':
1868 {
1869 if (LocaleCompare("bezier",keyword) == 0)
1870 {
1871 primitive_type=BezierPrimitive;
1872 break;
1873 }
1874 if (LocaleCompare("border-color",keyword) == 0)
1875 {
1876 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00001877 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00001878 &graphic_context[n]->border_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00001879 break;
1880 }
1881 status=MagickFalse;
1882 break;
1883 }
1884 case 'c':
1885 case 'C':
1886 {
1887 if (LocaleCompare("clip-path",keyword) == 0)
1888 {
1889 /*
1890 Create clip mask.
1891 */
1892 GetMagickToken(q,&q,token);
1893 (void) CloneString(&graphic_context[n]->clip_mask,token);
1894 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00001895 graphic_context[n]->clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001896 break;
1897 }
1898 if (LocaleCompare("clip-rule",keyword) == 0)
1899 {
cristybb503372010-05-27 20:51:26 +00001900 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001901 fill_rule;
1902
1903 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001904 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001905 token);
1906 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00001907 status=MagickFalse;
1908 else
1909 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00001910 break;
1911 }
1912 if (LocaleCompare("clip-units",keyword) == 0)
1913 {
cristybb503372010-05-27 20:51:26 +00001914 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001915 clip_units;
1916
1917 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001918 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001919 token);
1920 if (clip_units == -1)
1921 {
1922 status=MagickFalse;
1923 break;
1924 }
1925 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1926 if (clip_units == ObjectBoundingBox)
1927 {
1928 GetAffineMatrix(&current);
1929 affine.sx=draw_info->bounds.x2;
1930 affine.sy=draw_info->bounds.y2;
1931 affine.tx=draw_info->bounds.x1;
1932 affine.ty=draw_info->bounds.y1;
1933 break;
1934 }
1935 break;
1936 }
1937 if (LocaleCompare("circle",keyword) == 0)
1938 {
1939 primitive_type=CirclePrimitive;
1940 break;
1941 }
1942 if (LocaleCompare("color",keyword) == 0)
1943 {
1944 primitive_type=ColorPrimitive;
1945 break;
1946 }
1947 status=MagickFalse;
1948 break;
1949 }
1950 case 'd':
1951 case 'D':
1952 {
1953 if (LocaleCompare("decorate",keyword) == 0)
1954 {
cristybb503372010-05-27 20:51:26 +00001955 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001956 decorate;
1957
1958 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001959 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001960 token);
1961 if (decorate == -1)
anthony2a021472011-10-08 11:29:29 +00001962 status=MagickFalse;
1963 else
1964 graphic_context[n]->decorate=(DecorationType) decorate;
cristy3ed852e2009-09-05 21:47:34 +00001965 break;
1966 }
1967 status=MagickFalse;
1968 break;
1969 }
1970 case 'e':
1971 case 'E':
1972 {
1973 if (LocaleCompare("ellipse",keyword) == 0)
1974 {
1975 primitive_type=EllipsePrimitive;
1976 break;
1977 }
1978 if (LocaleCompare("encoding",keyword) == 0)
1979 {
1980 GetMagickToken(q,&q,token);
1981 (void) CloneString(&graphic_context[n]->encoding,token);
1982 break;
1983 }
1984 status=MagickFalse;
1985 break;
1986 }
1987 case 'f':
1988 case 'F':
1989 {
1990 if (LocaleCompare("fill",keyword) == 0)
1991 {
1992 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00001993 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00001994 if (GetImageArtifact(image,pattern) != (const char *) NULL)
1995 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00001996 &graphic_context[n]->fill_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00001997 else
1998 {
cristy9950d572011-10-01 18:22:35 +00001999 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002000 &graphic_context[n]->fill,exception);
cristy3ed852e2009-09-05 21:47:34 +00002001 if (status == MagickFalse)
2002 {
2003 ImageInfo
2004 *pattern_info;
2005
2006 pattern_info=AcquireImageInfo();
2007 (void) CopyMagickString(pattern_info->filename,token,
2008 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00002009 graphic_context[n]->fill_pattern=ReadImage(pattern_info,
2010 exception);
2011 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002012 pattern_info=DestroyImageInfo(pattern_info);
2013 }
2014 }
2015 break;
2016 }
cristy69fcc592012-04-28 18:39:59 +00002017 if (LocaleCompare("fill-alpha",keyword) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 GetMagickToken(q,&q,token);
2020 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002021 graphic_context[n]->fill.alpha=(MagickRealType) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002022 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002023 break;
2024 }
2025 if (LocaleCompare("fill-rule",keyword) == 0)
2026 {
cristybb503372010-05-27 20:51:26 +00002027 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002028 fill_rule;
2029
2030 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002031 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002032 token);
2033 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00002034 status=MagickFalse;
2035 else
2036 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00002037 break;
2038 }
2039 if (LocaleCompare("font",keyword) == 0)
2040 {
2041 GetMagickToken(q,&q,token);
2042 (void) CloneString(&graphic_context[n]->font,token);
2043 if (LocaleCompare("none",token) == 0)
2044 graphic_context[n]->font=(char *)
2045 RelinquishMagickMemory(graphic_context[n]->font);
2046 break;
2047 }
2048 if (LocaleCompare("font-family",keyword) == 0)
2049 {
2050 GetMagickToken(q,&q,token);
2051 (void) CloneString(&graphic_context[n]->family,token);
2052 break;
2053 }
2054 if (LocaleCompare("font-size",keyword) == 0)
2055 {
2056 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002057 graphic_context[n]->pointsize=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002058 break;
2059 }
2060 if (LocaleCompare("font-stretch",keyword) == 0)
2061 {
cristybb503372010-05-27 20:51:26 +00002062 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002063 stretch;
2064
2065 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002066 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002067 if (stretch == -1)
anthony2a021472011-10-08 11:29:29 +00002068 status=MagickFalse;
2069 else
2070 graphic_context[n]->stretch=(StretchType) stretch;
cristy3ed852e2009-09-05 21:47:34 +00002071 break;
2072 }
2073 if (LocaleCompare("font-style",keyword) == 0)
2074 {
cristybb503372010-05-27 20:51:26 +00002075 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002076 style;
2077
2078 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002079 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002080 if (style == -1)
anthony2a021472011-10-08 11:29:29 +00002081 status=MagickFalse;
2082 else
2083 graphic_context[n]->style=(StyleType) style;
cristy3ed852e2009-09-05 21:47:34 +00002084 break;
2085 }
2086 if (LocaleCompare("font-weight",keyword) == 0)
2087 {
2088 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002089 graphic_context[n]->weight=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002090 if (LocaleCompare(token,"all") == 0)
2091 graphic_context[n]->weight=0;
2092 if (LocaleCompare(token,"bold") == 0)
2093 graphic_context[n]->weight=700;
2094 if (LocaleCompare(token,"bolder") == 0)
2095 if (graphic_context[n]->weight <= 800)
2096 graphic_context[n]->weight+=100;
2097 if (LocaleCompare(token,"lighter") == 0)
2098 if (graphic_context[n]->weight >= 100)
2099 graphic_context[n]->weight-=100;
2100 if (LocaleCompare(token,"normal") == 0)
2101 graphic_context[n]->weight=400;
2102 break;
2103 }
2104 status=MagickFalse;
2105 break;
2106 }
2107 case 'g':
2108 case 'G':
2109 {
2110 if (LocaleCompare("gradient-units",keyword) == 0)
2111 {
2112 GetMagickToken(q,&q,token);
2113 break;
2114 }
2115 if (LocaleCompare("gravity",keyword) == 0)
2116 {
cristybb503372010-05-27 20:51:26 +00002117 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002118 gravity;
2119
2120 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002121 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002122 if (gravity == -1)
anthony2a021472011-10-08 11:29:29 +00002123 status=MagickFalse;
2124 else
2125 graphic_context[n]->gravity=(GravityType) gravity;
cristy3ed852e2009-09-05 21:47:34 +00002126 break;
2127 }
2128 status=MagickFalse;
2129 break;
2130 }
2131 case 'i':
2132 case 'I':
2133 {
2134 if (LocaleCompare("image",keyword) == 0)
2135 {
cristybb503372010-05-27 20:51:26 +00002136 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002137 compose;
2138
2139 primitive_type=ImagePrimitive;
2140 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002141 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002142 if (compose == -1)
anthony2a021472011-10-08 11:29:29 +00002143 status=MagickFalse;
2144 else
2145 graphic_context[n]->compose=(CompositeOperator) compose;
cristy3ed852e2009-09-05 21:47:34 +00002146 break;
2147 }
cristyb32b90a2009-09-07 21:45:48 +00002148 if (LocaleCompare("interline-spacing",keyword) == 0)
2149 {
2150 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002151 graphic_context[n]->interline_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002152 (char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00002153 break;
2154 }
cristy3ed852e2009-09-05 21:47:34 +00002155 if (LocaleCompare("interword-spacing",keyword) == 0)
2156 {
2157 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002158 graphic_context[n]->interword_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002159 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002160 break;
2161 }
2162 status=MagickFalse;
2163 break;
2164 }
2165 case 'k':
2166 case 'K':
2167 {
2168 if (LocaleCompare("kerning",keyword) == 0)
2169 {
2170 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002171 graphic_context[n]->kerning=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002172 break;
2173 }
2174 status=MagickFalse;
2175 break;
2176 }
2177 case 'l':
2178 case 'L':
2179 {
2180 if (LocaleCompare("line",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002181 primitive_type=LinePrimitive;
2182 else
2183 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002184 break;
2185 }
2186 case 'm':
2187 case 'M':
2188 {
2189 if (LocaleCompare("matte",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002190 primitive_type=MattePrimitive;
2191 else
2192 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002193 break;
2194 }
2195 case 'o':
2196 case 'O':
2197 {
2198 if (LocaleCompare("offset",keyword) == 0)
2199 {
2200 GetMagickToken(q,&q,token);
2201 break;
2202 }
2203 if (LocaleCompare("opacity",keyword) == 0)
2204 {
2205 GetMagickToken(q,&q,token);
2206 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristy4c08aed2011-07-01 19:47:50 +00002207 graphic_context[n]->alpha=ClampToQuantum((MagickRealType)
2208 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->alpha)*
cristydbdd0e32011-11-04 23:29:40 +00002209 factor*StringToDouble(token,(char **) NULL))));
cristyda1f9c12011-10-02 21:39:49 +00002210 graphic_context[n]->fill.alpha=(double) graphic_context[n]->alpha;
2211 graphic_context[n]->stroke.alpha=(double) graphic_context[n]->alpha;
cristy3ed852e2009-09-05 21:47:34 +00002212 break;
2213 }
2214 status=MagickFalse;
2215 break;
2216 }
2217 case 'p':
2218 case 'P':
2219 {
2220 if (LocaleCompare("path",keyword) == 0)
2221 {
2222 primitive_type=PathPrimitive;
2223 break;
2224 }
2225 if (LocaleCompare("point",keyword) == 0)
2226 {
2227 primitive_type=PointPrimitive;
2228 break;
2229 }
2230 if (LocaleCompare("polyline",keyword) == 0)
2231 {
2232 primitive_type=PolylinePrimitive;
2233 break;
2234 }
2235 if (LocaleCompare("polygon",keyword) == 0)
2236 {
2237 primitive_type=PolygonPrimitive;
2238 break;
2239 }
2240 if (LocaleCompare("pop",keyword) == 0)
2241 {
2242 GetMagickToken(q,&q,token);
2243 if (LocaleCompare("clip-path",token) == 0)
2244 break;
2245 if (LocaleCompare("defs",token) == 0)
2246 break;
2247 if (LocaleCompare("gradient",token) == 0)
2248 break;
2249 if (LocaleCompare("graphic-context",token) == 0)
2250 {
2251 if (n <= 0)
2252 {
cristy947cb4c2011-10-20 18:41:46 +00002253 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002254 DrawError,"UnbalancedGraphicContextPushPop","'%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002255 n=0;
2256 break;
2257 }
2258 if (graphic_context[n]->clip_mask != (char *) NULL)
2259 if (LocaleCompare(graphic_context[n]->clip_mask,
2260 graphic_context[n-1]->clip_mask) != 0)
cristye42f6582012-02-11 17:59:50 +00002261 (void) SetImageMask(image,(Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002262 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2263 n--;
2264 break;
2265 }
2266 if (LocaleCompare("pattern",token) == 0)
2267 break;
2268 status=MagickFalse;
2269 break;
2270 }
2271 if (LocaleCompare("push",keyword) == 0)
2272 {
2273 GetMagickToken(q,&q,token);
2274 if (LocaleCompare("clip-path",token) == 0)
2275 {
2276 char
2277 name[MaxTextExtent];
2278
2279 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002280 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002281 for (p=q; *q != '\0'; )
2282 {
2283 GetMagickToken(q,&q,token);
2284 if (LocaleCompare(token,"pop") != 0)
2285 continue;
2286 GetMagickToken(q,(const char **) NULL,token);
2287 if (LocaleCompare(token,"clip-path") != 0)
2288 continue;
2289 break;
2290 }
2291 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2292 (void) SetImageArtifact(image,name,token);
2293 GetMagickToken(q,&q,token);
2294 break;
2295 }
2296 if (LocaleCompare("gradient",token) == 0)
2297 {
2298 char
2299 key[2*MaxTextExtent],
2300 name[MaxTextExtent],
2301 type[MaxTextExtent];
2302
cristy3ed852e2009-09-05 21:47:34 +00002303 SegmentInfo
2304 segment;
2305
2306 GetMagickToken(q,&q,token);
2307 (void) CopyMagickString(name,token,MaxTextExtent);
2308 GetMagickToken(q,&q,token);
2309 (void) CopyMagickString(type,token,MaxTextExtent);
2310 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002311 segment.x1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002312 GetMagickToken(q,&q,token);
2313 if (*token == ',')
2314 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002315 segment.y1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002316 GetMagickToken(q,&q,token);
2317 if (*token == ',')
2318 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002319 segment.x2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002320 GetMagickToken(q,&q,token);
2321 if (*token == ',')
2322 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002323 segment.y2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002324 if (LocaleCompare(type,"radial") == 0)
2325 {
2326 GetMagickToken(q,&q,token);
2327 if (*token == ',')
2328 GetMagickToken(q,&q,token);
cristy3ed852e2009-09-05 21:47:34 +00002329 }
2330 for (p=q; *q != '\0'; )
2331 {
2332 GetMagickToken(q,&q,token);
2333 if (LocaleCompare(token,"pop") != 0)
2334 continue;
2335 GetMagickToken(q,(const char **) NULL,token);
2336 if (LocaleCompare(token,"gradient") != 0)
2337 continue;
2338 break;
2339 }
2340 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2341 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2342 graphic_context[n]->affine.ry*segment.y1+
2343 graphic_context[n]->affine.tx;
2344 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2345 graphic_context[n]->affine.sy*segment.y1+
2346 graphic_context[n]->affine.ty;
2347 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2348 graphic_context[n]->affine.ry*segment.y2+
2349 graphic_context[n]->affine.tx;
2350 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2351 graphic_context[n]->affine.sy*segment.y2+
2352 graphic_context[n]->affine.ty;
cristyb51dff52011-05-19 16:55:47 +00002353 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002354 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002355 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2356 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002357 "%gx%g%+.15g%+.15g",
cristy3ed852e2009-09-05 21:47:34 +00002358 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2359 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2360 bounds.x1,bounds.y1);
2361 (void) SetImageArtifact(image,key,geometry);
2362 GetMagickToken(q,&q,token);
2363 break;
2364 }
2365 if (LocaleCompare("pattern",token) == 0)
2366 {
2367 RectangleInfo
2368 bounds;
2369
2370 GetMagickToken(q,&q,token);
2371 (void) CopyMagickString(name,token,MaxTextExtent);
2372 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002373 bounds.x=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2374 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002375 GetMagickToken(q,&q,token);
2376 if (*token == ',')
2377 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002378 bounds.y=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2379 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002380 GetMagickToken(q,&q,token);
2381 if (*token == ',')
2382 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002383 bounds.width=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002384 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002385 GetMagickToken(q,&q,token);
2386 if (*token == ',')
2387 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002388 bounds.height=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002389 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002390 for (p=q; *q != '\0'; )
2391 {
2392 GetMagickToken(q,&q,token);
2393 if (LocaleCompare(token,"pop") != 0)
2394 continue;
2395 GetMagickToken(q,(const char **) NULL,token);
2396 if (LocaleCompare(token,"pattern") != 0)
2397 continue;
2398 break;
2399 }
2400 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
cristyb51dff52011-05-19 16:55:47 +00002401 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002402 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002403 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2404 (void) FormatLocaleString(geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002405 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002406 bounds.height,(double) bounds.x,(double) bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00002407 (void) SetImageArtifact(image,key,geometry);
2408 GetMagickToken(q,&q,token);
2409 break;
2410 }
2411 if (LocaleCompare("graphic-context",token) == 0)
2412 {
2413 n++;
2414 graphic_context=(DrawInfo **) ResizeQuantumMemory(
2415 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2416 if (graphic_context == (DrawInfo **) NULL)
2417 {
cristy947cb4c2011-10-20 18:41:46 +00002418 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002419 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristy947cb4c2011-10-20 18:41:46 +00002420 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002421 break;
2422 }
2423 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2424 graphic_context[n-1]);
2425 break;
2426 }
2427 if (LocaleCompare("defs",token) == 0)
2428 break;
2429 status=MagickFalse;
2430 break;
2431 }
2432 status=MagickFalse;
2433 break;
2434 }
2435 case 'r':
2436 case 'R':
2437 {
2438 if (LocaleCompare("rectangle",keyword) == 0)
2439 {
2440 primitive_type=RectanglePrimitive;
2441 break;
2442 }
2443 if (LocaleCompare("rotate",keyword) == 0)
2444 {
2445 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002446 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002447 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2448 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2449 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2450 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2451 break;
2452 }
2453 if (LocaleCompare("roundRectangle",keyword) == 0)
2454 {
2455 primitive_type=RoundRectanglePrimitive;
2456 break;
2457 }
2458 status=MagickFalse;
2459 break;
2460 }
2461 case 's':
2462 case 'S':
2463 {
2464 if (LocaleCompare("scale",keyword) == 0)
2465 {
2466 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002467 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002468 GetMagickToken(q,&q,token);
2469 if (*token == ',')
2470 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002471 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002472 break;
2473 }
2474 if (LocaleCompare("skewX",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.ry=sin(DegreesToRadians(angle));
2479 break;
2480 }
2481 if (LocaleCompare("skewY",keyword) == 0)
2482 {
2483 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002484 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002485 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2486 break;
2487 }
2488 if (LocaleCompare("stop-color",keyword) == 0)
2489 {
cristy101ab702011-10-13 13:06:32 +00002490 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002491 stop_color;
2492
2493 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002494 (void) QueryColorCompliance(token,AllCompliance,&stop_color,
cristy947cb4c2011-10-20 18:41:46 +00002495 exception);
cristy3ed852e2009-09-05 21:47:34 +00002496 (void) GradientImage(image,LinearGradient,ReflectSpread,
cristy947cb4c2011-10-20 18:41:46 +00002497 &start_color,&stop_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00002498 start_color=stop_color;
2499 GetMagickToken(q,&q,token);
2500 break;
2501 }
2502 if (LocaleCompare("stroke",keyword) == 0)
2503 {
2504 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002505 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002506 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2507 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00002508 &graphic_context[n]->stroke_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002509 else
2510 {
cristy9950d572011-10-01 18:22:35 +00002511 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002512 &graphic_context[n]->stroke,exception);
cristy3ed852e2009-09-05 21:47:34 +00002513 if (status == MagickFalse)
2514 {
2515 ImageInfo
2516 *pattern_info;
2517
2518 pattern_info=AcquireImageInfo();
2519 (void) CopyMagickString(pattern_info->filename,token,
2520 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00002521 graphic_context[n]->stroke_pattern=ReadImage(pattern_info,
2522 exception);
2523 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002524 pattern_info=DestroyImageInfo(pattern_info);
2525 }
2526 }
2527 break;
2528 }
2529 if (LocaleCompare("stroke-antialias",keyword) == 0)
2530 {
2531 GetMagickToken(q,&q,token);
2532 graphic_context[n]->stroke_antialias=
cristyf2f27272009-12-17 14:48:46 +00002533 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002534 break;
2535 }
2536 if (LocaleCompare("stroke-dasharray",keyword) == 0)
2537 {
2538 if (graphic_context[n]->dash_pattern != (double *) NULL)
2539 graphic_context[n]->dash_pattern=(double *)
2540 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2541 if (IsPoint(q) != MagickFalse)
2542 {
2543 const char
2544 *p;
2545
2546 p=q;
2547 GetMagickToken(p,&p,token);
2548 if (*token == ',')
2549 GetMagickToken(p,&p,token);
2550 for (x=0; IsPoint(token) != MagickFalse; x++)
2551 {
2552 GetMagickToken(p,&p,token);
2553 if (*token == ',')
2554 GetMagickToken(p,&p,token);
2555 }
2556 graphic_context[n]->dash_pattern=(double *)
2557 AcquireQuantumMemory((size_t) (2UL*x+1UL),
2558 sizeof(*graphic_context[n]->dash_pattern));
2559 if (graphic_context[n]->dash_pattern == (double *) NULL)
2560 {
cristy947cb4c2011-10-20 18:41:46 +00002561 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002562 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristy947cb4c2011-10-20 18:41:46 +00002563 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002564 break;
2565 }
2566 for (j=0; j < x; j++)
2567 {
2568 GetMagickToken(q,&q,token);
2569 if (*token == ',')
2570 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002571 graphic_context[n]->dash_pattern[j]=StringToDouble(token,
2572 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002573 }
2574 if ((x & 0x01) != 0)
2575 for ( ; j < (2*x); j++)
2576 graphic_context[n]->dash_pattern[j]=
2577 graphic_context[n]->dash_pattern[j-x];
2578 graphic_context[n]->dash_pattern[j]=0.0;
2579 break;
2580 }
2581 GetMagickToken(q,&q,token);
2582 break;
2583 }
2584 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2585 {
2586 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002587 graphic_context[n]->dash_offset=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002588 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002589 break;
2590 }
2591 if (LocaleCompare("stroke-linecap",keyword) == 0)
2592 {
cristybb503372010-05-27 20:51:26 +00002593 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002594 linecap;
2595
2596 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002597 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002598 if (linecap == -1)
anthony2a021472011-10-08 11:29:29 +00002599 status=MagickFalse;
2600 else
2601 graphic_context[n]->linecap=(LineCap) linecap;
cristy3ed852e2009-09-05 21:47:34 +00002602 break;
2603 }
2604 if (LocaleCompare("stroke-linejoin",keyword) == 0)
2605 {
cristybb503372010-05-27 20:51:26 +00002606 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002607 linejoin;
2608
2609 GetMagickToken(q,&q,token);
cristy7118edf2011-10-08 13:33:25 +00002610 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
2611 token);
cristy3ed852e2009-09-05 21:47:34 +00002612 if (linejoin == -1)
anthony2a021472011-10-08 11:29:29 +00002613 status=MagickFalse;
2614 else
2615 graphic_context[n]->linejoin=(LineJoin) linejoin;
cristy3ed852e2009-09-05 21:47:34 +00002616 break;
2617 }
2618 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2619 {
2620 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002621 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002622 break;
2623 }
2624 if (LocaleCompare("stroke-opacity",keyword) == 0)
2625 {
2626 GetMagickToken(q,&q,token);
2627 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002628 graphic_context[n]->stroke.alpha=(MagickRealType) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002629 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002630 break;
2631 }
2632 if (LocaleCompare("stroke-width",keyword) == 0)
2633 {
2634 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002635 graphic_context[n]->stroke_width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002636 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002637 break;
2638 }
2639 status=MagickFalse;
2640 break;
2641 }
2642 case 't':
2643 case 'T':
2644 {
2645 if (LocaleCompare("text",keyword) == 0)
2646 {
2647 primitive_type=TextPrimitive;
2648 break;
2649 }
2650 if (LocaleCompare("text-align",keyword) == 0)
2651 {
cristybb503372010-05-27 20:51:26 +00002652 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002653 align;
2654
2655 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002656 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002657 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002658 status=MagickFalse;
2659 else
2660 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002661 break;
2662 }
2663 if (LocaleCompare("text-anchor",keyword) == 0)
2664 {
cristybb503372010-05-27 20:51:26 +00002665 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002666 align;
2667
2668 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002669 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002670 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002671 status=MagickFalse;
2672 else
2673 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002674 break;
2675 }
2676 if (LocaleCompare("text-antialias",keyword) == 0)
2677 {
2678 GetMagickToken(q,&q,token);
2679 graphic_context[n]->text_antialias=
cristyf2f27272009-12-17 14:48:46 +00002680 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002681 break;
2682 }
2683 if (LocaleCompare("text-undercolor",keyword) == 0)
2684 {
2685 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002686 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002687 &graphic_context[n]->undercolor,exception);
cristy3ed852e2009-09-05 21:47:34 +00002688 break;
2689 }
2690 if (LocaleCompare("translate",keyword) == 0)
2691 {
2692 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002693 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002694 GetMagickToken(q,&q,token);
2695 if (*token == ',')
2696 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002697 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002698 break;
2699 }
2700 status=MagickFalse;
2701 break;
2702 }
2703 case 'v':
2704 case 'V':
2705 {
2706 if (LocaleCompare("viewbox",keyword) == 0)
2707 {
2708 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002709 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
2710 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002711 GetMagickToken(q,&q,token);
2712 if (*token == ',')
2713 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002714 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
2715 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002716 GetMagickToken(q,&q,token);
2717 if (*token == ',')
2718 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002719 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
2720 token,(char **) NULL)+0.5);
cristy06609ee2010-03-17 20:21:27 +00002721 GetMagickToken(q,&q,token);
2722 if (*token == ',')
2723 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002724 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
2725 token,(char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002726 break;
2727 }
2728 status=MagickFalse;
2729 break;
2730 }
2731 default:
2732 {
2733 status=MagickFalse;
2734 break;
2735 }
2736 }
2737 if (status == MagickFalse)
2738 break;
2739 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2740 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2741 {
cristyfbd70092010-12-01 19:31:14 +00002742 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2743 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2744 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2745 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2746 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2747 current.tx;
2748 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2749 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002750 }
2751 if (primitive_type == UndefinedPrimitive)
2752 {
2753 if (image->debug != MagickFalse)
2754 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2755 (int) (q-p),p);
2756 continue;
2757 }
2758 /*
2759 Parse the primitive attributes.
2760 */
2761 i=0;
2762 j=0;
2763 primitive_info[0].point.x=0.0;
2764 primitive_info[0].point.y=0.0;
2765 for (x=0; *q != '\0'; x++)
2766 {
2767 /*
2768 Define points.
2769 */
2770 if (IsPoint(q) == MagickFalse)
2771 break;
2772 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002773 point.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002774 GetMagickToken(q,&q,token);
2775 if (*token == ',')
2776 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002777 point.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002778 GetMagickToken(q,(const char **) NULL,token);
2779 if (*token == ',')
2780 GetMagickToken(q,&q,token);
2781 primitive_info[i].primitive=primitive_type;
2782 primitive_info[i].point=point;
2783 primitive_info[i].coordinates=0;
2784 primitive_info[i].method=FloodfillMethod;
2785 i++;
cristybb503372010-05-27 20:51:26 +00002786 if (i < (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +00002787 continue;
2788 number_points<<=1;
2789 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2790 (size_t) number_points,sizeof(*primitive_info));
2791 if (primitive_info == (PrimitiveInfo *) NULL)
2792 {
cristy947cb4c2011-10-20 18:41:46 +00002793 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002794 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002795 break;
2796 }
2797 }
2798 primitive_info[j].primitive=primitive_type;
cristybb503372010-05-27 20:51:26 +00002799 primitive_info[j].coordinates=(size_t) x;
cristy3ed852e2009-09-05 21:47:34 +00002800 primitive_info[j].method=FloodfillMethod;
2801 primitive_info[j].text=(char *) NULL;
2802 /*
2803 Circumscribe primitive within a circle.
2804 */
2805 bounds.x1=primitive_info[j].point.x;
2806 bounds.y1=primitive_info[j].point.y;
2807 bounds.x2=primitive_info[j].point.x;
2808 bounds.y2=primitive_info[j].point.y;
cristybb503372010-05-27 20:51:26 +00002809 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
cristy3ed852e2009-09-05 21:47:34 +00002810 {
2811 point=primitive_info[j+k].point;
2812 if (point.x < bounds.x1)
2813 bounds.x1=point.x;
2814 if (point.y < bounds.y1)
2815 bounds.y1=point.y;
2816 if (point.x > bounds.x2)
2817 bounds.x2=point.x;
2818 if (point.y > bounds.y2)
2819 bounds.y2=point.y;
2820 }
2821 /*
2822 Speculate how many points our primitive might consume.
2823 */
2824 length=primitive_info[j].coordinates;
2825 switch (primitive_type)
2826 {
2827 case RectanglePrimitive:
2828 {
2829 length*=5;
2830 break;
2831 }
2832 case RoundRectanglePrimitive:
2833 {
cristy78817ad2010-05-07 12:25:34 +00002834 length*=5+8*BezierQuantum;
cristy3ed852e2009-09-05 21:47:34 +00002835 break;
2836 }
2837 case BezierPrimitive:
2838 {
2839 if (primitive_info[j].coordinates > 107)
cristy947cb4c2011-10-20 18:41:46 +00002840 (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
anthonye5b39652012-04-21 05:37:29 +00002841 "TooManyBezierCoordinates","'%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002842 length=BezierQuantum*primitive_info[j].coordinates;
2843 break;
2844 }
2845 case PathPrimitive:
2846 {
2847 char
2848 *s,
2849 *t;
2850
2851 GetMagickToken(q,&q,token);
cristy40a08ad2010-02-09 02:27:44 +00002852 length=1;
cristy3ed852e2009-09-05 21:47:34 +00002853 t=token;
2854 for (s=token; *s != '\0'; s=t)
2855 {
cristy00976d82011-02-20 20:31:28 +00002856 double
2857 value;
2858
cristydbdd0e32011-11-04 23:29:40 +00002859 value=StringToDouble(s,&t);
cristy00976d82011-02-20 20:31:28 +00002860 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00002861 if (s == t)
2862 {
2863 t++;
2864 continue;
2865 }
cristyef7a6ce2011-05-14 15:01:11 +00002866 length++;
cristy3ed852e2009-09-05 21:47:34 +00002867 }
cristyd2f832c2011-06-28 12:35:24 +00002868 length=length*BezierQuantum/2;
cristy3ed852e2009-09-05 21:47:34 +00002869 break;
2870 }
2871 case CirclePrimitive:
2872 case ArcPrimitive:
2873 case EllipsePrimitive:
2874 {
2875 MagickRealType
2876 alpha,
2877 beta,
2878 radius;
2879
2880 alpha=bounds.x2-bounds.x1;
2881 beta=bounds.y2-bounds.y1;
2882 radius=hypot((double) alpha,(double) beta);
cristyf53f26f2011-05-16 00:56:05 +00002883 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002884 break;
2885 }
2886 default:
2887 break;
2888 }
cristybb503372010-05-27 20:51:26 +00002889 if ((size_t) (i+length) >= number_points)
cristy3ed852e2009-09-05 21:47:34 +00002890 {
2891 /*
2892 Resize based on speculative points required by primitive.
2893 */
cristy9ce61b92010-05-12 16:30:26 +00002894 number_points+=length+1;
cristy3ed852e2009-09-05 21:47:34 +00002895 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2896 (size_t) number_points,sizeof(*primitive_info));
2897 if (primitive_info == (PrimitiveInfo *) NULL)
2898 {
cristy947cb4c2011-10-20 18:41:46 +00002899 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002900 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristy3ed852e2009-09-05 21:47:34 +00002901 image->filename);
2902 break;
2903 }
2904 }
2905 switch (primitive_type)
2906 {
2907 case PointPrimitive:
2908 default:
2909 {
2910 if (primitive_info[j].coordinates != 1)
2911 {
2912 status=MagickFalse;
2913 break;
2914 }
2915 TracePoint(primitive_info+j,primitive_info[j].point);
cristybb503372010-05-27 20:51:26 +00002916 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002917 break;
2918 }
2919 case LinePrimitive:
2920 {
2921 if (primitive_info[j].coordinates != 2)
2922 {
2923 status=MagickFalse;
2924 break;
2925 }
2926 TraceLine(primitive_info+j,primitive_info[j].point,
2927 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002928 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002929 break;
2930 }
2931 case RectanglePrimitive:
2932 {
2933 if (primitive_info[j].coordinates != 2)
2934 {
2935 status=MagickFalse;
2936 break;
2937 }
2938 TraceRectangle(primitive_info+j,primitive_info[j].point,
2939 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002940 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002941 break;
2942 }
2943 case RoundRectanglePrimitive:
2944 {
2945 if (primitive_info[j].coordinates != 3)
2946 {
2947 status=MagickFalse;
2948 break;
2949 }
2950 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
2951 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002952 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002953 break;
2954 }
2955 case ArcPrimitive:
2956 {
2957 if (primitive_info[j].coordinates != 3)
2958 {
2959 primitive_type=UndefinedPrimitive;
2960 break;
2961 }
2962 TraceArc(primitive_info+j,primitive_info[j].point,
2963 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002964 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002965 break;
2966 }
2967 case EllipsePrimitive:
2968 {
2969 if (primitive_info[j].coordinates != 3)
2970 {
2971 status=MagickFalse;
2972 break;
2973 }
2974 TraceEllipse(primitive_info+j,primitive_info[j].point,
2975 primitive_info[j+1].point,primitive_info[j+2].point);
cristybb503372010-05-27 20:51:26 +00002976 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002977 break;
2978 }
2979 case CirclePrimitive:
2980 {
2981 if (primitive_info[j].coordinates != 2)
2982 {
2983 status=MagickFalse;
2984 break;
2985 }
2986 TraceCircle(primitive_info+j,primitive_info[j].point,
2987 primitive_info[j+1].point);
cristybb503372010-05-27 20:51:26 +00002988 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002989 break;
2990 }
2991 case PolylinePrimitive:
2992 break;
2993 case PolygonPrimitive:
2994 {
2995 primitive_info[i]=primitive_info[j];
2996 primitive_info[i].coordinates=0;
2997 primitive_info[j].coordinates++;
2998 i++;
2999 break;
3000 }
3001 case BezierPrimitive:
3002 {
3003 if (primitive_info[j].coordinates < 3)
3004 {
3005 status=MagickFalse;
3006 break;
3007 }
3008 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
cristybb503372010-05-27 20:51:26 +00003009 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00003010 break;
3011 }
3012 case PathPrimitive:
3013 {
cristybb503372010-05-27 20:51:26 +00003014 i=(ssize_t) (j+TracePath(primitive_info+j,token));
cristy3ed852e2009-09-05 21:47:34 +00003015 break;
3016 }
3017 case ColorPrimitive:
3018 case MattePrimitive:
3019 {
cristybb503372010-05-27 20:51:26 +00003020 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003021 method;
3022
3023 if (primitive_info[j].coordinates != 1)
3024 {
3025 status=MagickFalse;
3026 break;
3027 }
3028 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00003029 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00003030 if (method == -1)
anthony2a021472011-10-08 11:29:29 +00003031 status=MagickFalse;
3032 else
3033 primitive_info[j].method=(PaintMethod) method;
cristy3ed852e2009-09-05 21:47:34 +00003034 break;
3035 }
3036 case TextPrimitive:
3037 {
3038 if (primitive_info[j].coordinates != 1)
3039 {
3040 status=MagickFalse;
3041 break;
3042 }
3043 if (*token != ',')
3044 GetMagickToken(q,&q,token);
3045 primitive_info[j].text=AcquireString(token);
3046 break;
3047 }
3048 case ImagePrimitive:
3049 {
3050 if (primitive_info[j].coordinates != 2)
3051 {
3052 status=MagickFalse;
3053 break;
3054 }
3055 GetMagickToken(q,&q,token);
3056 primitive_info[j].text=AcquireString(token);
3057 break;
3058 }
3059 }
3060 if (primitive_info == (PrimitiveInfo *) NULL)
3061 break;
3062 if (image->debug != MagickFalse)
3063 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3064 if (status == MagickFalse)
3065 break;
3066 primitive_info[i].primitive=UndefinedPrimitive;
3067 if (i == 0)
3068 continue;
3069 /*
3070 Transform points.
3071 */
3072 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3073 {
3074 point=primitive_info[i].point;
3075 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3076 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3077 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3078 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3079 point=primitive_info[i].point;
3080 if (point.x < graphic_context[n]->bounds.x1)
3081 graphic_context[n]->bounds.x1=point.x;
3082 if (point.y < graphic_context[n]->bounds.y1)
3083 graphic_context[n]->bounds.y1=point.y;
3084 if (point.x > graphic_context[n]->bounds.x2)
3085 graphic_context[n]->bounds.x2=point.x;
3086 if (point.y > graphic_context[n]->bounds.y2)
3087 graphic_context[n]->bounds.y2=point.y;
3088 if (primitive_info[i].primitive == ImagePrimitive)
3089 break;
cristybb503372010-05-27 20:51:26 +00003090 if (i >= (ssize_t) number_points)
cristy9ce61b92010-05-12 16:30:26 +00003091 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00003092 }
cristy3ed852e2009-09-05 21:47:34 +00003093 if (graphic_context[n]->render != MagickFalse)
3094 {
3095 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3096 (LocaleCompare(graphic_context[n]->clip_mask,
3097 graphic_context[n-1]->clip_mask) != 0))
3098 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00003099 graphic_context[n]->clip_mask,exception);
cristy947cb4c2011-10-20 18:41:46 +00003100 (void) DrawPrimitive(image,graphic_context[n],primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003101 }
3102 if (primitive_info->text != (char *) NULL)
3103 primitive_info->text=(char *) RelinquishMagickMemory(
3104 primitive_info->text);
3105 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3106 primitive_extent);
3107 if (proceed == MagickFalse)
3108 break;
3109 }
3110 if (image->debug != MagickFalse)
3111 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3112 /*
3113 Relinquish resources.
3114 */
3115 token=DestroyString(token);
3116 if (primitive_info != (PrimitiveInfo *) NULL)
3117 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3118 primitive=DestroyString(primitive);
3119 for ( ; n >= 0; n--)
3120 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3121 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3122 if (status == MagickFalse)
3123 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3124 keyword);
3125 return(status);
3126}
3127
3128/*
3129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3130% %
3131% %
3132% %
3133% D r a w G r a d i e n t I m a g e %
3134% %
3135% %
3136% %
3137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3138%
3139% DrawGradientImage() draws a linear gradient on the image.
3140%
3141% The format of the DrawGradientImage method is:
3142%
3143% MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003144% const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003145%
3146% A description of each parameter follows:
3147%
3148% o image: the image.
3149%
anthony2a021472011-10-08 11:29:29 +00003150% o draw_info: the draw info.
cristy3ed852e2009-09-05 21:47:34 +00003151%
cristy947cb4c2011-10-20 18:41:46 +00003152% o exception: return any errors or warnings in this structure.
3153%
cristy3ed852e2009-09-05 21:47:34 +00003154*/
3155
3156static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
cristybb503372010-05-27 20:51:26 +00003157 const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00003158{
3159 switch (gradient->type)
3160 {
3161 case UndefinedGradient:
3162 case LinearGradient:
3163 {
3164 MagickRealType
3165 gamma,
3166 length,
3167 offset,
3168 scale;
3169
3170 PointInfo
3171 p,
3172 q;
3173
3174 const SegmentInfo
3175 *gradient_vector;
3176
3177 gradient_vector=(&gradient->gradient_vector);
3178 p.x=gradient_vector->x2-gradient_vector->x1;
3179 p.y=gradient_vector->y2-gradient_vector->y1;
3180 q.x=(double) x-gradient_vector->x1;
3181 q.y=(double) y-gradient_vector->y1;
3182 length=sqrt(q.x*q.x+q.y*q.y);
3183 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
3184 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
3185 scale=p.x*q.x+p.y*q.y;
3186 offset=gamma*scale*length;
3187 return(offset);
3188 }
3189 case RadialGradient:
3190 {
3191 MagickRealType
3192 length,
3193 offset;
3194
3195 PointInfo
3196 v;
3197
3198 v.x=(double) x-gradient->center.x;
3199 v.y=(double) y-gradient->center.y;
3200 length=sqrt(v.x*v.x+v.y*v.y);
3201 if (gradient->spread == RepeatSpread)
3202 return(length);
3203 offset=length/gradient->radius;
3204 return(offset);
3205 }
3206 }
3207 return(0.0);
3208}
3209
3210MagickExport MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003211 const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003212{
cristyc4c8d132010-01-07 01:58:38 +00003213 CacheView
3214 *image_view;
3215
cristy3ed852e2009-09-05 21:47:34 +00003216 const GradientInfo
3217 *gradient;
3218
3219 const SegmentInfo
3220 *gradient_vector;
3221
cristy3ed852e2009-09-05 21:47:34 +00003222 MagickBooleanType
3223 status;
3224
cristy4c08aed2011-07-01 19:47:50 +00003225 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003226 zero;
3227
3228 MagickRealType
3229 length;
3230
3231 PointInfo
3232 point;
3233
3234 RectangleInfo
3235 bounding_box;
3236
cristyac245f82012-05-05 17:13:57 +00003237 size_t
3238 height,
3239 width;
3240
cristy826a5472010-08-31 23:21:38 +00003241 ssize_t
3242 y;
3243
cristy3ed852e2009-09-05 21:47:34 +00003244 /*
3245 Draw linear or radial gradient on image.
3246 */
3247 assert(image != (Image *) NULL);
3248 assert(image->signature == MagickSignature);
3249 if (image->debug != MagickFalse)
3250 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3251 assert(draw_info != (const DrawInfo *) NULL);
3252 gradient=(&draw_info->gradient);
3253 gradient_vector=(&gradient->gradient_vector);
3254 point.x=gradient_vector->x2-gradient_vector->x1;
3255 point.y=gradient_vector->y2-gradient_vector->y1;
3256 length=sqrt(point.x*point.x+point.y*point.y);
3257 bounding_box=gradient->bounding_box;
3258 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003259 GetPixelInfo(image,&zero);
cristyac245f82012-05-05 17:13:57 +00003260 height=bounding_box.height-bounding_box.y;
3261 width=bounding_box.width-bounding_box.x;
cristydb070952012-04-20 14:33:00 +00003262 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00003263#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00003264 #pragma omp parallel for schedule(static) shared(status) \
3265 if ((height*width) > 8192) \
3266 num_threads(GetMagickResourceLimit(ThreadResource))
cristy3ed852e2009-09-05 21:47:34 +00003267#endif
cristybb503372010-05-27 20:51:26 +00003268 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00003269 {
cristy4c08aed2011-07-01 19:47:50 +00003270 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003271 composite,
3272 pixel;
3273
3274 MagickRealType
3275 alpha,
3276 offset;
3277
cristy4c08aed2011-07-01 19:47:50 +00003278 register Quantum
3279 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003280
cristybb503372010-05-27 20:51:26 +00003281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003282 i,
3283 x;
3284
cristy826a5472010-08-31 23:21:38 +00003285 ssize_t
3286 j;
3287
cristy3ed852e2009-09-05 21:47:34 +00003288 if (status == MagickFalse)
3289 continue;
3290 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003291 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003292 {
3293 status=MagickFalse;
3294 continue;
3295 }
cristy3ed852e2009-09-05 21:47:34 +00003296 pixel=zero;
3297 composite=zero;
3298 offset=GetStopColorOffset(gradient,0,y);
3299 if (gradient->type != RadialGradient)
3300 offset/=length;
cristybb503372010-05-27 20:51:26 +00003301 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
cristy3ed852e2009-09-05 21:47:34 +00003302 {
cristy803640d2011-11-17 02:11:32 +00003303 GetPixelInfoPixel(image,q,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003304 switch (gradient->spread)
3305 {
3306 case UndefinedSpread:
3307 case PadSpread:
3308 {
cristybb503372010-05-27 20:51:26 +00003309 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3310 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003311 {
3312 offset=GetStopColorOffset(gradient,x,y);
3313 if (gradient->type != RadialGradient)
3314 offset/=length;
3315 }
cristybb503372010-05-27 20:51:26 +00003316 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003317 if (offset < gradient->stops[i].offset)
3318 break;
3319 if ((offset < 0.0) || (i == 0))
3320 composite=gradient->stops[0].color;
3321 else
cristybb503372010-05-27 20:51:26 +00003322 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
cristy3ed852e2009-09-05 21:47:34 +00003323 composite=gradient->stops[gradient->number_stops-1].color;
3324 else
3325 {
3326 j=i;
3327 i--;
3328 alpha=(offset-gradient->stops[i].offset)/
3329 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003330 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003331 &gradient->stops[j].color,alpha,&composite);
3332 }
3333 break;
3334 }
3335 case ReflectSpread:
3336 {
cristybb503372010-05-27 20:51:26 +00003337 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3338 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003339 {
3340 offset=GetStopColorOffset(gradient,x,y);
3341 if (gradient->type != RadialGradient)
3342 offset/=length;
3343 }
3344 if (offset < 0.0)
3345 offset=(-offset);
cristybb503372010-05-27 20:51:26 +00003346 if ((ssize_t) fmod(offset,2.0) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003347 offset=fmod(offset,1.0);
3348 else
3349 offset=1.0-fmod(offset,1.0);
cristybb503372010-05-27 20:51:26 +00003350 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003351 if (offset < gradient->stops[i].offset)
3352 break;
3353 if (i == 0)
3354 composite=gradient->stops[0].color;
3355 else
cristybb503372010-05-27 20:51:26 +00003356 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003357 composite=gradient->stops[gradient->number_stops-1].color;
3358 else
3359 {
3360 j=i;
3361 i--;
3362 alpha=(offset-gradient->stops[i].offset)/
3363 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003364 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003365 &gradient->stops[j].color,alpha,&composite);
3366 }
3367 break;
3368 }
3369 case RepeatSpread:
3370 {
3371 MagickBooleanType
3372 antialias;
3373
3374 MagickRealType
3375 repeat;
3376
3377 antialias=MagickFalse;
3378 repeat=0.0;
cristybb503372010-05-27 20:51:26 +00003379 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3380 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003381 {
3382 offset=GetStopColorOffset(gradient,x,y);
3383 if (gradient->type == LinearGradient)
3384 {
3385 repeat=fmod(offset,length);
3386 if (repeat < 0.0)
3387 repeat=length-fmod(-repeat,length);
3388 else
3389 repeat=fmod(offset,length);
3390 antialias=(repeat < length) && ((repeat+1.0) > length) ?
3391 MagickTrue : MagickFalse;
3392 offset=repeat/length;
3393 }
3394 else
3395 {
3396 repeat=fmod(offset,gradient->radius);
3397 if (repeat < 0.0)
3398 repeat=gradient->radius-fmod(-repeat,gradient->radius);
3399 else
3400 repeat=fmod(offset,gradient->radius);
cristy4c08aed2011-07-01 19:47:50 +00003401 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
3402 MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003403 offset=repeat/gradient->radius;
3404 }
3405 }
cristybb503372010-05-27 20:51:26 +00003406 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003407 if (offset < gradient->stops[i].offset)
3408 break;
3409 if (i == 0)
3410 composite=gradient->stops[0].color;
3411 else
cristybb503372010-05-27 20:51:26 +00003412 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003413 composite=gradient->stops[gradient->number_stops-1].color;
3414 else
3415 {
3416 j=i;
3417 i--;
3418 alpha=(offset-gradient->stops[i].offset)/
3419 (gradient->stops[j].offset-gradient->stops[i].offset);
3420 if (antialias != MagickFalse)
3421 {
3422 if (gradient->type == LinearGradient)
3423 alpha=length-repeat;
3424 else
3425 alpha=gradient->radius-repeat;
3426 i=0;
cristybb503372010-05-27 20:51:26 +00003427 j=(ssize_t) gradient->number_stops-1L;
cristy3ed852e2009-09-05 21:47:34 +00003428 }
cristy4c08aed2011-07-01 19:47:50 +00003429 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003430 &gradient->stops[j].color,alpha,&composite);
3431 }
3432 break;
3433 }
3434 }
cristy4c08aed2011-07-01 19:47:50 +00003435 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
3436 &pixel);
cristy803640d2011-11-17 02:11:32 +00003437 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003438 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003439 }
3440 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3441 status=MagickFalse;
3442 }
3443 image_view=DestroyCacheView(image_view);
3444 return(status);
3445}
3446
3447/*
3448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3449% %
3450% %
3451% %
3452% D r a w P a t t e r n P a t h %
3453% %
3454% %
3455% %
3456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3457%
3458% DrawPatternPath() draws a pattern.
3459%
3460% The format of the DrawPatternPath method is:
3461%
3462% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00003463% const char *name,Image **pattern,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003464%
3465% A description of each parameter follows:
3466%
3467% o image: the image.
3468%
3469% o draw_info: the draw info.
3470%
3471% o name: the pattern name.
3472%
3473% o image: the image.
3474%
cristy018f07f2011-09-04 21:15:19 +00003475% o exception: return any errors or warnings in this structure.
3476%
cristy3ed852e2009-09-05 21:47:34 +00003477*/
3478MagickExport MagickBooleanType DrawPatternPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00003479 const DrawInfo *draw_info,const char *name,Image **pattern,
3480 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003481{
3482 char
3483 property[MaxTextExtent];
3484
3485 const char
3486 *geometry,
3487 *path;
3488
3489 DrawInfo
3490 *clone_info;
3491
3492 ImageInfo
3493 *image_info;
3494
3495 MagickBooleanType
3496 status;
3497
3498 assert(image != (Image *) NULL);
3499 assert(image->signature == MagickSignature);
3500 if (image->debug != MagickFalse)
3501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3502 assert(draw_info != (const DrawInfo *) NULL);
3503 assert(name != (const char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00003504 (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00003505 path=GetImageArtifact(image,property);
3506 if (path == (const char *) NULL)
3507 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00003508 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
cristy3ed852e2009-09-05 21:47:34 +00003509 geometry=GetImageArtifact(image,property);
3510 if (geometry == (const char *) NULL)
3511 return(MagickFalse);
3512 if ((*pattern) != (Image *) NULL)
3513 *pattern=DestroyImage(*pattern);
3514 image_info=AcquireImageInfo();
3515 image_info->size=AcquireString(geometry);
cristy947cb4c2011-10-20 18:41:46 +00003516 *pattern=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003517 image_info=DestroyImageInfo(image_info);
cristyfad60c92012-01-19 18:32:39 +00003518 (void) QueryColorCompliance("#00000000",AllCompliance,
cristyea1a8aa2011-10-20 13:24:06 +00003519 &(*pattern)->background_color,exception);
3520 (void) SetImageBackgroundColor(*pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00003521 if (image->debug != MagickFalse)
3522 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3523 "begin pattern-path %s %s",name,geometry);
3524 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3525 clone_info->fill_pattern=NewImageList();
3526 clone_info->stroke_pattern=NewImageList();
3527 (void) CloneString(&clone_info->primitive,path);
cristy018f07f2011-09-04 21:15:19 +00003528 status=DrawImage(*pattern,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003529 clone_info=DestroyDrawInfo(clone_info);
3530 if (image->debug != MagickFalse)
3531 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3532 return(status);
3533}
3534
3535/*
3536%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3537% %
3538% %
3539% %
3540+ D r a w P o l y g o n P r i m i t i v e %
3541% %
3542% %
3543% %
3544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3545%
3546% DrawPolygonPrimitive() draws a polygon on the image.
3547%
3548% The format of the DrawPolygonPrimitive method is:
3549%
3550% MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003551% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3552% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003553%
3554% A description of each parameter follows:
3555%
3556% o image: the image.
3557%
3558% o draw_info: the draw info.
3559%
3560% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3561%
cristy947cb4c2011-10-20 18:41:46 +00003562% o exception: return any errors or warnings in this structure.
3563%
cristy3ed852e2009-09-05 21:47:34 +00003564*/
3565
3566static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
3567{
cristybb503372010-05-27 20:51:26 +00003568 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003569 i;
3570
3571 assert(polygon_info != (PolygonInfo **) NULL);
cristyac245f82012-05-05 17:13:57 +00003572 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +00003573 if (polygon_info[i] != (PolygonInfo *) NULL)
3574 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
cristyb41ee102010-10-04 16:46:15 +00003575 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
cristy3ed852e2009-09-05 21:47:34 +00003576 return(polygon_info);
3577}
3578
3579static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
3580 const PrimitiveInfo *primitive_info)
3581{
3582 PathInfo
cristyfa112112010-01-04 17:48:07 +00003583 *restrict path_info;
cristy3ed852e2009-09-05 21:47:34 +00003584
cristy3ed852e2009-09-05 21:47:34 +00003585 PolygonInfo
3586 **polygon_info;
3587
cristy826a5472010-08-31 23:21:38 +00003588 register ssize_t
3589 i;
3590
cristybb503372010-05-27 20:51:26 +00003591 size_t
cristy3ed852e2009-09-05 21:47:34 +00003592 number_threads;
3593
cristyac245f82012-05-05 17:13:57 +00003594 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +00003595 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00003596 sizeof(*polygon_info));
3597 if (polygon_info == (PolygonInfo **) NULL)
3598 return((PolygonInfo **) NULL);
cristyac245f82012-05-05 17:13:57 +00003599 (void) ResetMagickMemory(polygon_info,0,number_threads*sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +00003600 path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
3601 if (path_info == (PathInfo *) NULL)
3602 return(DestroyPolygonThreadSet(polygon_info));
cristybb503372010-05-27 20:51:26 +00003603 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003604 {
3605 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
3606 if (polygon_info[i] == (PolygonInfo *) NULL)
3607 return(DestroyPolygonThreadSet(polygon_info));
3608 }
3609 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
3610 return(polygon_info);
3611}
3612
cristyb2e6b992011-12-17 16:42:29 +00003613static MagickRealType GetFillAlpha(PolygonInfo *polygon_info,
cristy3ed852e2009-09-05 21:47:34 +00003614 const MagickRealType mid,const MagickBooleanType fill,
cristy77f38fb2010-04-22 15:51:47 +00003615 const FillRule fill_rule,const double x,const double y,
cristyb2e6b992011-12-17 16:42:29 +00003616 MagickRealType *stroke_alpha)
cristy3ed852e2009-09-05 21:47:34 +00003617{
cristy3ed852e2009-09-05 21:47:34 +00003618 MagickRealType
cristyb32b90a2009-09-07 21:45:48 +00003619 alpha,
3620 beta,
cristy3ed852e2009-09-05 21:47:34 +00003621 distance,
cristyb2e6b992011-12-17 16:42:29 +00003622 subpath_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003623
3624 PointInfo
cristyb32b90a2009-09-07 21:45:48 +00003625 delta;
cristy3ed852e2009-09-05 21:47:34 +00003626
3627 register EdgeInfo
3628 *p;
3629
cristyb32b90a2009-09-07 21:45:48 +00003630 register const PointInfo
3631 *q;
3632
cristybb503372010-05-27 20:51:26 +00003633 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003634 i;
3635
cristycee97112010-05-28 00:44:52 +00003636 ssize_t
3637 j,
3638 winding_number;
3639
cristy3ed852e2009-09-05 21:47:34 +00003640 /*
3641 Compute fill & stroke opacity for this (x,y) point.
3642 */
cristyb2e6b992011-12-17 16:42:29 +00003643 *stroke_alpha=0.0;
3644 subpath_alpha=0.0;
cristy3ed852e2009-09-05 21:47:34 +00003645 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003646 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristy3ed852e2009-09-05 21:47:34 +00003647 {
cristyb32b90a2009-09-07 21:45:48 +00003648 if (y <= (p->bounds.y1-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003649 break;
cristyb32b90a2009-09-07 21:45:48 +00003650 if (y > (p->bounds.y2+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003651 {
cristybb503372010-05-27 20:51:26 +00003652 (void) DestroyEdge(polygon_info,(size_t) j);
cristy3ed852e2009-09-05 21:47:34 +00003653 continue;
3654 }
cristyb32b90a2009-09-07 21:45:48 +00003655 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003656 continue;
cristybb503372010-05-27 20:51:26 +00003657 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3658 for ( ; i < (ssize_t) p->number_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00003659 {
cristyb32b90a2009-09-07 21:45:48 +00003660 if (y <= (p->points[i-1].y-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003661 break;
cristyb32b90a2009-09-07 21:45:48 +00003662 if (y > (p->points[i].y+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003663 continue;
cristyb32b90a2009-09-07 21:45:48 +00003664 if (p->scanline != y)
cristy3ed852e2009-09-05 21:47:34 +00003665 {
cristyb32b90a2009-09-07 21:45:48 +00003666 p->scanline=y;
cristybb503372010-05-27 20:51:26 +00003667 p->highwater=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00003668 }
3669 /*
3670 Compute distance between a point and an edge.
3671 */
cristyb32b90a2009-09-07 21:45:48 +00003672 q=p->points+i-1;
3673 delta.x=(q+1)->x-q->x;
3674 delta.y=(q+1)->y-q->y;
3675 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
cristy3ed852e2009-09-05 21:47:34 +00003676 if (beta < 0.0)
3677 {
cristyb32b90a2009-09-07 21:45:48 +00003678 delta.x=x-q->x;
3679 delta.y=y-q->y;
cristy3ed852e2009-09-05 21:47:34 +00003680 distance=delta.x*delta.x+delta.y*delta.y;
3681 }
3682 else
3683 {
3684 alpha=delta.x*delta.x+delta.y*delta.y;
3685 if (beta > alpha)
3686 {
cristyb32b90a2009-09-07 21:45:48 +00003687 delta.x=x-(q+1)->x;
3688 delta.y=y-(q+1)->y;
cristy3ed852e2009-09-05 21:47:34 +00003689 distance=delta.x*delta.x+delta.y*delta.y;
3690 }
3691 else
3692 {
cristyb32b90a2009-09-07 21:45:48 +00003693 alpha=1.0/alpha;
3694 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
3695 distance=alpha*beta*beta;
cristy3ed852e2009-09-05 21:47:34 +00003696 }
3697 }
3698 /*
3699 Compute stroke & subpath opacity.
3700 */
3701 beta=0.0;
3702 if (p->ghostline == MagickFalse)
3703 {
cristyb32b90a2009-09-07 21:45:48 +00003704 alpha=mid+0.5;
cristyb2e6b992011-12-17 16:42:29 +00003705 if ((*stroke_alpha < 1.0) &&
cristy3ed852e2009-09-05 21:47:34 +00003706 (distance <= ((alpha+0.25)*(alpha+0.25))))
3707 {
3708 alpha=mid-0.5;
3709 if (distance <= ((alpha+0.25)*(alpha+0.25)))
cristyb2e6b992011-12-17 16:42:29 +00003710 *stroke_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003711 else
3712 {
3713 beta=1.0;
3714 if (distance != 1.0)
3715 beta=sqrt((double) distance);
cristyb32b90a2009-09-07 21:45:48 +00003716 alpha=beta-mid-0.5;
cristyb2e6b992011-12-17 16:42:29 +00003717 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
3718 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
cristy3ed852e2009-09-05 21:47:34 +00003719 }
3720 }
3721 }
cristyb2e6b992011-12-17 16:42:29 +00003722 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
cristy3ed852e2009-09-05 21:47:34 +00003723 continue;
3724 if (distance <= 0.0)
3725 {
cristyb2e6b992011-12-17 16:42:29 +00003726 subpath_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003727 continue;
3728 }
3729 if (distance > 1.0)
3730 continue;
3731 if (beta == 0.0)
3732 {
3733 beta=1.0;
3734 if (distance != 1.0)
cristyb32b90a2009-09-07 21:45:48 +00003735 beta=sqrt(distance);
cristy3ed852e2009-09-05 21:47:34 +00003736 }
3737 alpha=beta-1.0;
cristyb2e6b992011-12-17 16:42:29 +00003738 if (subpath_alpha < (alpha*alpha))
3739 subpath_alpha=alpha*alpha;
cristy3ed852e2009-09-05 21:47:34 +00003740 }
cristy3ed852e2009-09-05 21:47:34 +00003741 }
3742 /*
3743 Compute fill opacity.
3744 */
3745 if (fill == MagickFalse)
3746 return(0.0);
cristyb2e6b992011-12-17 16:42:29 +00003747 if (subpath_alpha >= 1.0)
cristy3ed852e2009-09-05 21:47:34 +00003748 return(1.0);
cristyb32b90a2009-09-07 21:45:48 +00003749 /*
3750 Determine winding number.
3751 */
3752 winding_number=0;
3753 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003754 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristyb32b90a2009-09-07 21:45:48 +00003755 {
3756 if (y <= p->bounds.y1)
3757 break;
3758 if ((y > p->bounds.y2) || (x <= p->bounds.x1))
3759 continue;
3760 if (x > p->bounds.x2)
3761 {
3762 winding_number+=p->direction ? 1 : -1;
3763 continue;
3764 }
cristybb503372010-05-27 20:51:26 +00003765 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3766 for ( ; i < (ssize_t) p->number_points; i++)
cristyb32b90a2009-09-07 21:45:48 +00003767 if (y <= p->points[i].y)
3768 break;
3769 q=p->points+i-1;
3770 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
3771 winding_number+=p->direction ? 1 : -1;
3772 }
cristy3ed852e2009-09-05 21:47:34 +00003773 if (fill_rule != NonZeroRule)
3774 {
3775 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
3776 return(1.0);
3777 }
3778 else
3779 if (MagickAbsoluteValue(winding_number) != 0)
3780 return(1.0);
cristyb2e6b992011-12-17 16:42:29 +00003781 return(subpath_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003782}
3783
3784static MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003785 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3786 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003787{
cristyfa112112010-01-04 17:48:07 +00003788 CacheView
3789 *image_view;
3790
cristy3ed852e2009-09-05 21:47:34 +00003791 MagickBooleanType
3792 fill,
3793 status;
3794
3795 MagickRealType
3796 mid;
3797
3798 PolygonInfo
cristyfa112112010-01-04 17:48:07 +00003799 **restrict polygon_info;
cristy3ed852e2009-09-05 21:47:34 +00003800
3801 register EdgeInfo
3802 *p;
3803
cristybb503372010-05-27 20:51:26 +00003804 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003805 i;
3806
3807 SegmentInfo
3808 bounds;
3809
cristyac245f82012-05-05 17:13:57 +00003810 size_t
3811 height,
3812 width;
3813
cristy826a5472010-08-31 23:21:38 +00003814 ssize_t
3815 start,
3816 stop,
3817 y;
3818
cristy3ed852e2009-09-05 21:47:34 +00003819 /*
3820 Compute bounding box.
3821 */
3822 assert(image != (Image *) NULL);
3823 assert(image->signature == MagickSignature);
3824 if (image->debug != MagickFalse)
3825 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3826 assert(draw_info != (DrawInfo *) NULL);
3827 assert(draw_info->signature == MagickSignature);
3828 assert(primitive_info != (PrimitiveInfo *) NULL);
3829 if (primitive_info->coordinates == 0)
3830 return(MagickTrue);
3831 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3832 if (polygon_info == (PolygonInfo **) NULL)
3833 return(MagickFalse);
3834 if (0)
cristy947cb4c2011-10-20 18:41:46 +00003835 DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
cristy3ed852e2009-09-05 21:47:34 +00003836 if (image->debug != MagickFalse)
3837 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3838 fill=(primitive_info->method == FillToBorderMethod) ||
3839 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3840 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3841 bounds=polygon_info[0]->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00003842 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00003843 {
3844 p=polygon_info[0]->edges+i;
3845 if (p->bounds.x1 < bounds.x1)
3846 bounds.x1=p->bounds.x1;
3847 if (p->bounds.y1 < bounds.y1)
3848 bounds.y1=p->bounds.y1;
3849 if (p->bounds.x2 > bounds.x2)
3850 bounds.x2=p->bounds.x2;
3851 if (p->bounds.y2 > bounds.y2)
3852 bounds.y2=p->bounds.y2;
3853 }
3854 bounds.x1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003855 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003856 image->columns ? (double) image->columns-1.0 : bounds.x1;
3857 bounds.y1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003858 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003859 image->rows ? (double) image->rows-1.0 : bounds.y1;
3860 bounds.x2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003861 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003862 image->columns ? (double) image->columns-1.0 : bounds.x2;
3863 bounds.y2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003864 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003865 image->rows ? (double) image->rows-1.0 : bounds.y2;
3866 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00003867 image_view=AcquireAuthenticCacheView(image,exception);
cristyac245f82012-05-05 17:13:57 +00003868 height=(size_t) (floor(bounds.y2+0.5)-ceil(bounds.y1-0.5));
3869 width=(size_t) (floor(bounds.x2+0.5)-ceil(bounds.x1-0.5));
cristy3ed852e2009-09-05 21:47:34 +00003870 if (primitive_info->coordinates == 1)
3871 {
3872 /*
3873 Draw point.
3874 */
cristy564a5692012-01-20 23:56:26 +00003875 start=(ssize_t) ceil(bounds.y1-0.5);
3876 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003877#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00003878 #pragma omp parallel for schedule(static) shared(status) \
3879 if ((height*width) > 8192) \
3880 num_threads(GetMagickResourceLimit(ThreadResource))
cristy3ed852e2009-09-05 21:47:34 +00003881#endif
cristy564a5692012-01-20 23:56:26 +00003882 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003883 {
3884 MagickBooleanType
3885 sync;
3886
cristy101ab702011-10-13 13:06:32 +00003887 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003888 pixel;
3889
cristybb503372010-05-27 20:51:26 +00003890 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003891 x;
3892
cristy4c08aed2011-07-01 19:47:50 +00003893 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003894 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003895
cristy564a5692012-01-20 23:56:26 +00003896 ssize_t
3897 start,
3898 stop;
3899
cristy3ed852e2009-09-05 21:47:34 +00003900 if (status == MagickFalse)
3901 continue;
cristy564a5692012-01-20 23:56:26 +00003902 start=(ssize_t) ceil(bounds.x1-0.5);
3903 stop=(ssize_t) floor(bounds.x2+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003904 x=start;
cristybb503372010-05-27 20:51:26 +00003905 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),
cristy3ed852e2009-09-05 21:47:34 +00003906 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003907 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003908 {
3909 status=MagickFalse;
3910 continue;
3911 }
cristy101ab702011-10-13 13:06:32 +00003912 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003913 for ( ; x <= stop; x++)
3914 {
cristybb503372010-05-27 20:51:26 +00003915 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3916 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
cristy4c08aed2011-07-01 19:47:50 +00003917 {
cristy2ed42f62011-10-02 19:49:57 +00003918 (void) GetStrokeColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00003919 SetPixelInfoPixel(image,&pixel,q);
cristy4c08aed2011-07-01 19:47:50 +00003920 }
cristyed231572011-07-14 02:18:59 +00003921 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003922 }
3923 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3924 if (sync == MagickFalse)
3925 status=MagickFalse;
3926 }
3927 image_view=DestroyCacheView(image_view);
3928 polygon_info=DestroyPolygonThreadSet(polygon_info);
3929 if (image->debug != MagickFalse)
3930 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3931 " end draw-polygon");
3932 return(status);
3933 }
3934 /*
3935 Draw polygon or line.
3936 */
3937 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00003938 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy564a5692012-01-20 23:56:26 +00003939 start=(ssize_t) ceil(bounds.y1-0.5);
3940 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003941#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00003942 #pragma omp parallel for schedule(static) shared(status) \
3943 if ((height*width) > 8192) \
3944 num_threads(GetMagickResourceLimit(ThreadResource))
cristy3ed852e2009-09-05 21:47:34 +00003945#endif
cristy564a5692012-01-20 23:56:26 +00003946 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003947 {
cristy5c9e6f22010-09-17 17:31:01 +00003948 const int
3949 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003950
cristy3ed852e2009-09-05 21:47:34 +00003951 MagickRealType
cristyb2e6b992011-12-17 16:42:29 +00003952 fill_alpha,
3953 stroke_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003954
cristy101ab702011-10-13 13:06:32 +00003955 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003956 fill_color,
3957 stroke_color;
3958
cristy4c08aed2011-07-01 19:47:50 +00003959 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003960 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003961
cristy826a5472010-08-31 23:21:38 +00003962 register ssize_t
3963 x;
3964
cristy564a5692012-01-20 23:56:26 +00003965 ssize_t
3966 start,
3967 stop;
3968
cristy3ed852e2009-09-05 21:47:34 +00003969 if (status == MagickFalse)
3970 continue;
cristy564a5692012-01-20 23:56:26 +00003971 start=(ssize_t) ceil(bounds.x1-0.5);
3972 stop=(ssize_t) floor(bounds.x2+0.5);
cristyb2e6b992011-12-17 16:42:29 +00003973 q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-start+1),1,
3974 exception);
cristy4c08aed2011-07-01 19:47:50 +00003975 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003976 {
3977 status=MagickFalse;
3978 continue;
3979 }
cristy3ed852e2009-09-05 21:47:34 +00003980 for (x=start; x <= stop; x++)
3981 {
3982 /*
3983 Fill and/or stroke.
3984 */
cristyb2e6b992011-12-17 16:42:29 +00003985 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
3986 (double) x,(double) y,&stroke_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003987 if (draw_info->stroke_antialias == MagickFalse)
3988 {
cristyb2e6b992011-12-17 16:42:29 +00003989 fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0;
3990 stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0;
cristy3ed852e2009-09-05 21:47:34 +00003991 }
cristy2ed42f62011-10-02 19:49:57 +00003992 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003993 fill_alpha=fill_alpha*fill_color.alpha;
3994 CompositePixelOver(image,&fill_color,fill_alpha,q,(MagickRealType)
cristy4c08aed2011-07-01 19:47:50 +00003995 GetPixelAlpha(image,q),q);
cristy2ed42f62011-10-02 19:49:57 +00003996 (void) GetStrokeColor(draw_info,x,y,&stroke_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003997 stroke_alpha=stroke_alpha*stroke_color.alpha;
3998 CompositePixelOver(image,&stroke_color,stroke_alpha,q,(MagickRealType)
cristy4c08aed2011-07-01 19:47:50 +00003999 GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00004000 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004001 }
4002 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4003 status=MagickFalse;
4004 }
4005 image_view=DestroyCacheView(image_view);
4006 polygon_info=DestroyPolygonThreadSet(polygon_info);
4007 if (image->debug != MagickFalse)
4008 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
4009 return(status);
4010}
4011
4012/*
4013%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4014% %
4015% %
4016% %
4017% D r a w P r i m i t i v e %
4018% %
4019% %
4020% %
4021%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4022%
4023% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
4024%
4025% The format of the DrawPrimitive method is:
4026%
4027% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00004028% PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004029%
4030% A description of each parameter follows:
4031%
4032% o image: the image.
4033%
4034% o draw_info: the draw info.
4035%
4036% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4037%
cristy947cb4c2011-10-20 18:41:46 +00004038% o exception: return any errors or warnings in this structure.
4039%
cristy3ed852e2009-09-05 21:47:34 +00004040*/
4041
4042static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4043{
4044 const char
4045 *methods[] =
4046 {
4047 "point",
4048 "replace",
4049 "floodfill",
4050 "filltoborder",
4051 "reset",
4052 "?"
4053 };
4054
cristy3ed852e2009-09-05 21:47:34 +00004055 PointInfo
4056 p,
4057 q,
4058 point;
4059
cristybb503372010-05-27 20:51:26 +00004060 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004061 i,
4062 x;
4063
cristy826a5472010-08-31 23:21:38 +00004064 ssize_t
4065 coordinates,
4066 y;
4067
cristybb503372010-05-27 20:51:26 +00004068 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4069 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004070 switch (primitive_info->primitive)
4071 {
4072 case PointPrimitive:
4073 {
4074 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004075 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004076 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004077 return;
4078 }
4079 case ColorPrimitive:
4080 {
4081 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004082 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004083 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004084 return;
4085 }
4086 case MattePrimitive:
4087 {
4088 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004089 "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004090 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004091 return;
4092 }
4093 case TextPrimitive:
4094 {
4095 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004096 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004097 return;
4098 }
4099 case ImagePrimitive:
4100 {
4101 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004102 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004103 return;
4104 }
4105 default:
4106 break;
4107 }
4108 coordinates=0;
4109 p=primitive_info[0].point;
4110 q.x=(-1.0);
4111 q.y=(-1.0);
4112 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4113 {
4114 point=primitive_info[i].point;
4115 if (coordinates <= 0)
4116 {
cristybb503372010-05-27 20:51:26 +00004117 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004118 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004119 " begin open (%.20g)",(double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004120 p=point;
4121 }
4122 point=primitive_info[i].point;
4123 if ((fabs(q.x-point.x) > MagickEpsilon) ||
4124 (fabs(q.y-point.y) > MagickEpsilon))
cristy8cd5b312010-01-07 01:10:24 +00004125 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004126 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004127 else
4128 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004129 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004130 q=point;
4131 coordinates--;
4132 if (coordinates > 0)
4133 continue;
4134 if ((fabs(p.x-point.x) > MagickEpsilon) ||
4135 (fabs(p.y-point.y) > MagickEpsilon))
cristye8c25f92010-06-03 00:53:06 +00004136 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4137 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004138 else
cristye8c25f92010-06-03 00:53:06 +00004139 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4140 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004141 }
4142}
4143
4144MagickExport MagickBooleanType DrawPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004145 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4146 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004147{
cristyc4c8d132010-01-07 01:58:38 +00004148 CacheView
4149 *image_view;
4150
cristy3ed852e2009-09-05 21:47:34 +00004151 MagickStatusType
4152 status;
4153
cristybb503372010-05-27 20:51:26 +00004154 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004155 i,
4156 x;
4157
cristy826a5472010-08-31 23:21:38 +00004158 ssize_t
4159 y;
4160
cristy3ed852e2009-09-05 21:47:34 +00004161 if (image->debug != MagickFalse)
4162 {
4163 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4164 " begin draw-primitive");
4165 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004166 " affine: %g %g %g %g %g %g",draw_info->affine.sx,
cristy3ed852e2009-09-05 21:47:34 +00004167 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4168 draw_info->affine.tx,draw_info->affine.ty);
4169 }
4170 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004171 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4172 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristydb070952012-04-20 14:33:00 +00004173 image_view=AcquireAuthenticCacheView(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004174 switch (primitive_info->primitive)
4175 {
4176 case PointPrimitive:
4177 {
cristy101ab702011-10-13 13:06:32 +00004178 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004179 fill_color;
4180
cristy4c08aed2011-07-01 19:47:50 +00004181 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004182 *q;
4183
cristybb503372010-05-27 20:51:26 +00004184 if ((y < 0) || (y >= (ssize_t) image->rows))
cristyb32b90a2009-09-07 21:45:48 +00004185 break;
cristybb503372010-05-27 20:51:26 +00004186 if ((x < 0) || (x >= (ssize_t) image->columns))
cristyb32b90a2009-09-07 21:45:48 +00004187 break;
cristy3ed852e2009-09-05 21:47:34 +00004188 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004189 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004190 break;
cristy2ed42f62011-10-02 19:49:57 +00004191 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristy4c08aed2011-07-01 19:47:50 +00004192 CompositePixelOver(image,&fill_color,(MagickRealType) fill_color.alpha,q,
4193 (MagickRealType) GetPixelAlpha(image,q),q);
cristy3ed852e2009-09-05 21:47:34 +00004194 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4195 break;
4196 }
4197 case ColorPrimitive:
4198 {
4199 switch (primitive_info->method)
4200 {
4201 case PointMethod:
4202 default:
4203 {
cristy101ab702011-10-13 13:06:32 +00004204 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004205 pixel;
4206
4207 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004208 *q;
4209
4210 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004211 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004212 break;
cristy101ab702011-10-13 13:06:32 +00004213 GetPixelInfo(image,&pixel);
cristy2ed42f62011-10-02 19:49:57 +00004214 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004215 SetPixelInfoPixel(image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004216 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4217 break;
4218 }
4219 case ReplaceMethod:
4220 {
4221 MagickBooleanType
4222 sync;
4223
cristy101ab702011-10-13 13:06:32 +00004224 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004225 pixel,
cristy3ed852e2009-09-05 21:47:34 +00004226 target;
4227
cristyf05d4942012-03-17 16:26:09 +00004228 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
cristy2ed42f62011-10-02 19:49:57 +00004229 exception);
cristybb503372010-05-27 20:51:26 +00004230 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004231 {
cristy4c08aed2011-07-01 19:47:50 +00004232 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004233 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004234
4235 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4236 exception);
cristy4c08aed2011-07-01 19:47:50 +00004237 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004238 break;
cristybb503372010-05-27 20:51:26 +00004239 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004240 {
cristy101ab702011-10-13 13:06:32 +00004241 GetPixelInfoPixel(image,q,&pixel);
4242 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004243 {
cristyed231572011-07-14 02:18:59 +00004244 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004245 continue;
4246 }
cristy2ed42f62011-10-02 19:49:57 +00004247 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004248 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004249 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004250 }
4251 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4252 if (sync == MagickFalse)
4253 break;
4254 }
4255 break;
4256 }
4257 case FloodfillMethod:
4258 case FillToBorderMethod:
4259 {
cristy4c08aed2011-07-01 19:47:50 +00004260 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004261 target;
4262
cristy3aa93752011-12-18 15:54:24 +00004263 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004264 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004265 if (primitive_info->method == FillToBorderMethod)
4266 {
4267 target.red=(MagickRealType) draw_info->border_color.red;
4268 target.green=(MagickRealType) draw_info->border_color.green;
4269 target.blue=(MagickRealType) draw_info->border_color.blue;
4270 }
cristyd42d9952011-07-08 14:21:50 +00004271 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
4272 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004273 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004274 break;
4275 }
4276 case ResetMethod:
4277 {
4278 MagickBooleanType
4279 sync;
4280
cristy101ab702011-10-13 13:06:32 +00004281 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004282 pixel;
4283
cristy101ab702011-10-13 13:06:32 +00004284 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00004285 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004286 {
cristy4c08aed2011-07-01 19:47:50 +00004287 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004288 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004289
cristy826a5472010-08-31 23:21:38 +00004290 register ssize_t
4291 x;
4292
cristy3ed852e2009-09-05 21:47:34 +00004293 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4294 exception);
cristy4c08aed2011-07-01 19:47:50 +00004295 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004296 break;
cristybb503372010-05-27 20:51:26 +00004297 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004298 {
cristy2ed42f62011-10-02 19:49:57 +00004299 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004300 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004301 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004302 }
4303 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4304 if (sync == MagickFalse)
4305 break;
4306 }
4307 break;
4308 }
4309 }
4310 break;
4311 }
4312 case MattePrimitive:
4313 {
4314 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004315 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004316 switch (primitive_info->method)
4317 {
4318 case PointMethod:
4319 default:
4320 {
cristy101ab702011-10-13 13:06:32 +00004321 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004322 pixel;
4323
cristy4c08aed2011-07-01 19:47:50 +00004324 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004325 *q;
4326
4327 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004328 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004329 break;
cristy2ed42f62011-10-02 19:49:57 +00004330 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004331 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00004332 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4333 break;
4334 }
4335 case ReplaceMethod:
4336 {
4337 MagickBooleanType
4338 sync;
4339
cristy101ab702011-10-13 13:06:32 +00004340 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004341 pixel,
4342 target;
4343
cristyf05d4942012-03-17 16:26:09 +00004344 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target,
cristy2ed42f62011-10-02 19:49:57 +00004345 exception);
cristybb503372010-05-27 20:51:26 +00004346 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004347 {
cristy4c08aed2011-07-01 19:47:50 +00004348 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004349 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004350
cristy826a5472010-08-31 23:21:38 +00004351 register ssize_t
4352 x;
4353
cristy3ed852e2009-09-05 21:47:34 +00004354 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4355 exception);
cristy4c08aed2011-07-01 19:47:50 +00004356 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004357 break;
cristybb503372010-05-27 20:51:26 +00004358 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004359 {
cristy101ab702011-10-13 13:06:32 +00004360 GetPixelInfoPixel(image,q,&pixel);
4361 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004362 {
cristyed231572011-07-14 02:18:59 +00004363 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004364 continue;
4365 }
cristy2ed42f62011-10-02 19:49:57 +00004366 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004367 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004368 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004369 }
4370 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4371 if (sync == MagickFalse)
4372 break;
4373 }
4374 break;
4375 }
4376 case FloodfillMethod:
4377 case FillToBorderMethod:
4378 {
cristybd5a96c2011-08-21 00:04:26 +00004379 ChannelType
4380 channel_mask;
4381
cristy4c08aed2011-07-01 19:47:50 +00004382 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004383 target;
4384
cristy3aa93752011-12-18 15:54:24 +00004385 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004386 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004387 if (primitive_info->method == FillToBorderMethod)
4388 {
4389 target.red=(MagickRealType) draw_info->border_color.red;
4390 target.green=(MagickRealType) draw_info->border_color.green;
4391 target.blue=(MagickRealType) draw_info->border_color.blue;
4392 }
cristybd5a96c2011-08-21 00:04:26 +00004393 channel_mask=SetPixelChannelMask(image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +00004394 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
cristy3ed852e2009-09-05 21:47:34 +00004395 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004396 MagickTrue,exception);
cristybd5a96c2011-08-21 00:04:26 +00004397 (void) SetPixelChannelMask(image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004398 break;
4399 }
4400 case ResetMethod:
4401 {
4402 MagickBooleanType
4403 sync;
4404
cristy101ab702011-10-13 13:06:32 +00004405 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004406 pixel;
4407
cristybb503372010-05-27 20:51:26 +00004408 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004409 {
cristy4c08aed2011-07-01 19:47:50 +00004410 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004411 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004412
cristy826a5472010-08-31 23:21:38 +00004413 register ssize_t
4414 x;
4415
cristy3ed852e2009-09-05 21:47:34 +00004416 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4417 exception);
cristy4c08aed2011-07-01 19:47:50 +00004418 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004419 break;
cristybb503372010-05-27 20:51:26 +00004420 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004421 {
cristy2ed42f62011-10-02 19:49:57 +00004422 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004423 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004424 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004425 }
4426 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4427 if (sync == MagickFalse)
4428 break;
4429 }
4430 break;
4431 }
4432 }
4433 break;
4434 }
4435 case TextPrimitive:
4436 {
4437 char
4438 geometry[MaxTextExtent];
4439
4440 DrawInfo
4441 *clone_info;
4442
4443 if (primitive_info->text == (char *) NULL)
4444 break;
4445 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4446 (void) CloneString(&clone_info->text,primitive_info->text);
cristyb51dff52011-05-19 16:55:47 +00004447 (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
cristy3ed852e2009-09-05 21:47:34 +00004448 primitive_info->point.x,primitive_info->point.y);
4449 (void) CloneString(&clone_info->geometry,geometry);
cristy5cbc0162011-08-29 00:36:28 +00004450 status=AnnotateImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004451 clone_info=DestroyDrawInfo(clone_info);
4452 break;
4453 }
4454 case ImagePrimitive:
4455 {
4456 AffineMatrix
4457 affine;
4458
4459 char
4460 composite_geometry[MaxTextExtent];
4461
4462 Image
4463 *composite_image;
4464
4465 ImageInfo
4466 *clone_info;
4467
cristy826a5472010-08-31 23:21:38 +00004468 RectangleInfo
4469 geometry;
4470
cristybb503372010-05-27 20:51:26 +00004471 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004472 x1,
4473 y1;
4474
cristy3ed852e2009-09-05 21:47:34 +00004475 if (primitive_info->text == (char *) NULL)
4476 break;
4477 clone_info=AcquireImageInfo();
4478 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4479 composite_image=ReadInlineImage(clone_info,primitive_info->text,
cristy947cb4c2011-10-20 18:41:46 +00004480 exception);
cristy3ed852e2009-09-05 21:47:34 +00004481 else
4482 {
4483 (void) CopyMagickString(clone_info->filename,primitive_info->text,
4484 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00004485 composite_image=ReadImage(clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004486 }
4487 clone_info=DestroyImageInfo(clone_info);
4488 if (composite_image == (Image *) NULL)
4489 break;
4490 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4491 NULL,(void *) NULL);
cristybb503372010-05-27 20:51:26 +00004492 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4493 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4494 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4495 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
cristy3ed852e2009-09-05 21:47:34 +00004496 {
4497 char
4498 geometry[MaxTextExtent];
4499
4500 /*
4501 Resize image.
4502 */
cristyb51dff52011-05-19 16:55:47 +00004503 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
cristy3ed852e2009-09-05 21:47:34 +00004504 primitive_info[1].point.x,primitive_info[1].point.y);
4505 composite_image->filter=image->filter;
cristye941a752011-10-15 01:52:48 +00004506 (void) TransformImage(&composite_image,(char *) NULL,geometry,
4507 exception);
cristy3ed852e2009-09-05 21:47:34 +00004508 }
4509 if (composite_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004510 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
4511 exception);
cristy4c08aed2011-07-01 19:47:50 +00004512 if (draw_info->alpha != OpaqueAlpha)
cristye941a752011-10-15 01:52:48 +00004513 (void) SetImageAlpha(composite_image,draw_info->alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00004514 SetGeometry(image,&geometry);
4515 image->gravity=draw_info->gravity;
4516 geometry.x=x;
4517 geometry.y=y;
cristyb51dff52011-05-19 16:55:47 +00004518 (void) FormatLocaleString(composite_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004519 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
cristye8c25f92010-06-03 00:53:06 +00004520 composite_image->rows,(double) geometry.x,(double) geometry.y);
cristy947cb4c2011-10-20 18:41:46 +00004521 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00004522 affine=draw_info->affine;
4523 affine.tx=(double) geometry.x;
4524 affine.ty=(double) geometry.y;
4525 composite_image->interpolate=image->interpolate;
cristyd5f3fc32011-04-26 14:44:36 +00004526 if (draw_info->compose == OverCompositeOp)
cristy947cb4c2011-10-20 18:41:46 +00004527 (void) DrawAffineImage(image,composite_image,&affine,exception);
cristyd5f3fc32011-04-26 14:44:36 +00004528 else
cristyfeb3e962012-03-29 17:25:55 +00004529 (void) CompositeImage(image,composite_image,draw_info->compose,
cristy39172402012-03-30 13:04:39 +00004530 MagickTrue,geometry.x,geometry.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00004531 composite_image=DestroyImage(composite_image);
4532 break;
4533 }
4534 default:
4535 {
4536 MagickRealType
4537 mid,
4538 scale;
4539
4540 DrawInfo
4541 *clone_info;
4542
4543 if (IsEventLogging() != MagickFalse)
4544 LogPrimitiveInfo(primitive_info);
4545 scale=ExpandAffine(&draw_info->affine);
4546 if ((draw_info->dash_pattern != (double *) NULL) &&
4547 (draw_info->dash_pattern[0] != 0.0) &&
4548 ((scale*draw_info->stroke_width) > MagickEpsilon) &&
cristy4c08aed2011-07-01 19:47:50 +00004549 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004550 {
4551 /*
4552 Draw dash polygon.
4553 */
4554 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4555 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004556 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004557 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4558 exception);
cristy3ed852e2009-09-05 21:47:34 +00004559 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004560 (void) DrawDashPolygon(draw_info,primitive_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004561 break;
4562 }
4563 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4564 if ((mid > 1.0) &&
cristy4c08aed2011-07-01 19:47:50 +00004565 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004566 {
4567 MagickBooleanType
4568 closed_path;
4569
4570 /*
4571 Draw strokes while respecting line cap/join attributes.
4572 */
4573 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4574 closed_path=
4575 (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4576 (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4577 MagickTrue : MagickFalse;
cristybb503372010-05-27 20:51:26 +00004578 i=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004579 if ((((draw_info->linecap == RoundCap) ||
4580 (closed_path != MagickFalse)) &&
4581 (draw_info->linejoin == RoundJoin)) ||
4582 (primitive_info[i].primitive != UndefinedPrimitive))
4583 {
cristy947cb4c2011-10-20 18:41:46 +00004584 (void) DrawPolygonPrimitive(image,draw_info,primitive_info,
4585 exception);
cristy3ed852e2009-09-05 21:47:34 +00004586 break;
4587 }
4588 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4589 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004590 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004591 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4592 exception);
cristy3ed852e2009-09-05 21:47:34 +00004593 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004594 status|=DrawStrokePolygon(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004595 break;
4596 }
cristy947cb4c2011-10-20 18:41:46 +00004597 status=DrawPolygonPrimitive(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004598 break;
4599 }
4600 }
4601 image_view=DestroyCacheView(image_view);
4602 if (image->debug != MagickFalse)
4603 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4604 return(status != 0 ? MagickTrue : MagickFalse);
4605}
4606
4607/*
4608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4609% %
4610% %
4611% %
4612+ D r a w S t r o k e P o l y g o n %
4613% %
4614% %
4615% %
4616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4617%
4618% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4619% the image while respecting the line cap and join attributes.
4620%
4621% The format of the DrawStrokePolygon method is:
4622%
4623% MagickBooleanType DrawStrokePolygon(Image *image,
4624% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4625%
4626% A description of each parameter follows:
4627%
4628% o image: the image.
4629%
4630% o draw_info: the draw info.
4631%
4632% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4633%
4634%
4635*/
4636
4637static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00004638 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004639{
4640 PrimitiveInfo
4641 linecap[5];
4642
cristybb503372010-05-27 20:51:26 +00004643 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004644 i;
4645
4646 for (i=0; i < 4; i++)
4647 linecap[i]=(*primitive_info);
4648 linecap[0].coordinates=4;
4649 linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4650 linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4651 linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4652 linecap[3].point.y+=(double) (10.0*MagickEpsilon);
4653 linecap[4].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00004654 (void) DrawPolygonPrimitive(image,draw_info,linecap,exception);
cristy3ed852e2009-09-05 21:47:34 +00004655}
4656
4657static MagickBooleanType DrawStrokePolygon(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004658 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4659 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004660{
4661 DrawInfo
4662 *clone_info;
4663
4664 MagickBooleanType
4665 closed_path,
4666 status;
4667
4668 PrimitiveInfo
4669 *stroke_polygon;
4670
4671 register const PrimitiveInfo
4672 *p,
4673 *q;
4674
4675 /*
4676 Draw stroked polygon.
4677 */
4678 if (image->debug != MagickFalse)
4679 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4680 " begin draw-stroke-polygon");
4681 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4682 clone_info->fill=draw_info->stroke;
cristy2d2d5622012-01-19 19:11:29 +00004683 if (clone_info->fill_pattern != (Image *) NULL)
4684 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
cristy2195b752012-01-19 16:04:04 +00004685 if (clone_info->stroke_pattern != (Image *) NULL)
cristy2d2d5622012-01-19 19:11:29 +00004686 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
4687 MagickTrue,exception);
cristy4c08aed2011-07-01 19:47:50 +00004688 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004689 clone_info->stroke_width=0.0;
4690 clone_info->fill_rule=NonZeroRule;
4691 status=MagickTrue;
4692 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4693 {
4694 stroke_polygon=TraceStrokePolygon(draw_info,p);
cristy947cb4c2011-10-20 18:41:46 +00004695 status=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00004696 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4697 q=p+p->coordinates-1;
4698 closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4699 MagickTrue : MagickFalse;
4700 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4701 {
cristy947cb4c2011-10-20 18:41:46 +00004702 DrawRoundLinecap(image,draw_info,p,exception);
4703 DrawRoundLinecap(image,draw_info,q,exception);
cristy3ed852e2009-09-05 21:47:34 +00004704 }
4705 }
4706 clone_info=DestroyDrawInfo(clone_info);
4707 if (image->debug != MagickFalse)
4708 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4709 " end draw-stroke-polygon");
4710 return(status);
4711}
4712
4713/*
4714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4715% %
4716% %
4717% %
4718% G e t A f f i n e M a t r i x %
4719% %
4720% %
4721% %
4722%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4723%
4724% GetAffineMatrix() returns an AffineMatrix initialized to the identity
4725% matrix.
4726%
4727% The format of the GetAffineMatrix method is:
4728%
4729% void GetAffineMatrix(AffineMatrix *affine_matrix)
4730%
4731% A description of each parameter follows:
4732%
4733% o affine_matrix: the affine matrix.
4734%
4735*/
4736MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
4737{
4738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4739 assert(affine_matrix != (AffineMatrix *) NULL);
4740 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4741 affine_matrix->sx=1.0;
4742 affine_matrix->sy=1.0;
4743}
4744
4745/*
4746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4747% %
4748% %
4749% %
4750+ G e t D r a w I n f o %
4751% %
4752% %
4753% %
4754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4755%
anthony9c88e8f2011-09-29 11:31:53 +00004756% GetDrawInfo() initializes draw_info to default values from image_info.
cristy3ed852e2009-09-05 21:47:34 +00004757%
4758% The format of the GetDrawInfo method is:
4759%
4760% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4761%
4762% A description of each parameter follows:
4763%
4764% o image_info: the image info..
4765%
4766% o draw_info: the draw info.
4767%
4768*/
4769MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4770{
4771 const char
4772 *option;
4773
4774 ExceptionInfo
4775 *exception;
4776
cristy3ed852e2009-09-05 21:47:34 +00004777 /*
4778 Initialize draw attributes.
4779 */
4780 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4781 assert(draw_info != (DrawInfo *) NULL);
4782 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +00004783 GetAffineMatrix(&draw_info->affine);
4784 exception=AcquireExceptionInfo();
cristyfad60c92012-01-19 18:32:39 +00004785 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
cristy9950d572011-10-01 18:22:35 +00004786 exception);
cristyfad60c92012-01-19 18:32:39 +00004787 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
cristy9950d572011-10-01 18:22:35 +00004788 exception);
cristy3ed852e2009-09-05 21:47:34 +00004789 draw_info->stroke_width=1.0;
cristy4c08aed2011-07-01 19:47:50 +00004790 draw_info->alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004791 draw_info->fill_rule=EvenOddRule;
4792 draw_info->linecap=ButtCap;
4793 draw_info->linejoin=MiterJoin;
4794 draw_info->miterlimit=10;
4795 draw_info->decorate=NoDecoration;
cristy3ed852e2009-09-05 21:47:34 +00004796 draw_info->pointsize=12.0;
cristy4c08aed2011-07-01 19:47:50 +00004797 draw_info->undercolor.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004798 draw_info->compose=OverCompositeOp;
cristy3ed852e2009-09-05 21:47:34 +00004799 draw_info->render=MagickTrue;
4800 draw_info->debug=IsEventLogging();
cristy0245b5d2011-10-05 12:03:49 +00004801 if (image_info != (ImageInfo *) NULL)
4802 {
4803 draw_info->stroke_antialias=image_info->antialias;
4804 if (image_info->font != (char *) NULL)
4805 draw_info->font=AcquireString(image_info->font);
4806 if (image_info->density != (char *) NULL)
4807 draw_info->density=AcquireString(image_info->density);
4808 draw_info->text_antialias=image_info->antialias;
4809 if (image_info->pointsize != 0.0)
4810 draw_info->pointsize=image_info->pointsize;
4811 draw_info->border_color=image_info->border_color;
4812 if (image_info->server_name != (char *) NULL)
4813 draw_info->server_name=AcquireString(image_info->server_name);
4814 option=GetImageOption(image_info,"encoding");
4815 if (option != (const char *) NULL)
4816 (void) CloneString(&draw_info->encoding,option);
4817 option=GetImageOption(image_info,"kerning");
4818 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004819 draw_info->kerning=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004820 option=GetImageOption(image_info,"interline-spacing");
4821 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004822 draw_info->interline_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004823 option=GetImageOption(image_info,"interword-spacing");
4824 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004825 draw_info->interword_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004826 option=GetImageOption(image_info,"direction");
4827 if (option != (const char *) NULL)
4828 draw_info->direction=(DirectionType) ParseCommandOption(
4829 MagickDirectionOptions,MagickFalse,option);
anthony42f6c202011-10-23 10:28:55 +00004830 else
4831 draw_info->direction=UndefinedDirection;
cristy0245b5d2011-10-05 12:03:49 +00004832 option=GetImageOption(image_info,"fill");
4833 if (option != (const char *) NULL)
4834 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
4835 exception);
4836 option=GetImageOption(image_info,"stroke");
4837 if (option != (const char *) NULL)
4838 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
4839 exception);
4840 option=GetImageOption(image_info,"strokewidth");
4841 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004842 draw_info->stroke_width=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004843 option=GetImageOption(image_info,"undercolor");
4844 if (option != (const char *) NULL)
4845 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
4846 exception);
4847 option=GetImageOption(image_info,"gravity");
4848 if (option != (const char *) NULL)
4849 draw_info->gravity=(GravityType) ParseCommandOption(
4850 MagickGravityOptions,MagickFalse,option);
4851 }
cristy3ed852e2009-09-05 21:47:34 +00004852 exception=DestroyExceptionInfo(exception);
4853 draw_info->signature=MagickSignature;
cristy3ed852e2009-09-05 21:47:34 +00004854}
4855
4856/*
4857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4858% %
4859% %
4860% %
4861+ P e r m u t a t e %
4862% %
4863% %
4864% %
4865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4866%
4867% Permutate() returns the permuation of the (n,k).
4868%
4869% The format of the Permutate method is:
4870%
cristybb503372010-05-27 20:51:26 +00004871% void Permutate(ssize_t n,ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004872%
4873% A description of each parameter follows:
4874%
4875% o n:
4876%
4877% o k:
4878%
4879%
4880*/
cristybb503372010-05-27 20:51:26 +00004881static inline MagickRealType Permutate(const ssize_t n,const ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004882{
4883 MagickRealType
4884 r;
4885
cristybb503372010-05-27 20:51:26 +00004886 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004887 i;
4888
4889 r=1.0;
4890 for (i=k+1; i <= n; i++)
4891 r*=i;
4892 for (i=1; i <= (n-k); i++)
4893 r/=i;
4894 return(r);
4895}
4896
4897/*
4898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4899% %
4900% %
4901% %
4902+ T r a c e P r i m i t i v e %
4903% %
4904% %
4905% %
4906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4907%
4908% TracePrimitive is a collection of methods for generating graphic
4909% primitives such as arcs, ellipses, paths, etc.
4910%
4911*/
4912
4913static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4914 const PointInfo end,const PointInfo degrees)
4915{
4916 PointInfo
4917 center,
4918 radii;
4919
4920 center.x=0.5*(end.x+start.x);
4921 center.y=0.5*(end.y+start.y);
4922 radii.x=fabs(center.x-start.x);
4923 radii.y=fabs(center.y-start.y);
4924 TraceEllipse(primitive_info,center,radii,degrees);
4925}
4926
4927static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
4928 const PointInfo end,const PointInfo arc,const MagickRealType angle,
4929 const MagickBooleanType large_arc,const MagickBooleanType sweep)
4930{
4931 MagickRealType
4932 alpha,
4933 beta,
4934 delta,
4935 factor,
4936 gamma,
4937 theta;
4938
4939 PointInfo
4940 center,
4941 points[3],
4942 radii;
4943
4944 register MagickRealType
4945 cosine,
4946 sine;
4947
4948 register PrimitiveInfo
4949 *p;
4950
cristybb503372010-05-27 20:51:26 +00004951 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004952 i;
4953
cristybb503372010-05-27 20:51:26 +00004954 size_t
cristy3ed852e2009-09-05 21:47:34 +00004955 arc_segments;
4956
4957 if ((start.x == end.x) && (start.y == end.y))
4958 {
4959 TracePoint(primitive_info,end);
4960 return;
4961 }
4962 radii.x=fabs(arc.x);
4963 radii.y=fabs(arc.y);
4964 if ((radii.x == 0.0) || (radii.y == 0.0))
4965 {
4966 TraceLine(primitive_info,start,end);
4967 return;
4968 }
4969 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
4970 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
4971 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
4972 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
4973 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
4974 (radii.y*radii.y);
4975 if (delta < MagickEpsilon)
4976 {
4977 TraceLine(primitive_info,start,end);
4978 return;
4979 }
4980 if (delta > 1.0)
4981 {
4982 radii.x*=sqrt((double) delta);
4983 radii.y*=sqrt((double) delta);
4984 }
4985 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
4986 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
4987 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
4988 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
4989 alpha=points[1].x-points[0].x;
4990 beta=points[1].y-points[0].y;
4991 factor=1.0/(alpha*alpha+beta*beta)-0.25;
4992 if (factor <= 0.0)
4993 factor=0.0;
4994 else
4995 {
4996 factor=sqrt((double) factor);
4997 if (sweep == large_arc)
4998 factor=(-factor);
4999 }
5000 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
5001 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
5002 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
5003 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
5004 if ((theta < 0.0) && (sweep != MagickFalse))
5005 theta+=(MagickRealType) (2.0*MagickPI);
5006 else
5007 if ((theta > 0.0) && (sweep == MagickFalse))
5008 theta-=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00005009 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
cristy20be8a02010-08-17 00:23:28 +00005010 MagickEpsilon))));
cristy3ed852e2009-09-05 21:47:34 +00005011 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005012 for (i=0; i < (ssize_t) arc_segments; i++)
cristy3ed852e2009-09-05 21:47:34 +00005013 {
5014 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
5015 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
5016 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
5017 sin(fmod((double) beta,DegreesToRadians(360.0)));
5018 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
5019 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
5020 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5021 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
5022 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
5023 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5024 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
5025 theta/arc_segments),DegreesToRadians(360.0))));
5026 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
5027 theta/arc_segments),DegreesToRadians(360.0))));
5028 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
5029 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5030 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
5031 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5032 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
5033 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
5034 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
5035 points[0].y);
5036 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
5037 points[0].y);
5038 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
5039 points[1].y);
5040 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
5041 points[1].y);
5042 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
5043 points[2].y);
5044 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
5045 points[2].y);
cristybb503372010-05-27 20:51:26 +00005046 if (i == (ssize_t) (arc_segments-1))
cristy3ed852e2009-09-05 21:47:34 +00005047 (p+3)->point=end;
5048 TraceBezier(p,4);
5049 p+=p->coordinates;
5050 }
cristybb503372010-05-27 20:51:26 +00005051 primitive_info->coordinates=(size_t) (p-primitive_info);
5052 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005053 {
5054 p->primitive=primitive_info->primitive;
5055 p--;
5056 }
5057}
5058
5059static void TraceBezier(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005060 const size_t number_coordinates)
cristy3ed852e2009-09-05 21:47:34 +00005061{
5062 MagickRealType
5063 alpha,
5064 *coefficients,
5065 weight;
5066
5067 PointInfo
5068 end,
5069 point,
5070 *points;
5071
cristy826a5472010-08-31 23:21:38 +00005072 register PrimitiveInfo
5073 *p;
5074
cristybb503372010-05-27 20:51:26 +00005075 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005076 i,
5077 j;
5078
cristybb503372010-05-27 20:51:26 +00005079 size_t
cristy3ed852e2009-09-05 21:47:34 +00005080 control_points,
5081 quantum;
5082
5083 /*
5084 Allocate coeficients.
5085 */
5086 quantum=number_coordinates;
cristybb503372010-05-27 20:51:26 +00005087 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005088 {
cristybb503372010-05-27 20:51:26 +00005089 for (j=i+1; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005090 {
5091 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
5092 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005093 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005094 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
5095 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005096 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005097 }
5098 }
cristybb503372010-05-27 20:51:26 +00005099 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
cristy3ed852e2009-09-05 21:47:34 +00005100 (double) BezierQuantum);
5101 control_points=quantum*number_coordinates;
5102 coefficients=(MagickRealType *) AcquireQuantumMemory((size_t)
5103 number_coordinates,sizeof(*coefficients));
5104 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5105 sizeof(*points));
5106 if ((coefficients == (MagickRealType *) NULL) ||
5107 (points == (PointInfo *) NULL))
5108 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5109 /*
5110 Compute bezier points.
5111 */
5112 end=primitive_info[number_coordinates-1].point;
cristybb503372010-05-27 20:51:26 +00005113 for (i=0; i < (ssize_t) number_coordinates; i++)
5114 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
cristy3ed852e2009-09-05 21:47:34 +00005115 weight=0.0;
cristybb503372010-05-27 20:51:26 +00005116 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005117 {
5118 p=primitive_info;
5119 point.x=0.0;
5120 point.y=0.0;
5121 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
cristybb503372010-05-27 20:51:26 +00005122 for (j=0; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005123 {
5124 point.x+=alpha*coefficients[j]*p->point.x;
5125 point.y+=alpha*coefficients[j]*p->point.y;
5126 alpha*=weight/(1.0-weight);
5127 p++;
5128 }
5129 points[i]=point;
5130 weight+=1.0/control_points;
5131 }
5132 /*
5133 Bezier curves are just short segmented polys.
5134 */
5135 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005136 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005137 {
5138 TracePoint(p,points[i]);
5139 p+=p->coordinates;
5140 }
5141 TracePoint(p,end);
5142 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005143 primitive_info->coordinates=(size_t) (p-primitive_info);
5144 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005145 {
5146 p->primitive=primitive_info->primitive;
5147 p--;
5148 }
5149 points=(PointInfo *) RelinquishMagickMemory(points);
5150 coefficients=(MagickRealType *) RelinquishMagickMemory(coefficients);
5151}
5152
5153static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5154 const PointInfo end)
5155{
5156 MagickRealType
5157 alpha,
5158 beta,
5159 radius;
5160
5161 PointInfo
5162 offset,
5163 degrees;
5164
5165 alpha=end.x-start.x;
5166 beta=end.y-start.y;
5167 radius=hypot((double) alpha,(double) beta);
5168 offset.x=(double) radius;
5169 offset.y=(double) radius;
5170 degrees.x=0.0;
5171 degrees.y=360.0;
5172 TraceEllipse(primitive_info,start,offset,degrees);
5173}
5174
5175static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5176 const PointInfo stop,const PointInfo degrees)
5177{
5178 MagickRealType
5179 delta,
5180 step,
5181 y;
5182
5183 PointInfo
5184 angle,
5185 point;
5186
5187 register PrimitiveInfo
5188 *p;
5189
cristybb503372010-05-27 20:51:26 +00005190 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005191 i;
5192
5193 /*
5194 Ellipses are just short segmented polys.
5195 */
5196 if ((stop.x == 0.0) && (stop.y == 0.0))
5197 {
5198 TracePoint(primitive_info,start);
5199 return;
5200 }
5201 delta=2.0/MagickMax(stop.x,stop.y);
5202 step=(MagickRealType) (MagickPI/8.0);
cristyd4e3ffa2011-01-20 13:47:55 +00005203 if ((delta >= 0.0) && (delta < (MagickRealType) (MagickPI/8.0)))
5204 step=(MagickRealType) (MagickPI/(4*(MagickPI/delta/2+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00005205 angle.x=DegreesToRadians(degrees.x);
5206 y=degrees.y;
5207 while (y < degrees.x)
5208 y+=360.0;
5209 angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
5210 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5211 {
5212 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5213 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5214 TracePoint(p,point);
5215 p+=p->coordinates;
5216 }
5217 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5218 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5219 TracePoint(p,point);
5220 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005221 primitive_info->coordinates=(size_t) (p-primitive_info);
5222 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005223 {
5224 p->primitive=primitive_info->primitive;
5225 p--;
5226 }
5227}
5228
5229static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5230 const PointInfo end)
5231{
5232 TracePoint(primitive_info,start);
5233 if ((fabs(start.x-end.x) <= MagickEpsilon) &&
5234 (fabs(start.y-end.y) <= MagickEpsilon))
5235 {
5236 primitive_info->primitive=PointPrimitive;
5237 primitive_info->coordinates=1;
5238 return;
5239 }
5240 TracePoint(primitive_info+1,end);
5241 (primitive_info+1)->primitive=primitive_info->primitive;
5242 primitive_info->coordinates=2;
5243}
5244
cristybb503372010-05-27 20:51:26 +00005245static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00005246{
5247 char
5248 token[MaxTextExtent];
5249
5250 const char
5251 *p;
5252
5253 int
5254 attribute,
5255 last_attribute;
5256
5257 MagickRealType
5258 x,
5259 y;
5260
5261 PointInfo
5262 end,
5263 points[4],
5264 point,
5265 start;
5266
5267 PrimitiveType
5268 primitive_type;
5269
5270 register PrimitiveInfo
5271 *q;
5272
cristybb503372010-05-27 20:51:26 +00005273 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005274 i;
5275
cristybb503372010-05-27 20:51:26 +00005276 size_t
cristy3ed852e2009-09-05 21:47:34 +00005277 number_coordinates,
5278 z_count;
5279
5280 attribute=0;
5281 point.x=0.0;
5282 point.y=0.0;
5283 start.x=0.0;
5284 start.y=0.0;
5285 number_coordinates=0;
5286 z_count=0;
5287 primitive_type=primitive_info->primitive;
5288 q=primitive_info;
5289 for (p=path; *p != '\0'; )
5290 {
5291 while (isspace((int) ((unsigned char) *p)) != 0)
5292 p++;
5293 if (*p == '\0')
5294 break;
5295 last_attribute=attribute;
5296 attribute=(int) (*p++);
5297 switch (attribute)
5298 {
5299 case 'a':
5300 case 'A':
5301 {
5302 MagickBooleanType
5303 large_arc,
5304 sweep;
5305
5306 MagickRealType
5307 angle;
5308
5309 PointInfo
5310 arc;
5311
5312 /*
5313 Compute arc points.
5314 */
5315 do
5316 {
5317 GetMagickToken(p,&p,token);
5318 if (*token == ',')
5319 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005320 arc.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005321 GetMagickToken(p,&p,token);
5322 if (*token == ',')
5323 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005324 arc.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005325 GetMagickToken(p,&p,token);
5326 if (*token == ',')
5327 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005328 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005329 GetMagickToken(p,&p,token);
5330 if (*token == ',')
5331 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005332 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005333 GetMagickToken(p,&p,token);
5334 if (*token == ',')
5335 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005336 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005337 GetMagickToken(p,&p,token);
5338 if (*token == ',')
5339 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005340 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005341 GetMagickToken(p,&p,token);
5342 if (*token == ',')
5343 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005344 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005345 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5346 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5347 TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5348 q+=q->coordinates;
5349 point=end;
cristy671fcfc2011-12-13 13:28:31 +00005350 while (isspace((int) ((unsigned char) *p)) != 0)
5351 p++;
5352 if (*p == ',')
5353 p++;
cristy3ed852e2009-09-05 21:47:34 +00005354 } while (IsPoint(p) != MagickFalse);
5355 break;
5356 }
5357 case 'c':
5358 case 'C':
5359 {
5360 /*
5361 Compute bezier points.
5362 */
5363 do
5364 {
5365 points[0]=point;
5366 for (i=1; i < 4; i++)
5367 {
5368 GetMagickToken(p,&p,token);
5369 if (*token == ',')
5370 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005371 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005372 GetMagickToken(p,&p,token);
5373 if (*token == ',')
5374 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005375 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005376 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5377 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5378 points[i]=end;
5379 }
5380 for (i=0; i < 4; i++)
5381 (q+i)->point=points[i];
5382 TraceBezier(q,4);
5383 q+=q->coordinates;
5384 point=end;
5385 } while (IsPoint(p) != MagickFalse);
5386 break;
5387 }
5388 case 'H':
5389 case 'h':
5390 {
5391 do
5392 {
5393 GetMagickToken(p,&p,token);
5394 if (*token == ',')
5395 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005396 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005397 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
5398 TracePoint(q,point);
5399 q+=q->coordinates;
5400 } while (IsPoint(p) != MagickFalse);
5401 break;
5402 }
5403 case 'l':
5404 case 'L':
5405 {
5406 do
5407 {
5408 GetMagickToken(p,&p,token);
5409 if (*token == ',')
5410 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005411 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005412 GetMagickToken(p,&p,token);
5413 if (*token == ',')
5414 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005415 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005416 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
5417 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
5418 TracePoint(q,point);
5419 q+=q->coordinates;
5420 } while (IsPoint(p) != MagickFalse);
5421 break;
5422 }
5423 case 'M':
5424 case 'm':
5425 {
5426 if (q != primitive_info)
5427 {
cristybb503372010-05-27 20:51:26 +00005428 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005429 number_coordinates+=primitive_info->coordinates;
5430 primitive_info=q;
5431 }
5432 i=0;
5433 do
5434 {
5435 GetMagickToken(p,&p,token);
5436 if (*token == ',')
5437 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005438 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005439 GetMagickToken(p,&p,token);
5440 if (*token == ',')
5441 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005442 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005443 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
5444 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
5445 if (i == 0)
5446 start=point;
5447 i++;
5448 TracePoint(q,point);
5449 q+=q->coordinates;
cristy826a5472010-08-31 23:21:38 +00005450 if ((i != 0) && (attribute == (int) 'M'))
cristy3ed852e2009-09-05 21:47:34 +00005451 {
5452 TracePoint(q,point);
5453 q+=q->coordinates;
5454 }
5455 } while (IsPoint(p) != MagickFalse);
5456 break;
5457 }
5458 case 'q':
5459 case 'Q':
5460 {
5461 /*
5462 Compute bezier points.
5463 */
5464 do
5465 {
5466 points[0]=point;
5467 for (i=1; i < 3; i++)
5468 {
5469 GetMagickToken(p,&p,token);
5470 if (*token == ',')
5471 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005472 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005473 GetMagickToken(p,&p,token);
5474 if (*token == ',')
5475 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005476 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005477 if (*p == ',')
5478 p++;
5479 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
5480 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
5481 points[i]=end;
5482 }
5483 for (i=0; i < 3; i++)
5484 (q+i)->point=points[i];
5485 TraceBezier(q,3);
5486 q+=q->coordinates;
5487 point=end;
5488 } while (IsPoint(p) != MagickFalse);
5489 break;
5490 }
5491 case 's':
5492 case 'S':
5493 {
5494 /*
5495 Compute bezier points.
5496 */
5497 do
5498 {
5499 points[0]=points[3];
5500 points[1].x=2.0*points[3].x-points[2].x;
5501 points[1].y=2.0*points[3].y-points[2].y;
5502 for (i=2; i < 4; i++)
5503 {
5504 GetMagickToken(p,&p,token);
5505 if (*token == ',')
5506 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005507 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005508 GetMagickToken(p,&p,token);
5509 if (*token == ',')
5510 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005511 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005512 if (*p == ',')
5513 p++;
5514 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
5515 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
5516 points[i]=end;
5517 }
5518 if (strchr("CcSs",last_attribute) == (char *) NULL)
5519 {
5520 points[0]=points[2];
5521 points[1]=points[3];
5522 }
5523 for (i=0; i < 4; i++)
5524 (q+i)->point=points[i];
5525 TraceBezier(q,4);
5526 q+=q->coordinates;
5527 point=end;
5528 } while (IsPoint(p) != MagickFalse);
5529 break;
5530 }
5531 case 't':
5532 case 'T':
5533 {
5534 /*
5535 Compute bezier points.
5536 */
5537 do
5538 {
5539 points[0]=points[2];
5540 points[1].x=2.0*points[2].x-points[1].x;
5541 points[1].y=2.0*points[2].y-points[1].y;
5542 for (i=2; i < 3; i++)
5543 {
5544 GetMagickToken(p,&p,token);
5545 if (*token == ',')
5546 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005547 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005548 GetMagickToken(p,&p,token);
5549 if (*token == ',')
5550 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005551 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005552 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
5553 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
5554 points[i]=end;
5555 }
5556 if (strchr("QqTt",last_attribute) == (char *) NULL)
5557 {
5558 points[0]=points[2];
5559 points[1]=points[3];
5560 }
5561 for (i=0; i < 3; i++)
5562 (q+i)->point=points[i];
5563 TraceBezier(q,3);
5564 q+=q->coordinates;
5565 point=end;
5566 } while (IsPoint(p) != MagickFalse);
5567 break;
5568 }
5569 case 'v':
5570 case 'V':
5571 {
5572 do
5573 {
5574 GetMagickToken(p,&p,token);
5575 if (*token == ',')
5576 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005577 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005578 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
5579 TracePoint(q,point);
5580 q+=q->coordinates;
5581 } while (IsPoint(p) != MagickFalse);
5582 break;
5583 }
5584 case 'z':
5585 case 'Z':
5586 {
5587 point=start;
5588 TracePoint(q,point);
5589 q+=q->coordinates;
cristybb503372010-05-27 20:51:26 +00005590 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005591 number_coordinates+=primitive_info->coordinates;
5592 primitive_info=q;
5593 z_count++;
5594 break;
5595 }
5596 default:
5597 {
5598 if (isalpha((int) ((unsigned char) attribute)) != 0)
cristy1e604812011-05-19 18:07:50 +00005599 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n",
5600 attribute);
cristy3ed852e2009-09-05 21:47:34 +00005601 break;
5602 }
5603 }
5604 }
cristybb503372010-05-27 20:51:26 +00005605 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005606 number_coordinates+=primitive_info->coordinates;
cristybb503372010-05-27 20:51:26 +00005607 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005608 {
5609 q--;
5610 q->primitive=primitive_type;
5611 if (z_count > 1)
5612 q->method=FillToBorderMethod;
5613 }
5614 q=primitive_info;
5615 return(number_coordinates);
5616}
5617
5618static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
5619 const PointInfo end)
5620{
5621 PointInfo
5622 point;
5623
5624 register PrimitiveInfo
5625 *p;
5626
cristybb503372010-05-27 20:51:26 +00005627 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005628 i;
5629
5630 p=primitive_info;
5631 TracePoint(p,start);
5632 p+=p->coordinates;
5633 point.x=start.x;
5634 point.y=end.y;
5635 TracePoint(p,point);
5636 p+=p->coordinates;
5637 TracePoint(p,end);
5638 p+=p->coordinates;
5639 point.x=end.x;
5640 point.y=start.y;
5641 TracePoint(p,point);
5642 p+=p->coordinates;
5643 TracePoint(p,start);
5644 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005645 primitive_info->coordinates=(size_t) (p-primitive_info);
5646 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005647 {
5648 p->primitive=primitive_info->primitive;
5649 p--;
5650 }
5651}
5652
5653static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
5654 const PointInfo start,const PointInfo end,PointInfo arc)
5655{
5656 PointInfo
5657 degrees,
5658 offset,
5659 point;
5660
5661 register PrimitiveInfo
5662 *p;
5663
cristybb503372010-05-27 20:51:26 +00005664 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005665 i;
5666
5667 p=primitive_info;
5668 offset.x=fabs(end.x-start.x);
5669 offset.y=fabs(end.y-start.y);
5670 if (arc.x > (0.5*offset.x))
5671 arc.x=0.5*offset.x;
5672 if (arc.y > (0.5*offset.y))
5673 arc.y=0.5*offset.y;
5674 point.x=start.x+offset.x-arc.x;
5675 point.y=start.y+arc.y;
5676 degrees.x=270.0;
5677 degrees.y=360.0;
5678 TraceEllipse(p,point,arc,degrees);
5679 p+=p->coordinates;
5680 point.x=start.x+offset.x-arc.x;
5681 point.y=start.y+offset.y-arc.y;
5682 degrees.x=0.0;
5683 degrees.y=90.0;
5684 TraceEllipse(p,point,arc,degrees);
5685 p+=p->coordinates;
5686 point.x=start.x+arc.x;
5687 point.y=start.y+offset.y-arc.y;
5688 degrees.x=90.0;
5689 degrees.y=180.0;
5690 TraceEllipse(p,point,arc,degrees);
5691 p+=p->coordinates;
5692 point.x=start.x+arc.x;
5693 point.y=start.y+arc.y;
5694 degrees.x=180.0;
5695 degrees.y=270.0;
5696 TraceEllipse(p,point,arc,degrees);
5697 p+=p->coordinates;
5698 TracePoint(p,primitive_info->point);
5699 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005700 primitive_info->coordinates=(size_t) (p-primitive_info);
5701 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005702 {
5703 p->primitive=primitive_info->primitive;
5704 p--;
5705 }
5706}
5707
5708static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005709 const size_t number_vertices,const MagickRealType offset)
cristy3ed852e2009-09-05 21:47:34 +00005710{
5711 MagickRealType
5712 distance;
5713
cristy3ed852e2009-09-05 21:47:34 +00005714 register MagickRealType
5715 dx,
5716 dy;
5717
cristybb503372010-05-27 20:51:26 +00005718 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005719 i;
5720
cristy826a5472010-08-31 23:21:38 +00005721 ssize_t
5722 j;
5723
cristy3ed852e2009-09-05 21:47:34 +00005724 dx=0.0;
5725 dy=0.0;
cristybb503372010-05-27 20:51:26 +00005726 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005727 {
5728 dx=primitive_info[0].point.x-primitive_info[i].point.x;
5729 dy=primitive_info[0].point.y-primitive_info[i].point.y;
5730 if ((fabs((double) dx) >= MagickEpsilon) ||
5731 (fabs((double) dy) >= MagickEpsilon))
5732 break;
5733 }
cristybb503372010-05-27 20:51:26 +00005734 if (i == (ssize_t) number_vertices)
5735 i=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005736 distance=hypot((double) dx,(double) dy);
5737 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
5738 dx*(distance+offset)/distance);
5739 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
5740 dy*(distance+offset)/distance);
cristybb503372010-05-27 20:51:26 +00005741 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00005742 {
5743 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
5744 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
5745 if ((fabs((double) dx) >= MagickEpsilon) ||
5746 (fabs((double) dy) >= MagickEpsilon))
5747 break;
5748 }
5749 distance=hypot((double) dx,(double) dy);
5750 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
5751 dx*(distance+offset)/distance);
5752 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
5753 dy*(distance+offset)/distance);
5754}
5755
5756static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
5757 const PrimitiveInfo *primitive_info)
5758{
5759 typedef struct _LineSegment
5760 {
5761 double
5762 p,
5763 q;
5764 } LineSegment;
5765
5766 LineSegment
5767 dx,
5768 dy,
5769 inverse_slope,
5770 slope,
5771 theta;
5772
cristy3ed852e2009-09-05 21:47:34 +00005773 MagickBooleanType
5774 closed_path;
5775
5776 MagickRealType
5777 delta_theta,
5778 dot_product,
5779 mid,
5780 miterlimit;
5781
5782 PointInfo
5783 box_p[5],
5784 box_q[5],
5785 center,
5786 offset,
5787 *path_p,
5788 *path_q;
5789
5790 PrimitiveInfo
5791 *polygon_primitive,
5792 *stroke_polygon;
5793
cristybb503372010-05-27 20:51:26 +00005794 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005795 i;
5796
cristybb503372010-05-27 20:51:26 +00005797 size_t
cristy3ed852e2009-09-05 21:47:34 +00005798 arc_segments,
5799 max_strokes,
5800 number_vertices;
5801
cristy826a5472010-08-31 23:21:38 +00005802 ssize_t
5803 j,
5804 n,
5805 p,
5806 q;
5807
cristy3ed852e2009-09-05 21:47:34 +00005808 /*
5809 Allocate paths.
5810 */
5811 number_vertices=primitive_info->coordinates;
5812 max_strokes=2*number_vertices+6*BezierQuantum+360;
5813 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5814 sizeof(*path_p));
5815 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5816 sizeof(*path_q));
5817 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
5818 number_vertices+2UL,sizeof(*polygon_primitive));
5819 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
5820 (polygon_primitive == (PrimitiveInfo *) NULL))
5821 return((PrimitiveInfo *) NULL);
5822 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
5823 number_vertices*sizeof(*polygon_primitive));
5824 closed_path=
5825 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
5826 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
5827 MagickTrue : MagickFalse;
5828 if ((draw_info->linejoin == RoundJoin) ||
5829 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
5830 {
5831 polygon_primitive[number_vertices]=primitive_info[1];
5832 number_vertices++;
5833 }
5834 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
5835 /*
5836 Compute the slope for the first line segment, p.
5837 */
5838 dx.p=0.0;
5839 dy.p=0.0;
cristybb503372010-05-27 20:51:26 +00005840 for (n=1; n < (ssize_t) number_vertices; n++)
cristy3ed852e2009-09-05 21:47:34 +00005841 {
5842 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
5843 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
5844 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
5845 break;
5846 }
cristybb503372010-05-27 20:51:26 +00005847 if (n == (ssize_t) number_vertices)
5848 n=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005849 slope.p=0.0;
5850 inverse_slope.p=0.0;
5851 if (fabs(dx.p) <= MagickEpsilon)
5852 {
5853 if (dx.p >= 0.0)
5854 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5855 else
5856 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5857 }
5858 else
5859 if (fabs(dy.p) <= MagickEpsilon)
5860 {
5861 if (dy.p >= 0.0)
5862 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5863 else
5864 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5865 }
5866 else
5867 {
5868 slope.p=dy.p/dx.p;
5869 inverse_slope.p=(-1.0/slope.p);
5870 }
5871 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5872 miterlimit=(MagickRealType) (draw_info->miterlimit*draw_info->miterlimit*
5873 mid*mid);
5874 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
5875 TraceSquareLinecap(polygon_primitive,number_vertices,mid);
5876 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
5877 offset.y=(double) (offset.x*inverse_slope.p);
5878 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
5879 {
5880 box_p[0].x=polygon_primitive[0].point.x-offset.x;
5881 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
5882 box_p[1].x=polygon_primitive[n].point.x-offset.x;
5883 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
5884 box_q[0].x=polygon_primitive[0].point.x+offset.x;
5885 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
5886 box_q[1].x=polygon_primitive[n].point.x+offset.x;
5887 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
5888 }
5889 else
5890 {
5891 box_p[0].x=polygon_primitive[0].point.x+offset.x;
5892 box_p[0].y=polygon_primitive[0].point.y+offset.y;
5893 box_p[1].x=polygon_primitive[n].point.x+offset.x;
5894 box_p[1].y=polygon_primitive[n].point.y+offset.y;
5895 box_q[0].x=polygon_primitive[0].point.x-offset.x;
5896 box_q[0].y=polygon_primitive[0].point.y-offset.y;
5897 box_q[1].x=polygon_primitive[n].point.x-offset.x;
5898 box_q[1].y=polygon_primitive[n].point.y-offset.y;
5899 }
5900 /*
5901 Create strokes for the line join attribute: bevel, miter, round.
5902 */
5903 p=0;
5904 q=0;
5905 path_q[p++]=box_q[0];
5906 path_p[q++]=box_p[0];
cristybb503372010-05-27 20:51:26 +00005907 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005908 {
5909 /*
5910 Compute the slope for this line segment, q.
5911 */
5912 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
5913 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
5914 dot_product=dx.q*dx.q+dy.q*dy.q;
5915 if (dot_product < 0.25)
5916 continue;
5917 slope.q=0.0;
5918 inverse_slope.q=0.0;
5919 if (fabs(dx.q) < MagickEpsilon)
5920 {
5921 if (dx.q >= 0.0)
5922 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5923 else
5924 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5925 }
5926 else
5927 if (fabs(dy.q) <= MagickEpsilon)
5928 {
5929 if (dy.q >= 0.0)
5930 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5931 else
5932 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5933 }
5934 else
5935 {
5936 slope.q=dy.q/dx.q;
5937 inverse_slope.q=(-1.0/slope.q);
5938 }
5939 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
5940 offset.y=(double) (offset.x*inverse_slope.q);
5941 dot_product=dy.q*offset.x-dx.q*offset.y;
5942 if (dot_product > 0.0)
5943 {
5944 box_p[2].x=polygon_primitive[n].point.x-offset.x;
5945 box_p[2].y=polygon_primitive[n].point.y-offset.y;
5946 box_p[3].x=polygon_primitive[i].point.x-offset.x;
5947 box_p[3].y=polygon_primitive[i].point.y-offset.y;
5948 box_q[2].x=polygon_primitive[n].point.x+offset.x;
5949 box_q[2].y=polygon_primitive[n].point.y+offset.y;
5950 box_q[3].x=polygon_primitive[i].point.x+offset.x;
5951 box_q[3].y=polygon_primitive[i].point.y+offset.y;
5952 }
5953 else
5954 {
5955 box_p[2].x=polygon_primitive[n].point.x+offset.x;
5956 box_p[2].y=polygon_primitive[n].point.y+offset.y;
5957 box_p[3].x=polygon_primitive[i].point.x+offset.x;
5958 box_p[3].y=polygon_primitive[i].point.y+offset.y;
5959 box_q[2].x=polygon_primitive[n].point.x-offset.x;
5960 box_q[2].y=polygon_primitive[n].point.y-offset.y;
5961 box_q[3].x=polygon_primitive[i].point.x-offset.x;
5962 box_q[3].y=polygon_primitive[i].point.y-offset.y;
5963 }
5964 if (fabs((double) (slope.p-slope.q)) <= MagickEpsilon)
5965 {
5966 box_p[4]=box_p[1];
5967 box_q[4]=box_q[1];
5968 }
5969 else
5970 {
5971 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
5972 box_p[3].y)/(slope.p-slope.q));
5973 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
5974 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
5975 box_q[3].y)/(slope.p-slope.q));
5976 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
5977 }
cristybb503372010-05-27 20:51:26 +00005978 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00005979 {
5980 max_strokes+=6*BezierQuantum+360;
5981 path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
5982 sizeof(*path_p));
5983 path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
5984 sizeof(*path_q));
5985 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
5986 {
5987 polygon_primitive=(PrimitiveInfo *)
5988 RelinquishMagickMemory(polygon_primitive);
5989 return((PrimitiveInfo *) NULL);
5990 }
5991 }
5992 dot_product=dx.q*dy.p-dx.p*dy.q;
5993 if (dot_product <= 0.0)
5994 switch (draw_info->linejoin)
5995 {
5996 case BevelJoin:
5997 {
5998 path_q[q++]=box_q[1];
5999 path_q[q++]=box_q[2];
6000 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6001 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6002 if (dot_product <= miterlimit)
6003 path_p[p++]=box_p[4];
6004 else
6005 {
6006 path_p[p++]=box_p[1];
6007 path_p[p++]=box_p[2];
6008 }
6009 break;
6010 }
6011 case MiterJoin:
6012 {
6013 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6014 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6015 if (dot_product <= miterlimit)
6016 {
6017 path_q[q++]=box_q[4];
6018 path_p[p++]=box_p[4];
6019 }
6020 else
6021 {
6022 path_q[q++]=box_q[1];
6023 path_q[q++]=box_q[2];
6024 path_p[p++]=box_p[1];
6025 path_p[p++]=box_p[2];
6026 }
6027 break;
6028 }
6029 case RoundJoin:
6030 {
6031 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6032 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6033 if (dot_product <= miterlimit)
6034 path_p[p++]=box_p[4];
6035 else
6036 {
6037 path_p[p++]=box_p[1];
6038 path_p[p++]=box_p[2];
6039 }
6040 center=polygon_primitive[n].point;
6041 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
6042 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
6043 if (theta.q < theta.p)
6044 theta.q+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006045 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
cristy3ed852e2009-09-05 21:47:34 +00006046 (2.0*sqrt((double) (1.0/mid)))));
6047 path_q[q].x=box_q[1].x;
6048 path_q[q].y=box_q[1].y;
6049 q++;
cristybb503372010-05-27 20:51:26 +00006050 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006051 {
6052 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6053 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
6054 (theta.p+delta_theta),DegreesToRadians(360.0))));
6055 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
6056 (theta.p+delta_theta),DegreesToRadians(360.0))));
6057 q++;
6058 }
6059 path_q[q++]=box_q[2];
6060 break;
6061 }
6062 default:
6063 break;
6064 }
6065 else
6066 switch (draw_info->linejoin)
6067 {
6068 case BevelJoin:
6069 {
6070 path_p[p++]=box_p[1];
6071 path_p[p++]=box_p[2];
6072 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6073 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6074 if (dot_product <= miterlimit)
6075 path_q[q++]=box_q[4];
6076 else
6077 {
6078 path_q[q++]=box_q[1];
6079 path_q[q++]=box_q[2];
6080 }
6081 break;
6082 }
6083 case MiterJoin:
6084 {
6085 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6086 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6087 if (dot_product <= miterlimit)
6088 {
6089 path_q[q++]=box_q[4];
6090 path_p[p++]=box_p[4];
6091 }
6092 else
6093 {
6094 path_q[q++]=box_q[1];
6095 path_q[q++]=box_q[2];
6096 path_p[p++]=box_p[1];
6097 path_p[p++]=box_p[2];
6098 }
6099 break;
6100 }
6101 case RoundJoin:
6102 {
6103 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6104 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6105 if (dot_product <= miterlimit)
6106 path_q[q++]=box_q[4];
6107 else
6108 {
6109 path_q[q++]=box_q[1];
6110 path_q[q++]=box_q[2];
6111 }
6112 center=polygon_primitive[n].point;
6113 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
6114 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
6115 if (theta.p < theta.q)
6116 theta.p+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006117 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
cristy3ed852e2009-09-05 21:47:34 +00006118 (2.0*sqrt((double) (1.0/mid)))));
6119 path_p[p++]=box_p[1];
cristybb503372010-05-27 20:51:26 +00006120 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006121 {
6122 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6123 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
6124 (theta.p+delta_theta),DegreesToRadians(360.0))));
6125 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
6126 (theta.p+delta_theta),DegreesToRadians(360.0))));
6127 p++;
6128 }
6129 path_p[p++]=box_p[2];
6130 break;
6131 }
6132 default:
6133 break;
6134 }
6135 slope.p=slope.q;
6136 inverse_slope.p=inverse_slope.q;
6137 box_p[0]=box_p[2];
6138 box_p[1]=box_p[3];
6139 box_q[0]=box_q[2];
6140 box_q[1]=box_q[3];
6141 dx.p=dx.q;
6142 dy.p=dy.q;
6143 n=i;
6144 }
6145 path_p[p++]=box_p[1];
6146 path_q[q++]=box_q[1];
6147 /*
6148 Trace stroked polygon.
6149 */
6150 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
6151 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
6152 if (stroke_polygon != (PrimitiveInfo *) NULL)
6153 {
cristybb503372010-05-27 20:51:26 +00006154 for (i=0; i < (ssize_t) p; i++)
cristy3ed852e2009-09-05 21:47:34 +00006155 {
6156 stroke_polygon[i]=polygon_primitive[0];
6157 stroke_polygon[i].point=path_p[i];
6158 }
6159 if (closed_path != MagickFalse)
6160 {
6161 stroke_polygon[i]=polygon_primitive[0];
6162 stroke_polygon[i].point=stroke_polygon[0].point;
6163 i++;
6164 }
cristybb503372010-05-27 20:51:26 +00006165 for ( ; i < (ssize_t) (p+q+closed_path); i++)
cristy3ed852e2009-09-05 21:47:34 +00006166 {
6167 stroke_polygon[i]=polygon_primitive[0];
6168 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
6169 }
6170 if (closed_path != MagickFalse)
6171 {
6172 stroke_polygon[i]=polygon_primitive[0];
6173 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
6174 i++;
6175 }
6176 stroke_polygon[i]=polygon_primitive[0];
6177 stroke_polygon[i].point=stroke_polygon[0].point;
6178 i++;
6179 stroke_polygon[i].primitive=UndefinedPrimitive;
cristybb503372010-05-27 20:51:26 +00006180 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
cristy3ed852e2009-09-05 21:47:34 +00006181 }
6182 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
6183 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
6184 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
6185 return(stroke_polygon);
6186}