blob: c5349e3ecb1b39260e679d2fefd332c00c87a1a1 [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"
55#include "MagickCore/composite.h"
56#include "MagickCore/composite-private.h"
57#include "MagickCore/constitute.h"
58#include "MagickCore/draw.h"
59#include "MagickCore/draw-private.h"
60#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/gem.h"
64#include "MagickCore/geometry.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/paint.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/property.h"
74#include "MagickCore/resample.h"
75#include "MagickCore/resample-private.h"
76#include "MagickCore/string_.h"
77#include "MagickCore/string-private.h"
78#include "MagickCore/thread-private.h"
79#include "MagickCore/token.h"
80#include "MagickCore/transform.h"
81#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000082
83/*
84 Define declarations.
85*/
86#define BezierQuantum 200
87
88/*
89 Typedef declarations.
90*/
91typedef struct _EdgeInfo
92{
93 SegmentInfo
94 bounds;
95
96 MagickRealType
97 scanline;
98
99 PointInfo
100 *points;
101
cristybb503372010-05-27 20:51:26 +0000102 size_t
cristy3ed852e2009-09-05 21:47:34 +0000103 number_points;
104
cristybb503372010-05-27 20:51:26 +0000105 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000106 direction;
107
108 MagickBooleanType
109 ghostline;
110
cristybb503372010-05-27 20:51:26 +0000111 size_t
cristy3ed852e2009-09-05 21:47:34 +0000112 highwater;
113} EdgeInfo;
114
115typedef struct _ElementInfo
116{
117 MagickRealType
118 cx,
119 cy,
120 major,
121 minor,
122 angle;
123} ElementInfo;
124
125typedef struct _PolygonInfo
126{
127 EdgeInfo
128 *edges;
129
cristybb503372010-05-27 20:51:26 +0000130 size_t
cristy3ed852e2009-09-05 21:47:34 +0000131 number_edges;
132} PolygonInfo;
133
134typedef enum
135{
136 MoveToCode,
137 OpenCode,
138 GhostlineCode,
139 LineToCode,
140 EndCode
141} PathInfoCode;
142
143typedef struct _PathInfo
144{
145 PointInfo
146 point;
147
148 PathInfoCode
149 code;
150} PathInfo;
151
152/*
153 Forward declarations.
154*/
155static MagickBooleanType
cristy947cb4c2011-10-20 18:41:46 +0000156 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
157 ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000158
159static PrimitiveInfo
160 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
161
cristybb503372010-05-27 20:51:26 +0000162static size_t
cristy3ed852e2009-09-05 21:47:34 +0000163 TracePath(PrimitiveInfo *,const char *);
164
165static void
166 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
167 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo,
168 const MagickRealType,const MagickBooleanType,const MagickBooleanType),
cristybb503372010-05-27 20:51:26 +0000169 TraceBezier(PrimitiveInfo *,const size_t),
cristy3ed852e2009-09-05 21:47:34 +0000170 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo),
171 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
172 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
173 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
174 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo,
175 PointInfo),
cristybb503372010-05-27 20:51:26 +0000176 TraceSquareLinecap(PrimitiveInfo *,const size_t,const MagickRealType);
cristy3ed852e2009-09-05 21:47:34 +0000177
178/*
179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180% %
181% %
182% %
183% A c q u i r e D r a w I n f o %
184% %
185% %
186% %
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188%
189% AcquireDrawInfo() returns a DrawInfo structure properly initialized.
190%
191% The format of the AcquireDrawInfo method is:
192%
193% DrawInfo *AcquireDrawInfo(void)
194%
195*/
196MagickExport DrawInfo *AcquireDrawInfo(void)
197{
198 DrawInfo
199 *draw_info;
200
cristy73bd4a52010-10-05 11:24:23 +0000201 draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +0000202 if (draw_info == (DrawInfo *) NULL)
203 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
204 GetDrawInfo((ImageInfo *) NULL,draw_info);
205 return(draw_info);
206}
207
208/*
209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210% %
211% %
212% %
213% C l o n e D r a w I n f o %
214% %
215% %
216% %
217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218%
anthony36fe1752011-09-29 11:28:37 +0000219% CloneDrawInfo() makes a copy of the given draw_info structure. If NULL
220% is specified, a new draw_info structure is created initialized to
221% default values, according to the given image_info.
cristy3ed852e2009-09-05 21:47:34 +0000222%
223% The format of the CloneDrawInfo method is:
224%
225% DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
226% const DrawInfo *draw_info)
227%
228% A description of each parameter follows:
229%
230% o image_info: the image info.
231%
232% o draw_info: the draw info.
233%
234*/
235MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
236 const DrawInfo *draw_info)
237{
238 DrawInfo
239 *clone_info;
240
cristy947cb4c2011-10-20 18:41:46 +0000241 ExceptionInfo
242 *exception;
243
cristy73bd4a52010-10-05 11:24:23 +0000244 clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info));
cristy3ed852e2009-09-05 21:47:34 +0000245 if (clone_info == (DrawInfo *) NULL)
246 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
247 GetDrawInfo(image_info,clone_info);
248 if (draw_info == (DrawInfo *) NULL)
249 return(clone_info);
cristy947cb4c2011-10-20 18:41:46 +0000250 exception=AcquireExceptionInfo();
anthony42f6c202011-10-23 10:28:55 +0000251 (void) CloneString(&clone_info->primitive,draw_info->primitive);
252 (void) CloneString(&clone_info->geometry,draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000253 clone_info->viewbox=draw_info->viewbox;
254 clone_info->affine=draw_info->affine;
255 clone_info->gravity=draw_info->gravity;
256 clone_info->fill=draw_info->fill;
257 clone_info->stroke=draw_info->stroke;
258 clone_info->stroke_width=draw_info->stroke_width;
259 if (draw_info->fill_pattern != (Image *) NULL)
260 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
cristy947cb4c2011-10-20 18:41:46 +0000261 exception);
cristy3ed852e2009-09-05 21:47:34 +0000262 if (draw_info->stroke_pattern != (Image *) NULL)
263 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
cristy947cb4c2011-10-20 18:41:46 +0000264 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000265 clone_info->stroke_antialias=draw_info->stroke_antialias;
266 clone_info->text_antialias=draw_info->text_antialias;
267 clone_info->fill_rule=draw_info->fill_rule;
268 clone_info->linecap=draw_info->linecap;
269 clone_info->linejoin=draw_info->linejoin;
270 clone_info->miterlimit=draw_info->miterlimit;
271 clone_info->dash_offset=draw_info->dash_offset;
272 clone_info->decorate=draw_info->decorate;
273 clone_info->compose=draw_info->compose;
anthony42f6c202011-10-23 10:28:55 +0000274 (void) CloneString(&clone_info->text,draw_info->text);
275 (void) CloneString(&clone_info->font,draw_info->font);
276 (void) CloneString(&clone_info->metrics,draw_info->metrics);
277 (void) CloneString(&clone_info->family,draw_info->family);
cristy3ed852e2009-09-05 21:47:34 +0000278 clone_info->style=draw_info->style;
279 clone_info->stretch=draw_info->stretch;
280 clone_info->weight=draw_info->weight;
anthony42f6c202011-10-23 10:28:55 +0000281 (void) CloneString(&clone_info->encoding,draw_info->encoding);
cristy3ed852e2009-09-05 21:47:34 +0000282 clone_info->pointsize=draw_info->pointsize;
283 clone_info->kerning=draw_info->kerning;
cristyb32b90a2009-09-07 21:45:48 +0000284 clone_info->interline_spacing=draw_info->interline_spacing;
cristy3ed852e2009-09-05 21:47:34 +0000285 clone_info->interword_spacing=draw_info->interword_spacing;
cristyc9b12952010-03-28 01:12:28 +0000286 clone_info->direction=draw_info->direction;
anthony42f6c202011-10-23 10:28:55 +0000287 (void) CloneString(&clone_info->density,draw_info->density);
cristy3ed852e2009-09-05 21:47:34 +0000288 clone_info->align=draw_info->align;
289 clone_info->undercolor=draw_info->undercolor;
290 clone_info->border_color=draw_info->border_color;
anthony42f6c202011-10-23 10:28:55 +0000291 (void) CloneString(&clone_info->server_name,draw_info->server_name);
cristy3ed852e2009-09-05 21:47:34 +0000292 if (draw_info->dash_pattern != (double *) NULL)
293 {
cristybb503372010-05-27 20:51:26 +0000294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000295 x;
296
297 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
298 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
299 sizeof(*clone_info->dash_pattern));
300 if (clone_info->dash_pattern == (double *) NULL)
301 ThrowFatalException(ResourceLimitFatalError,
302 "UnableToAllocateDashPattern");
303 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
304 (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
305 }
306 clone_info->gradient=draw_info->gradient;
307 if (draw_info->gradient.stops != (StopInfo *) NULL)
308 {
cristybb503372010-05-27 20:51:26 +0000309 size_t
cristy3ed852e2009-09-05 21:47:34 +0000310 number_stops;
311
312 number_stops=clone_info->gradient.number_stops;
313 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
314 number_stops,sizeof(*clone_info->gradient.stops));
315 if (clone_info->gradient.stops == (StopInfo *) NULL)
316 ThrowFatalException(ResourceLimitFatalError,
317 "UnableToAllocateDashPattern");
318 (void) CopyMagickMemory(clone_info->gradient.stops,
319 draw_info->gradient.stops,(size_t) number_stops*
320 sizeof(*clone_info->gradient.stops));
321 }
anthony42f6c202011-10-23 10:28:55 +0000322 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
cristy3ed852e2009-09-05 21:47:34 +0000323 clone_info->bounds=draw_info->bounds;
324 clone_info->clip_units=draw_info->clip_units;
325 clone_info->render=draw_info->render;
cristy4c08aed2011-07-01 19:47:50 +0000326 clone_info->alpha=draw_info->alpha;
cristy3ed852e2009-09-05 21:47:34 +0000327 clone_info->element_reference=draw_info->element_reference;
328 clone_info->debug=IsEventLogging();
cristy947cb4c2011-10-20 18:41:46 +0000329 exception=DestroyExceptionInfo(exception);
cristy3ed852e2009-09-05 21:47:34 +0000330 return(clone_info);
331}
332
333/*
334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335% %
336% %
337% %
338+ C o n v e r t P a t h T o P o l y g o n %
339% %
340% %
341% %
342%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343%
344% ConvertPathToPolygon() converts a path to the more efficient sorted
345% rendering form.
346%
347% The format of the ConvertPathToPolygon method is:
348%
349% PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
350% const PathInfo *path_info)
351%
352% A description of each parameter follows:
353%
354% o Method ConvertPathToPolygon returns the path in a more efficient sorted
355% rendering form of type PolygonInfo.
356%
357% o draw_info: Specifies a pointer to an DrawInfo structure.
358%
359% o path_info: Specifies a pointer to an PathInfo structure.
360%
361%
362*/
363
364#if defined(__cplusplus) || defined(c_plusplus)
365extern "C" {
366#endif
367
368static int CompareEdges(const void *x,const void *y)
369{
370 register const EdgeInfo
371 *p,
372 *q;
373
374 /*
375 Compare two edges.
376 */
377 p=(const EdgeInfo *) x;
378 q=(const EdgeInfo *) y;
379 if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
380 return(1);
381 if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
382 return(-1);
383 if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
384 return(1);
385 if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
386 return(-1);
387 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
388 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
389 return(1);
390 return(-1);
391}
392
393#if defined(__cplusplus) || defined(c_plusplus)
394}
395#endif
396
397static void LogPolygonInfo(const PolygonInfo *polygon_info)
398{
399 register EdgeInfo
400 *p;
401
cristybb503372010-05-27 20:51:26 +0000402 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000403 i,
404 j;
405
406 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
407 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +0000408 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000409 {
cristye8c25f92010-06-03 00:53:06 +0000410 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:",
411 (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000412 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
413 p->direction != MagickFalse ? "down" : "up");
414 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
415 p->ghostline != MagickFalse ? "transparent" : "opaque");
416 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000417 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000418 p->bounds.x2,p->bounds.y2);
cristybb503372010-05-27 20:51:26 +0000419 for (j=0; j < (ssize_t) p->number_points; j++)
cristy14388de2011-05-15 14:57:16 +0000420 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g",
cristy3ed852e2009-09-05 21:47:34 +0000421 p->points[j].x,p->points[j].y);
422 p++;
423 }
424 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
425}
426
cristybb503372010-05-27 20:51:26 +0000427static void ReversePoints(PointInfo *points,const size_t number_points)
cristy3ed852e2009-09-05 21:47:34 +0000428{
429 PointInfo
430 point;
431
cristybb503372010-05-27 20:51:26 +0000432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000433 i;
434
cristybb503372010-05-27 20:51:26 +0000435 for (i=0; i < (ssize_t) (number_points >> 1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000436 {
437 point=points[i];
438 points[i]=points[number_points-(i+1)];
439 points[number_points-(i+1)]=point;
440 }
441}
442
443static PolygonInfo *ConvertPathToPolygon(
444 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
445{
cristycee97112010-05-28 00:44:52 +0000446 long
cristy3ed852e2009-09-05 21:47:34 +0000447 direction,
448 next_direction;
449
450 PointInfo
451 point,
452 *points;
453
454 PolygonInfo
455 *polygon_info;
456
457 SegmentInfo
458 bounds;
459
cristybb503372010-05-27 20:51:26 +0000460 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000461 i,
462 n;
463
464 MagickBooleanType
465 ghostline;
466
cristybb503372010-05-27 20:51:26 +0000467 size_t
cristy3ed852e2009-09-05 21:47:34 +0000468 edge,
469 number_edges,
470 number_points;
471
472 /*
473 Convert a path to the more efficient sorted rendering form.
474 */
cristy73bd4a52010-10-05 11:24:23 +0000475 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
cristy3ed852e2009-09-05 21:47:34 +0000476 if (polygon_info == (PolygonInfo *) NULL)
477 return((PolygonInfo *) NULL);
478 number_edges=16;
479 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
480 sizeof(*polygon_info->edges));
481 if (polygon_info->edges == (EdgeInfo *) NULL)
482 return((PolygonInfo *) NULL);
483 direction=0;
484 edge=0;
485 ghostline=MagickFalse;
486 n=0;
487 number_points=0;
488 points=(PointInfo *) NULL;
489 (void) ResetMagickMemory(&point,0,sizeof(point));
490 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
491 for (i=0; path_info[i].code != EndCode; i++)
492 {
493 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
494 (path_info[i].code == GhostlineCode))
495 {
496 /*
497 Move to.
498 */
499 if ((points != (PointInfo *) NULL) && (n >= 2))
500 {
501 if (edge == number_edges)
502 {
503 number_edges<<=1;
504 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
505 polygon_info->edges,(size_t) number_edges,
506 sizeof(*polygon_info->edges));
507 if (polygon_info->edges == (EdgeInfo *) NULL)
508 return((PolygonInfo *) NULL);
509 }
cristybb503372010-05-27 20:51:26 +0000510 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000511 polygon_info->edges[edge].scanline=(-1.0);
512 polygon_info->edges[edge].highwater=0;
513 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000514 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000515 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000516 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000517 polygon_info->edges[edge].points=points;
518 polygon_info->edges[edge].bounds=bounds;
519 polygon_info->edges[edge].bounds.y1=points[0].y;
520 polygon_info->edges[edge].bounds.y2=points[n-1].y;
521 points=(PointInfo *) NULL;
522 ghostline=MagickFalse;
523 edge++;
524 }
525 if (points == (PointInfo *) NULL)
526 {
527 number_points=16;
528 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
529 sizeof(*points));
530 if (points == (PointInfo *) NULL)
531 return((PolygonInfo *) NULL);
532 }
533 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
534 point=path_info[i].point;
535 points[0]=point;
536 bounds.x1=point.x;
537 bounds.x2=point.x;
538 direction=0;
539 n=1;
540 continue;
541 }
542 /*
543 Line to.
544 */
545 next_direction=((path_info[i].point.y > point.y) ||
546 ((path_info[i].point.y == point.y) &&
547 (path_info[i].point.x > point.x))) ? 1 : -1;
548 if ((direction != 0) && (direction != next_direction))
549 {
550 /*
551 New edge.
552 */
553 point=points[n-1];
554 if (edge == number_edges)
555 {
556 number_edges<<=1;
557 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
558 polygon_info->edges,(size_t) number_edges,
559 sizeof(*polygon_info->edges));
560 if (polygon_info->edges == (EdgeInfo *) NULL)
561 return((PolygonInfo *) NULL);
562 }
cristybb503372010-05-27 20:51:26 +0000563 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000564 polygon_info->edges[edge].scanline=(-1.0);
565 polygon_info->edges[edge].highwater=0;
566 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000567 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000568 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000569 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000570 polygon_info->edges[edge].points=points;
571 polygon_info->edges[edge].bounds=bounds;
572 polygon_info->edges[edge].bounds.y1=points[0].y;
573 polygon_info->edges[edge].bounds.y2=points[n-1].y;
574 number_points=16;
575 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
576 sizeof(*points));
577 if (points == (PointInfo *) NULL)
578 return((PolygonInfo *) NULL);
579 n=1;
580 ghostline=MagickFalse;
581 points[0]=point;
582 bounds.x1=point.x;
583 bounds.x2=point.x;
584 edge++;
585 }
586 direction=next_direction;
587 if (points == (PointInfo *) NULL)
588 continue;
cristybb503372010-05-27 20:51:26 +0000589 if (n == (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +0000590 {
591 number_points<<=1;
592 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
593 sizeof(*points));
594 if (points == (PointInfo *) NULL)
595 return((PolygonInfo *) NULL);
596 }
597 point=path_info[i].point;
598 points[n]=point;
599 if (point.x < bounds.x1)
600 bounds.x1=point.x;
601 if (point.x > bounds.x2)
602 bounds.x2=point.x;
603 n++;
604 }
605 if (points != (PointInfo *) NULL)
606 {
607 if (n < 2)
608 points=(PointInfo *) RelinquishMagickMemory(points);
609 else
610 {
611 if (edge == number_edges)
612 {
613 number_edges<<=1;
614 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
615 polygon_info->edges,(size_t) number_edges,
616 sizeof(*polygon_info->edges));
617 if (polygon_info->edges == (EdgeInfo *) NULL)
618 return((PolygonInfo *) NULL);
619 }
cristybb503372010-05-27 20:51:26 +0000620 polygon_info->edges[edge].number_points=(size_t) n;
cristy3ed852e2009-09-05 21:47:34 +0000621 polygon_info->edges[edge].scanline=(-1.0);
622 polygon_info->edges[edge].highwater=0;
623 polygon_info->edges[edge].ghostline=ghostline;
cristybb503372010-05-27 20:51:26 +0000624 polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
cristy3ed852e2009-09-05 21:47:34 +0000625 if (direction < 0)
cristybb503372010-05-27 20:51:26 +0000626 ReversePoints(points,(size_t) n);
cristy3ed852e2009-09-05 21:47:34 +0000627 polygon_info->edges[edge].points=points;
628 polygon_info->edges[edge].bounds=bounds;
629 polygon_info->edges[edge].bounds.y1=points[0].y;
630 polygon_info->edges[edge].bounds.y2=points[n-1].y;
631 ghostline=MagickFalse;
632 edge++;
633 }
634 }
635 polygon_info->number_edges=edge;
636 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
637 sizeof(*polygon_info->edges),CompareEdges);
638 if (IsEventLogging() != MagickFalse)
639 LogPolygonInfo(polygon_info);
640 return(polygon_info);
641}
642
643/*
644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645% %
646% %
647% %
648+ C o n v e r t P r i m i t i v e T o P a t h %
649% %
650% %
651% %
652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653%
654% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
655% path structure.
656%
657% The format of the ConvertPrimitiveToPath method is:
658%
659% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
660% const PrimitiveInfo *primitive_info)
661%
662% A description of each parameter follows:
663%
664% o Method ConvertPrimitiveToPath returns a vector path structure of type
665% PathInfo.
666%
667% o draw_info: a structure of type DrawInfo.
668%
669% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
670%
671%
672*/
673
674static void LogPathInfo(const PathInfo *path_info)
675{
676 register const PathInfo
677 *p;
678
679 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
680 for (p=path_info; p->code != EndCode; p++)
681 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +0000682 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
cristy3ed852e2009-09-05 21:47:34 +0000683 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
684 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
685 "?");
686 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
687}
688
689static PathInfo *ConvertPrimitiveToPath(
690 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
691{
cristy3ed852e2009-09-05 21:47:34 +0000692 PathInfo
693 *path_info;
694
695 PathInfoCode
696 code;
697
698 PointInfo
699 p,
700 q;
701
cristybb503372010-05-27 20:51:26 +0000702 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000703 i,
704 n;
705
cristy826a5472010-08-31 23:21:38 +0000706 ssize_t
707 coordinates,
708 start;
709
cristy3ed852e2009-09-05 21:47:34 +0000710 /*
711 Converts a PrimitiveInfo structure into a vector path structure.
712 */
713 switch (primitive_info->primitive)
714 {
715 case PointPrimitive:
716 case ColorPrimitive:
717 case MattePrimitive:
718 case TextPrimitive:
719 case ImagePrimitive:
720 return((PathInfo *) NULL);
721 default:
722 break;
723 }
724 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
725 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
726 sizeof(*path_info));
727 if (path_info == (PathInfo *) NULL)
728 return((PathInfo *) NULL);
729 coordinates=0;
730 n=0;
731 p.x=(-1.0);
732 p.y=(-1.0);
733 q.x=(-1.0);
734 q.y=(-1.0);
735 start=0;
736 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
737 {
738 code=LineToCode;
739 if (coordinates <= 0)
740 {
cristybb503372010-05-27 20:51:26 +0000741 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +0000742 p=primitive_info[i].point;
743 start=n;
744 code=MoveToCode;
745 }
746 coordinates--;
747 /*
748 Eliminate duplicate points.
749 */
750 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
751 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
752 {
753 path_info[n].code=code;
754 path_info[n].point=primitive_info[i].point;
755 q=primitive_info[i].point;
756 n++;
757 }
758 if (coordinates > 0)
759 continue;
760 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
761 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
762 continue;
763 /*
764 Mark the p point as open if it does not match the q.
765 */
766 path_info[start].code=OpenCode;
767 path_info[n].code=GhostlineCode;
768 path_info[n].point=primitive_info[i].point;
769 n++;
770 path_info[n].code=LineToCode;
771 path_info[n].point=p;
772 n++;
773 }
774 path_info[n].code=EndCode;
775 path_info[n].point.x=0.0;
776 path_info[n].point.y=0.0;
777 if (IsEventLogging() != MagickFalse)
778 LogPathInfo(path_info);
779 return(path_info);
780}
781
782/*
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784% %
785% %
786% %
787% D e s t r o y D r a w I n f o %
788% %
789% %
790% %
791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
792%
793% DestroyDrawInfo() deallocates memory associated with an DrawInfo
794% structure.
795%
796% The format of the DestroyDrawInfo method is:
797%
798% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
799%
800% A description of each parameter follows:
801%
802% o draw_info: the draw info.
803%
804*/
805MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
806{
807 if (draw_info->debug != MagickFalse)
808 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
809 assert(draw_info != (DrawInfo *) NULL);
810 assert(draw_info->signature == MagickSignature);
811 if (draw_info->primitive != (char *) NULL)
812 draw_info->primitive=DestroyString(draw_info->primitive);
813 if (draw_info->text != (char *) NULL)
814 draw_info->text=DestroyString(draw_info->text);
815 if (draw_info->geometry != (char *) NULL)
816 draw_info->geometry=DestroyString(draw_info->geometry);
cristy3ed852e2009-09-05 21:47:34 +0000817 if (draw_info->fill_pattern != (Image *) NULL)
818 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
819 if (draw_info->stroke_pattern != (Image *) NULL)
820 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
821 if (draw_info->font != (char *) NULL)
822 draw_info->font=DestroyString(draw_info->font);
823 if (draw_info->metrics != (char *) NULL)
824 draw_info->metrics=DestroyString(draw_info->metrics);
825 if (draw_info->family != (char *) NULL)
826 draw_info->family=DestroyString(draw_info->family);
827 if (draw_info->encoding != (char *) NULL)
828 draw_info->encoding=DestroyString(draw_info->encoding);
829 if (draw_info->density != (char *) NULL)
830 draw_info->density=DestroyString(draw_info->density);
831 if (draw_info->server_name != (char *) NULL)
832 draw_info->server_name=(char *)
833 RelinquishMagickMemory(draw_info->server_name);
834 if (draw_info->dash_pattern != (double *) NULL)
835 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
836 draw_info->dash_pattern);
837 if (draw_info->gradient.stops != (StopInfo *) NULL)
838 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
839 draw_info->gradient.stops);
840 if (draw_info->clip_mask != (char *) NULL)
841 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
842 draw_info->signature=(~MagickSignature);
843 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
844 return(draw_info);
845}
846
847/*
848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849% %
850% %
851% %
852+ D e s t r o y E d g e %
853% %
854% %
855% %
856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
857%
858% DestroyEdge() destroys the specified polygon edge.
859%
860% The format of the DestroyEdge method is:
861%
cristybb503372010-05-27 20:51:26 +0000862% ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge)
cristy3ed852e2009-09-05 21:47:34 +0000863%
864% A description of each parameter follows:
865%
866% o polygon_info: Specifies a pointer to an PolygonInfo structure.
867%
868% o edge: the polygon edge number to destroy.
869%
870*/
cristybb503372010-05-27 20:51:26 +0000871static size_t DestroyEdge(PolygonInfo *polygon_info,
872 const size_t edge)
cristy3ed852e2009-09-05 21:47:34 +0000873{
874 assert(edge < polygon_info->number_edges);
875 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
876 polygon_info->edges[edge].points);
877 polygon_info->number_edges--;
878 if (edge < polygon_info->number_edges)
879 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
880 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
881 return(polygon_info->number_edges);
882}
883
884/*
885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
886% %
887% %
888% %
889+ D e s t r o y P o l y g o n I n f o %
890% %
891% %
892% %
893%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894%
895% DestroyPolygonInfo() destroys the PolygonInfo data structure.
896%
897% The format of the DestroyPolygonInfo method is:
898%
899% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
900%
901% A description of each parameter follows:
902%
903% o polygon_info: Specifies a pointer to an PolygonInfo structure.
904%
905*/
906static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
907{
cristybb503372010-05-27 20:51:26 +0000908 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000909 i;
910
cristybb503372010-05-27 20:51:26 +0000911 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +0000912 polygon_info->edges[i].points=(PointInfo *)
913 RelinquishMagickMemory(polygon_info->edges[i].points);
914 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
915 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
916}
917
918/*
919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920% %
921% %
922% %
923% D r a w A f f i n e I m a g e %
924% %
925% %
926% %
927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
928%
929% DrawAffineImage() composites the source over the destination image as
930% dictated by the affine transform.
931%
932% The format of the DrawAffineImage method is:
933%
934% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
cristy947cb4c2011-10-20 18:41:46 +0000935% const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000936%
937% A description of each parameter follows:
938%
939% o image: the image.
940%
941% o source: the source image.
942%
943% o affine: the affine transform.
944%
cristy947cb4c2011-10-20 18:41:46 +0000945% o exception: return any errors or warnings in this structure.
946%
cristy3ed852e2009-09-05 21:47:34 +0000947*/
cristyefa9e8a2011-11-07 01:56:51 +0000948
cristy3ed852e2009-09-05 21:47:34 +0000949static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
950 const double y,const SegmentInfo *edge)
951{
952 double
953 intercept,
954 z;
955
956 register double
957 x;
958
959 SegmentInfo
960 inverse_edge;
961
962 /*
963 Determine left and right edges.
964 */
965 inverse_edge.x1=edge->x1;
966 inverse_edge.y1=edge->y1;
967 inverse_edge.x2=edge->x2;
968 inverse_edge.y2=edge->y2;
969 z=affine->ry*y+affine->tx;
970 if (affine->sx > MagickEpsilon)
971 {
972 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000973 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000974 if (x > inverse_edge.x1)
975 inverse_edge.x1=x;
976 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000977 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000978 if (x < inverse_edge.x2)
979 inverse_edge.x2=x;
980 }
981 else
982 if (affine->sx < -MagickEpsilon)
983 {
984 intercept=(-z+(double) image->columns)/affine->sx;
cristyefa9e8a2011-11-07 01:56:51 +0000985 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000986 if (x > inverse_edge.x1)
987 inverse_edge.x1=x;
988 intercept=(-z/affine->sx);
cristyefa9e8a2011-11-07 01:56:51 +0000989 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +0000990 if (x < inverse_edge.x2)
991 inverse_edge.x2=x;
992 }
993 else
cristybb503372010-05-27 20:51:26 +0000994 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
cristy3ed852e2009-09-05 21:47:34 +0000995 {
996 inverse_edge.x2=edge->x1;
997 return(inverse_edge);
998 }
999 /*
1000 Determine top and bottom edges.
1001 */
1002 z=affine->sy*y+affine->ty;
1003 if (affine->rx > MagickEpsilon)
1004 {
1005 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001006 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001007 if (x > inverse_edge.x1)
1008 inverse_edge.x1=x;
1009 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001010 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001011 if (x < inverse_edge.x2)
1012 inverse_edge.x2=x;
1013 }
1014 else
1015 if (affine->rx < -MagickEpsilon)
1016 {
1017 intercept=(-z+(double) image->rows)/affine->rx;
cristyefa9e8a2011-11-07 01:56:51 +00001018 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001019 if (x > inverse_edge.x1)
1020 inverse_edge.x1=x;
1021 intercept=(-z/affine->rx);
cristyefa9e8a2011-11-07 01:56:51 +00001022 x=intercept;
cristy3ed852e2009-09-05 21:47:34 +00001023 if (x < inverse_edge.x2)
1024 inverse_edge.x2=x;
1025 }
1026 else
cristybb503372010-05-27 20:51:26 +00001027 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001028 {
1029 inverse_edge.x2=edge->x2;
1030 return(inverse_edge);
1031 }
1032 return(inverse_edge);
1033}
1034
1035static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1036{
1037 AffineMatrix
1038 inverse_affine;
1039
1040 double
1041 determinant;
1042
1043 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
1044 inverse_affine.sx=determinant*affine->sy;
1045 inverse_affine.rx=determinant*(-affine->rx);
1046 inverse_affine.ry=determinant*(-affine->ry);
1047 inverse_affine.sy=determinant*affine->sx;
1048 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1049 inverse_affine.ry;
1050 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1051 inverse_affine.sy;
1052 return(inverse_affine);
1053}
1054
cristybb503372010-05-27 20:51:26 +00001055static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +00001056{
1057 if (x < 0)
1058 return(-x);
1059 return(x);
1060}
1061
1062static inline double MagickMax(const double x,const double y)
1063{
1064 if (x > y)
1065 return(x);
1066 return(y);
1067}
1068
1069static inline double MagickMin(const double x,const double y)
1070{
1071 if (x < y)
1072 return(x);
1073 return(y);
1074}
1075
1076MagickExport MagickBooleanType DrawAffineImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00001077 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001078{
1079 AffineMatrix
1080 inverse_affine;
1081
cristyfa112112010-01-04 17:48:07 +00001082 CacheView
1083 *image_view,
1084 *source_view;
1085
cristy3ed852e2009-09-05 21:47:34 +00001086 MagickBooleanType
1087 status;
1088
cristy4c08aed2011-07-01 19:47:50 +00001089 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001090 zero;
1091
1092 PointInfo
1093 extent[4],
1094 min,
1095 max,
1096 point;
1097
cristybb503372010-05-27 20:51:26 +00001098 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001099 i;
1100
cristy3ed852e2009-09-05 21:47:34 +00001101 SegmentInfo
1102 edge;
1103
cristy826a5472010-08-31 23:21:38 +00001104 ssize_t
cristy564a5692012-01-20 23:56:26 +00001105 start,
1106 stop,
cristy826a5472010-08-31 23:21:38 +00001107 y;
1108
cristy3ed852e2009-09-05 21:47:34 +00001109 /*
1110 Determine bounding box.
1111 */
1112 assert(image != (Image *) NULL);
1113 assert(image->signature == MagickSignature);
1114 if (image->debug != MagickFalse)
1115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1116 assert(source != (const Image *) NULL);
1117 assert(source->signature == MagickSignature);
1118 assert(affine != (AffineMatrix *) NULL);
1119 extent[0].x=0.0;
1120 extent[0].y=0.0;
1121 extent[1].x=(double) source->columns-1.0;
1122 extent[1].y=0.0;
1123 extent[2].x=(double) source->columns-1.0;
1124 extent[2].y=(double) source->rows-1.0;
1125 extent[3].x=0.0;
1126 extent[3].y=(double) source->rows-1.0;
1127 for (i=0; i < 4; i++)
1128 {
1129 point=extent[i];
1130 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1131 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1132 }
1133 min=extent[0];
1134 max=extent[0];
1135 for (i=1; i < 4; i++)
1136 {
1137 if (min.x > extent[i].x)
1138 min.x=extent[i].x;
1139 if (min.y > extent[i].y)
1140 min.y=extent[i].y;
1141 if (max.x < extent[i].x)
1142 max.x=extent[i].x;
1143 if (max.y < extent[i].y)
1144 max.y=extent[i].y;
1145 }
1146 /*
1147 Affine transform image.
1148 */
cristy574cc262011-08-05 01:23:58 +00001149 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001150 return(MagickFalse);
1151 status=MagickTrue;
1152 edge.x1=MagickMax(min.x,0.0);
1153 edge.y1=MagickMax(min.y,0.0);
1154 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
1155 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
1156 inverse_affine=InverseAffineMatrix(affine);
cristy4c08aed2011-07-01 19:47:50 +00001157 GetPixelInfo(image,&zero);
cristy564a5692012-01-20 23:56:26 +00001158 start=(ssize_t) ceil(edge.y1-0.5);
1159 stop=(ssize_t) ceil(edge.y2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00001160 image_view=AcquireCacheView(image);
1161 source_view=AcquireCacheView(source);
cristyb5d5f722009-11-04 03:03:49 +00001162#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001163 #pragma omp parallel for schedule(static) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001164#endif
cristy564a5692012-01-20 23:56:26 +00001165 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00001166 {
cristy4c08aed2011-07-01 19:47:50 +00001167 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001168 composite,
1169 pixel;
1170
1171 PointInfo
1172 point;
1173
cristybb503372010-05-27 20:51:26 +00001174 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001175 x;
1176
cristy4c08aed2011-07-01 19:47:50 +00001177 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001178 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001179
1180 SegmentInfo
1181 inverse_edge;
1182
cristy826a5472010-08-31 23:21:38 +00001183 ssize_t
1184 x_offset;
1185
cristy3ed852e2009-09-05 21:47:34 +00001186 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1187 if (inverse_edge.x2 < inverse_edge.x1)
1188 continue;
cristy6ebe97c2010-07-03 01:17:28 +00001189 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1-
1190 0.5),y,(size_t) ((ssize_t) floor(inverse_edge.x2+0.5)-(ssize_t) floor(
cristy06609ee2010-03-17 20:21:27 +00001191 inverse_edge.x1+0.5)+1),1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001192 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001193 continue;
cristy3ed852e2009-09-05 21:47:34 +00001194 pixel=zero;
1195 composite=zero;
1196 x_offset=0;
cristybb503372010-05-27 20:51:26 +00001197 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 +00001198 {
1199 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1200 inverse_affine.tx;
1201 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1202 inverse_affine.ty;
cristy4c08aed2011-07-01 19:47:50 +00001203 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1204 point.x,point.y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00001205 GetPixelInfoPixel(image,q,&composite);
cristy4c08aed2011-07-01 19:47:50 +00001206 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1207 &composite);
cristy803640d2011-11-17 02:11:32 +00001208 SetPixelInfoPixel(image,&composite,q);
cristy3ed852e2009-09-05 21:47:34 +00001209 x_offset++;
cristyed231572011-07-14 02:18:59 +00001210 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001211 }
1212 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1213 status=MagickFalse;
1214 }
cristy3ed852e2009-09-05 21:47:34 +00001215 source_view=DestroyCacheView(source_view);
1216 image_view=DestroyCacheView(image_view);
1217 return(status);
1218}
1219
1220/*
1221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222% %
1223% %
1224% %
1225+ D r a w B o u n d i n g R e c t a n g l e s %
1226% %
1227% %
1228% %
1229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230%
1231% DrawBoundingRectangles() draws the bounding rectangles on the image. This
1232% is only useful for developers debugging the rendering algorithm.
1233%
1234% The format of the DrawBoundingRectangles method is:
1235%
1236% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001237% PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001238%
1239% A description of each parameter follows:
1240%
1241% o image: the image.
1242%
1243% o draw_info: the draw info.
1244%
1245% o polygon_info: Specifies a pointer to a PolygonInfo structure.
1246%
cristy947cb4c2011-10-20 18:41:46 +00001247% o exception: return any errors or warnings in this structure.
1248%
cristy3ed852e2009-09-05 21:47:34 +00001249*/
1250static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001251 const PolygonInfo *polygon_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001252{
1253 DrawInfo
1254 *clone_info;
1255
cristy3ed852e2009-09-05 21:47:34 +00001256 MagickRealType
1257 mid;
1258
1259 PointInfo
1260 end,
1261 resolution,
1262 start;
1263
1264 PrimitiveInfo
1265 primitive_info[6];
1266
cristybb503372010-05-27 20:51:26 +00001267 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001268 i;
1269
1270 SegmentInfo
1271 bounds;
1272
cristy826a5472010-08-31 23:21:38 +00001273 ssize_t
1274 coordinates;
1275
cristy3ed852e2009-09-05 21:47:34 +00001276 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
cristyfad60c92012-01-19 18:32:39 +00001277 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill,
cristy947cb4c2011-10-20 18:41:46 +00001278 exception);
cristy3ed852e2009-09-05 21:47:34 +00001279 resolution.x=DefaultResolution;
1280 resolution.y=DefaultResolution;
1281 if (clone_info->density != (char *) NULL)
1282 {
1283 GeometryInfo
1284 geometry_info;
1285
1286 MagickStatusType
1287 flags;
1288
1289 flags=ParseGeometry(clone_info->density,&geometry_info);
1290 resolution.x=geometry_info.rho;
1291 resolution.y=geometry_info.sigma;
1292 if ((flags & SigmaValue) == MagickFalse)
1293 resolution.y=resolution.x;
1294 }
1295 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1296 clone_info->stroke_width/2.0;
1297 bounds.x1=0.0;
1298 bounds.y1=0.0;
1299 bounds.x2=0.0;
1300 bounds.y2=0.0;
1301 if (polygon_info != (PolygonInfo *) NULL)
1302 {
1303 bounds=polygon_info->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00001304 for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001305 {
1306 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1307 bounds.x1=polygon_info->edges[i].bounds.x1;
1308 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1309 bounds.y1=polygon_info->edges[i].bounds.y1;
1310 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1311 bounds.x2=polygon_info->edges[i].bounds.x2;
1312 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1313 bounds.y2=polygon_info->edges[i].bounds.y2;
1314 }
1315 bounds.x1-=mid;
1316 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1317 image->columns ? (double) image->columns-1 : bounds.x1;
1318 bounds.y1-=mid;
1319 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1320 image->rows ? (double) image->rows-1 : bounds.y1;
1321 bounds.x2+=mid;
1322 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1323 image->columns ? (double) image->columns-1 : bounds.x2;
1324 bounds.y2+=mid;
1325 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1326 image->rows ? (double) image->rows-1 : bounds.y2;
cristybb503372010-05-27 20:51:26 +00001327 for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00001328 {
1329 if (polygon_info->edges[i].direction != 0)
cristy9950d572011-10-01 18:22:35 +00001330 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001331 exception);
cristy3ed852e2009-09-05 21:47:34 +00001332 else
cristy9950d572011-10-01 18:22:35 +00001333 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001334 exception);
cristy3ed852e2009-09-05 21:47:34 +00001335 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1336 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1337 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1338 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1339 primitive_info[0].primitive=RectanglePrimitive;
1340 TraceRectangle(primitive_info,start,end);
1341 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001342 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001343 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001344 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001345 }
1346 }
cristy9950d572011-10-01 18:22:35 +00001347 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke,
cristy947cb4c2011-10-20 18:41:46 +00001348 exception);
cristy3ed852e2009-09-05 21:47:34 +00001349 start.x=(double) (bounds.x1-mid);
1350 start.y=(double) (bounds.y1-mid);
1351 end.x=(double) (bounds.x2+mid);
1352 end.y=(double) (bounds.y2+mid);
1353 primitive_info[0].primitive=RectanglePrimitive;
1354 TraceRectangle(primitive_info,start,end);
1355 primitive_info[0].method=ReplaceMethod;
cristybb503372010-05-27 20:51:26 +00001356 coordinates=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00001357 primitive_info[coordinates].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001358 (void) DrawPrimitive(image,clone_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001359 clone_info=DestroyDrawInfo(clone_info);
1360}
1361
1362/*
1363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1364% %
1365% %
1366% %
1367% D r a w C l i p P a t h %
1368% %
1369% %
1370% %
1371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1372%
1373% DrawClipPath() draws the clip path on the image mask.
1374%
1375% The format of the DrawClipPath method is:
1376%
1377% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00001378% const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001379%
1380% A description of each parameter follows:
1381%
1382% o image: the image.
1383%
1384% o draw_info: the draw info.
1385%
1386% o name: the name of the clip path.
1387%
cristy018f07f2011-09-04 21:15:19 +00001388% o exception: return any errors or warnings in this structure.
1389%
cristy3ed852e2009-09-05 21:47:34 +00001390*/
1391MagickExport MagickBooleanType DrawClipPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00001392 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001393{
1394 char
cristy10a6c612012-01-29 21:41:05 +00001395 filename[MaxTextExtent];
1396
1397 Image
1398 *clip_mask;
cristy3ed852e2009-09-05 21:47:34 +00001399
1400 const char
1401 *value;
1402
1403 DrawInfo
1404 *clone_info;
1405
1406 MagickStatusType
1407 status;
1408
1409 assert(image != (Image *) NULL);
1410 assert(image->signature == MagickSignature);
1411 if (image->debug != MagickFalse)
1412 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1413 assert(draw_info != (const DrawInfo *) NULL);
cristy10a6c612012-01-29 21:41:05 +00001414 (void) FormatLocaleString(filename,MaxTextExtent,"%s",name);
1415 value=GetImageArtifact(image,filename);
cristy3ed852e2009-09-05 21:47:34 +00001416 if (value == (const char *) NULL)
1417 return(MagickFalse);
cristy10a6c612012-01-29 21:41:05 +00001418 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1419 if (clip_mask == (Image *) NULL)
1420 return(MagickFalse);
cristyfad60c92012-01-19 18:32:39 +00001421 (void) QueryColorCompliance("#0000",AllCompliance,
cristy10a6c612012-01-29 21:41:05 +00001422 &clip_mask->background_color,exception);
1423 clip_mask->background_color.alpha=(Quantum) TransparentAlpha;
1424 (void) SetImageBackgroundColor(clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001425 if (image->debug != MagickFalse)
1426 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1427 draw_info->clip_mask);
1428 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1429 (void) CloneString(&clone_info->primitive,value);
cristy9950d572011-10-01 18:22:35 +00001430 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
cristyea1a8aa2011-10-20 13:24:06 +00001431 exception);
cristy3ed852e2009-09-05 21:47:34 +00001432 clone_info->clip_mask=(char *) NULL;
cristy0c901882012-01-30 15:58:59 +00001433 status=NegateImage(clip_mask,MagickFalse,exception);
cristy10a6c612012-01-29 21:41:05 +00001434 (void) SetImageMask(image,clip_mask,exception);
1435 clip_mask=DestroyImage(clip_mask);
cristy3ed852e2009-09-05 21:47:34 +00001436 clone_info=DestroyDrawInfo(clone_info);
cristy10a6c612012-01-29 21:41:05 +00001437 status=DrawImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001438 if (image->debug != MagickFalse)
1439 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1440 return(status != 0 ? MagickTrue : MagickFalse);
1441}
1442
1443/*
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445% %
1446% %
1447% %
1448+ D r a w D a s h P o l y g o n %
1449% %
1450% %
1451% %
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453%
1454% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1455% image while respecting the dash offset and dash pattern attributes.
1456%
1457% The format of the DrawDashPolygon method is:
1458%
1459% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001460% const PrimitiveInfo *primitive_info,Image *image,
1461% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001462%
1463% A description of each parameter follows:
1464%
1465% o draw_info: the draw info.
1466%
1467% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1468%
1469% o image: the image.
1470%
cristy947cb4c2011-10-20 18:41:46 +00001471% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001472%
1473*/
1474static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00001475 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001476{
1477 DrawInfo
1478 *clone_info;
1479
cristy3ed852e2009-09-05 21:47:34 +00001480 MagickRealType
1481 length,
1482 maximum_length,
1483 offset,
1484 scale,
1485 total_length;
1486
1487 MagickStatusType
1488 status;
1489
1490 PrimitiveInfo
1491 *dash_polygon;
1492
cristybb503372010-05-27 20:51:26 +00001493 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001494 i;
1495
1496 register MagickRealType
1497 dx,
1498 dy;
1499
cristybb503372010-05-27 20:51:26 +00001500 size_t
cristy3ed852e2009-09-05 21:47:34 +00001501 number_vertices;
1502
cristy826a5472010-08-31 23:21:38 +00001503 ssize_t
1504 j,
1505 n;
1506
cristy3ed852e2009-09-05 21:47:34 +00001507 assert(draw_info != (const DrawInfo *) NULL);
1508 if (image->debug != MagickFalse)
1509 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
1510 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1511 clone_info->miterlimit=0;
1512 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
cristybb503372010-05-27 20:51:26 +00001513 number_vertices=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00001514 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1515 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
1516 if (dash_polygon == (PrimitiveInfo *) NULL)
1517 return(MagickFalse);
1518 dash_polygon[0]=primitive_info[0];
1519 scale=ExpandAffine(&draw_info->affine);
1520 length=scale*(draw_info->dash_pattern[0]-0.5);
1521 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
1522 j=1;
1523 for (n=0; offset > 0.0; j=0)
1524 {
1525 if (draw_info->dash_pattern[n] <= 0.0)
1526 break;
1527 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1528 if (offset > length)
1529 {
1530 offset-=length;
1531 n++;
1532 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1533 continue;
1534 }
1535 if (offset < length)
1536 {
1537 length-=offset;
1538 offset=0.0;
1539 break;
1540 }
1541 offset=0.0;
1542 n++;
1543 }
1544 status=MagickTrue;
1545 maximum_length=0.0;
1546 total_length=0.0;
cristybb503372010-05-27 20:51:26 +00001547 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00001548 {
1549 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1550 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1551 maximum_length=hypot((double) dx,dy);
1552 if (length == 0.0)
1553 {
1554 n++;
1555 if (draw_info->dash_pattern[n] == 0.0)
1556 n=0;
1557 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1558 }
1559 for (total_length=0.0; (total_length+length) < maximum_length; )
1560 {
1561 total_length+=length;
1562 if ((n & 0x01) != 0)
1563 {
1564 dash_polygon[0]=primitive_info[0];
1565 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1566 total_length/maximum_length);
1567 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1568 total_length/maximum_length);
1569 j=1;
1570 }
1571 else
1572 {
cristybb503372010-05-27 20:51:26 +00001573 if ((j+1) > (ssize_t) (2*number_vertices))
cristy3ed852e2009-09-05 21:47:34 +00001574 break;
1575 dash_polygon[j]=primitive_info[i-1];
1576 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1577 total_length/maximum_length);
1578 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1579 total_length/maximum_length);
1580 dash_polygon[j].coordinates=1;
1581 j++;
cristybb503372010-05-27 20:51:26 +00001582 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001583 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001584 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001585 }
1586 n++;
1587 if (draw_info->dash_pattern[n] == 0.0)
1588 n=0;
1589 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1590 }
1591 length-=(maximum_length-total_length);
1592 if ((n & 0x01) != 0)
1593 continue;
1594 dash_polygon[j]=primitive_info[i];
1595 dash_polygon[j].coordinates=1;
1596 j++;
1597 }
1598 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
1599 {
1600 dash_polygon[j]=primitive_info[i-1];
1601 dash_polygon[j].point.x+=MagickEpsilon;
1602 dash_polygon[j].point.y+=MagickEpsilon;
1603 dash_polygon[j].coordinates=1;
1604 j++;
cristybb503372010-05-27 20:51:26 +00001605 dash_polygon[0].coordinates=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +00001606 dash_polygon[j].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00001607 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00001608 }
1609 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1610 clone_info=DestroyDrawInfo(clone_info);
1611 if (image->debug != MagickFalse)
1612 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
1613 return(status != 0 ? MagickTrue : MagickFalse);
1614}
1615
1616/*
1617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1618% %
1619% %
1620% %
1621% D r a w I m a g e %
1622% %
1623% %
1624% %
1625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626%
1627% DrawImage() draws a graphic primitive on your image. The primitive
1628% may be represented as a string or filename. Precede the filename with an
1629% "at" sign (@) and the contents of the file are drawn on the image. You
1630% can affect how text is drawn by setting one or more members of the draw
1631% info structure.
1632%
1633% The format of the DrawImage method is:
1634%
cristy018f07f2011-09-04 21:15:19 +00001635% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1636% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001637%
1638% A description of each parameter follows:
1639%
1640% o image: the image.
1641%
1642% o draw_info: the draw info.
1643%
cristy018f07f2011-09-04 21:15:19 +00001644% o exception: return any errors or warnings in this structure.
1645%
cristy3ed852e2009-09-05 21:47:34 +00001646*/
1647
1648static inline MagickBooleanType IsPoint(const char *point)
1649{
1650 char
1651 *p;
1652
1653 double
1654 value;
1655
cristydbdd0e32011-11-04 23:29:40 +00001656 value=StringToDouble(point,&p);
cristy3ed852e2009-09-05 21:47:34 +00001657 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
1658}
1659
1660static inline void TracePoint(PrimitiveInfo *primitive_info,
1661 const PointInfo point)
1662{
1663 primitive_info->coordinates=1;
1664 primitive_info->point=point;
1665}
1666
cristy018f07f2011-09-04 21:15:19 +00001667MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
1668 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001669{
1670#define RenderImageTag "Render/Image"
1671
1672 AffineMatrix
1673 affine,
1674 current;
1675
1676 char
1677 key[2*MaxTextExtent],
1678 keyword[MaxTextExtent],
1679 geometry[MaxTextExtent],
1680 name[MaxTextExtent],
1681 pattern[MaxTextExtent],
1682 *primitive,
1683 *token;
1684
1685 const char
1686 *q;
1687
1688 DrawInfo
1689 **graphic_context;
1690
cristy3ed852e2009-09-05 21:47:34 +00001691 MagickBooleanType
1692 proceed,
1693 status;
1694
1695 MagickRealType
1696 angle,
1697 factor,
1698 primitive_extent;
1699
1700 PointInfo
1701 point;
1702
cristy101ab702011-10-13 13:06:32 +00001703 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001704 start_color;
1705
1706 PrimitiveInfo
1707 *primitive_info;
1708
1709 PrimitiveType
1710 primitive_type;
1711
1712 register const char
1713 *p;
1714
cristybb503372010-05-27 20:51:26 +00001715 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001716 i,
1717 x;
1718
1719 SegmentInfo
1720 bounds;
1721
1722 size_t
cristy826a5472010-08-31 23:21:38 +00001723 length,
cristy3ed852e2009-09-05 21:47:34 +00001724 number_points;
1725
cristy826a5472010-08-31 23:21:38 +00001726 ssize_t
1727 j,
1728 k,
1729 n;
1730
cristy3ed852e2009-09-05 21:47:34 +00001731 /*
1732 Ensure the annotation info is valid.
1733 */
1734 assert(image != (Image *) NULL);
1735 assert(image->signature == MagickSignature);
1736 if (image->debug != MagickFalse)
1737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1738 assert(draw_info != (DrawInfo *) NULL);
1739 assert(draw_info->signature == MagickSignature);
1740 if (image->debug != MagickFalse)
1741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1742 if ((draw_info->primitive == (char *) NULL) ||
1743 (*draw_info->primitive == '\0'))
1744 return(MagickFalse);
1745 if (image->debug != MagickFalse)
1746 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
1747 if (*draw_info->primitive != '@')
1748 primitive=AcquireString(draw_info->primitive);
1749 else
cristy947cb4c2011-10-20 18:41:46 +00001750 primitive=FileToString(draw_info->primitive+1,~0,exception);
cristy3ed852e2009-09-05 21:47:34 +00001751 if (primitive == (char *) NULL)
1752 return(MagickFalse);
1753 primitive_extent=(MagickRealType) strlen(primitive);
1754 (void) SetImageArtifact(image,"MVG",primitive);
1755 n=0;
1756 /*
1757 Allocate primitive info memory.
1758 */
cristy73bd4a52010-10-05 11:24:23 +00001759 graphic_context=(DrawInfo **) AcquireMagickMemory(
cristyed110712010-03-23 01:16:38 +00001760 sizeof(*graphic_context));
cristy3ed852e2009-09-05 21:47:34 +00001761 if (graphic_context == (DrawInfo **) NULL)
1762 {
1763 primitive=DestroyString(primitive);
1764 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1765 image->filename);
1766 }
1767 number_points=2047;
1768 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
1769 sizeof(*primitive_info));
1770 if (primitive_info == (PrimitiveInfo *) NULL)
1771 {
1772 primitive=DestroyString(primitive);
1773 for ( ; n >= 0; n--)
1774 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
1775 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
1776 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1777 image->filename);
1778 }
1779 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1780 graphic_context[n]->viewbox=image->page;
1781 if ((image->page.width == 0) || (image->page.height == 0))
1782 {
1783 graphic_context[n]->viewbox.width=image->columns;
1784 graphic_context[n]->viewbox.height=image->rows;
1785 }
1786 token=AcquireString(primitive);
cristy9950d572011-10-01 18:22:35 +00001787 (void) QueryColorCompliance("#000000",AllCompliance,&start_color,
cristy947cb4c2011-10-20 18:41:46 +00001788 exception);
1789 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001790 return(MagickFalse);
1791 status=MagickTrue;
1792 for (q=primitive; *q != '\0'; )
1793 {
1794 /*
1795 Interpret graphic primitive.
1796 */
1797 GetMagickToken(q,&q,keyword);
1798 if (*keyword == '\0')
1799 break;
1800 if (*keyword == '#')
1801 {
1802 /*
1803 Comment.
1804 */
1805 while ((*q != '\n') && (*q != '\0'))
1806 q++;
1807 continue;
1808 }
1809 p=q-strlen(keyword)-1;
1810 primitive_type=UndefinedPrimitive;
1811 current=graphic_context[n]->affine;
1812 GetAffineMatrix(&affine);
1813 switch (*keyword)
1814 {
1815 case ';':
1816 break;
1817 case 'a':
1818 case 'A':
1819 {
1820 if (LocaleCompare("affine",keyword) == 0)
1821 {
1822 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001823 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001824 GetMagickToken(q,&q,token);
1825 if (*token == ',')
1826 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001827 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001828 GetMagickToken(q,&q,token);
1829 if (*token == ',')
1830 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001831 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001832 GetMagickToken(q,&q,token);
1833 if (*token == ',')
1834 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00001835 affine.sy=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.tx=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.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001844 break;
1845 }
1846 if (LocaleCompare("arc",keyword) == 0)
1847 {
1848 primitive_type=ArcPrimitive;
1849 break;
1850 }
1851 status=MagickFalse;
1852 break;
1853 }
1854 case 'b':
1855 case 'B':
1856 {
1857 if (LocaleCompare("bezier",keyword) == 0)
1858 {
1859 primitive_type=BezierPrimitive;
1860 break;
1861 }
1862 if (LocaleCompare("border-color",keyword) == 0)
1863 {
1864 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00001865 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00001866 &graphic_context[n]->border_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00001867 break;
1868 }
1869 status=MagickFalse;
1870 break;
1871 }
1872 case 'c':
1873 case 'C':
1874 {
1875 if (LocaleCompare("clip-path",keyword) == 0)
1876 {
1877 /*
1878 Create clip mask.
1879 */
1880 GetMagickToken(q,&q,token);
1881 (void) CloneString(&graphic_context[n]->clip_mask,token);
1882 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00001883 graphic_context[n]->clip_mask,exception);
cristy3ed852e2009-09-05 21:47:34 +00001884 break;
1885 }
1886 if (LocaleCompare("clip-rule",keyword) == 0)
1887 {
cristybb503372010-05-27 20:51:26 +00001888 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001889 fill_rule;
1890
1891 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001892 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001893 token);
1894 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00001895 status=MagickFalse;
1896 else
1897 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00001898 break;
1899 }
1900 if (LocaleCompare("clip-units",keyword) == 0)
1901 {
cristybb503372010-05-27 20:51:26 +00001902 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001903 clip_units;
1904
1905 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001906 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001907 token);
1908 if (clip_units == -1)
1909 {
1910 status=MagickFalse;
1911 break;
1912 }
1913 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
1914 if (clip_units == ObjectBoundingBox)
1915 {
1916 GetAffineMatrix(&current);
1917 affine.sx=draw_info->bounds.x2;
1918 affine.sy=draw_info->bounds.y2;
1919 affine.tx=draw_info->bounds.x1;
1920 affine.ty=draw_info->bounds.y1;
1921 break;
1922 }
1923 break;
1924 }
1925 if (LocaleCompare("circle",keyword) == 0)
1926 {
1927 primitive_type=CirclePrimitive;
1928 break;
1929 }
1930 if (LocaleCompare("color",keyword) == 0)
1931 {
1932 primitive_type=ColorPrimitive;
1933 break;
1934 }
1935 status=MagickFalse;
1936 break;
1937 }
1938 case 'd':
1939 case 'D':
1940 {
1941 if (LocaleCompare("decorate",keyword) == 0)
1942 {
cristybb503372010-05-27 20:51:26 +00001943 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001944 decorate;
1945
1946 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00001947 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00001948 token);
1949 if (decorate == -1)
anthony2a021472011-10-08 11:29:29 +00001950 status=MagickFalse;
1951 else
1952 graphic_context[n]->decorate=(DecorationType) decorate;
cristy3ed852e2009-09-05 21:47:34 +00001953 break;
1954 }
1955 status=MagickFalse;
1956 break;
1957 }
1958 case 'e':
1959 case 'E':
1960 {
1961 if (LocaleCompare("ellipse",keyword) == 0)
1962 {
1963 primitive_type=EllipsePrimitive;
1964 break;
1965 }
1966 if (LocaleCompare("encoding",keyword) == 0)
1967 {
1968 GetMagickToken(q,&q,token);
1969 (void) CloneString(&graphic_context[n]->encoding,token);
1970 break;
1971 }
1972 status=MagickFalse;
1973 break;
1974 }
1975 case 'f':
1976 case 'F':
1977 {
1978 if (LocaleCompare("fill",keyword) == 0)
1979 {
1980 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00001981 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00001982 if (GetImageArtifact(image,pattern) != (const char *) NULL)
1983 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00001984 &graphic_context[n]->fill_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00001985 else
1986 {
cristy9950d572011-10-01 18:22:35 +00001987 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00001988 &graphic_context[n]->fill,exception);
cristy3ed852e2009-09-05 21:47:34 +00001989 if (status == MagickFalse)
1990 {
1991 ImageInfo
1992 *pattern_info;
1993
1994 pattern_info=AcquireImageInfo();
1995 (void) CopyMagickString(pattern_info->filename,token,
1996 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00001997 graphic_context[n]->fill_pattern=ReadImage(pattern_info,
1998 exception);
1999 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002000 pattern_info=DestroyImageInfo(pattern_info);
2001 }
2002 }
2003 break;
2004 }
2005 if (LocaleCompare("fill-opacity",keyword) == 0)
2006 {
2007 GetMagickToken(q,&q,token);
2008 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002009 graphic_context[n]->fill.alpha=(MagickRealType) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002010 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002011 break;
2012 }
2013 if (LocaleCompare("fill-rule",keyword) == 0)
2014 {
cristybb503372010-05-27 20:51:26 +00002015 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002016 fill_rule;
2017
2018 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002019 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00002020 token);
2021 if (fill_rule == -1)
anthony2a021472011-10-08 11:29:29 +00002022 status=MagickFalse;
2023 else
2024 graphic_context[n]->fill_rule=(FillRule) fill_rule;
cristy3ed852e2009-09-05 21:47:34 +00002025 break;
2026 }
2027 if (LocaleCompare("font",keyword) == 0)
2028 {
2029 GetMagickToken(q,&q,token);
2030 (void) CloneString(&graphic_context[n]->font,token);
2031 if (LocaleCompare("none",token) == 0)
2032 graphic_context[n]->font=(char *)
2033 RelinquishMagickMemory(graphic_context[n]->font);
2034 break;
2035 }
2036 if (LocaleCompare("font-family",keyword) == 0)
2037 {
2038 GetMagickToken(q,&q,token);
2039 (void) CloneString(&graphic_context[n]->family,token);
2040 break;
2041 }
2042 if (LocaleCompare("font-size",keyword) == 0)
2043 {
2044 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002045 graphic_context[n]->pointsize=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002046 break;
2047 }
2048 if (LocaleCompare("font-stretch",keyword) == 0)
2049 {
cristybb503372010-05-27 20:51:26 +00002050 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002051 stretch;
2052
2053 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002054 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002055 if (stretch == -1)
anthony2a021472011-10-08 11:29:29 +00002056 status=MagickFalse;
2057 else
2058 graphic_context[n]->stretch=(StretchType) stretch;
cristy3ed852e2009-09-05 21:47:34 +00002059 break;
2060 }
2061 if (LocaleCompare("font-style",keyword) == 0)
2062 {
cristybb503372010-05-27 20:51:26 +00002063 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002064 style;
2065
2066 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002067 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002068 if (style == -1)
anthony2a021472011-10-08 11:29:29 +00002069 status=MagickFalse;
2070 else
2071 graphic_context[n]->style=(StyleType) style;
cristy3ed852e2009-09-05 21:47:34 +00002072 break;
2073 }
2074 if (LocaleCompare("font-weight",keyword) == 0)
2075 {
2076 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002077 graphic_context[n]->weight=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002078 if (LocaleCompare(token,"all") == 0)
2079 graphic_context[n]->weight=0;
2080 if (LocaleCompare(token,"bold") == 0)
2081 graphic_context[n]->weight=700;
2082 if (LocaleCompare(token,"bolder") == 0)
2083 if (graphic_context[n]->weight <= 800)
2084 graphic_context[n]->weight+=100;
2085 if (LocaleCompare(token,"lighter") == 0)
2086 if (graphic_context[n]->weight >= 100)
2087 graphic_context[n]->weight-=100;
2088 if (LocaleCompare(token,"normal") == 0)
2089 graphic_context[n]->weight=400;
2090 break;
2091 }
2092 status=MagickFalse;
2093 break;
2094 }
2095 case 'g':
2096 case 'G':
2097 {
2098 if (LocaleCompare("gradient-units",keyword) == 0)
2099 {
2100 GetMagickToken(q,&q,token);
2101 break;
2102 }
2103 if (LocaleCompare("gravity",keyword) == 0)
2104 {
cristybb503372010-05-27 20:51:26 +00002105 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002106 gravity;
2107
2108 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002109 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002110 if (gravity == -1)
anthony2a021472011-10-08 11:29:29 +00002111 status=MagickFalse;
2112 else
2113 graphic_context[n]->gravity=(GravityType) gravity;
cristy3ed852e2009-09-05 21:47:34 +00002114 break;
2115 }
2116 status=MagickFalse;
2117 break;
2118 }
2119 case 'i':
2120 case 'I':
2121 {
2122 if (LocaleCompare("image",keyword) == 0)
2123 {
cristybb503372010-05-27 20:51:26 +00002124 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002125 compose;
2126
2127 primitive_type=ImagePrimitive;
2128 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002129 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002130 if (compose == -1)
anthony2a021472011-10-08 11:29:29 +00002131 status=MagickFalse;
2132 else
2133 graphic_context[n]->compose=(CompositeOperator) compose;
cristy3ed852e2009-09-05 21:47:34 +00002134 break;
2135 }
cristyb32b90a2009-09-07 21:45:48 +00002136 if (LocaleCompare("interline-spacing",keyword) == 0)
2137 {
2138 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002139 graphic_context[n]->interline_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002140 (char **) NULL);
cristyb32b90a2009-09-07 21:45:48 +00002141 break;
2142 }
cristy3ed852e2009-09-05 21:47:34 +00002143 if (LocaleCompare("interword-spacing",keyword) == 0)
2144 {
2145 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002146 graphic_context[n]->interword_spacing=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002147 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002148 break;
2149 }
2150 status=MagickFalse;
2151 break;
2152 }
2153 case 'k':
2154 case 'K':
2155 {
2156 if (LocaleCompare("kerning",keyword) == 0)
2157 {
2158 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002159 graphic_context[n]->kerning=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002160 break;
2161 }
2162 status=MagickFalse;
2163 break;
2164 }
2165 case 'l':
2166 case 'L':
2167 {
2168 if (LocaleCompare("line",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002169 primitive_type=LinePrimitive;
2170 else
2171 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002172 break;
2173 }
2174 case 'm':
2175 case 'M':
2176 {
2177 if (LocaleCompare("matte",keyword) == 0)
anthony2a021472011-10-08 11:29:29 +00002178 primitive_type=MattePrimitive;
2179 else
2180 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002181 break;
2182 }
2183 case 'o':
2184 case 'O':
2185 {
2186 if (LocaleCompare("offset",keyword) == 0)
2187 {
2188 GetMagickToken(q,&q,token);
2189 break;
2190 }
2191 if (LocaleCompare("opacity",keyword) == 0)
2192 {
2193 GetMagickToken(q,&q,token);
2194 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristy4c08aed2011-07-01 19:47:50 +00002195 graphic_context[n]->alpha=ClampToQuantum((MagickRealType)
2196 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->alpha)*
cristydbdd0e32011-11-04 23:29:40 +00002197 factor*StringToDouble(token,(char **) NULL))));
cristyda1f9c12011-10-02 21:39:49 +00002198 graphic_context[n]->fill.alpha=(double) graphic_context[n]->alpha;
2199 graphic_context[n]->stroke.alpha=(double) graphic_context[n]->alpha;
cristy3ed852e2009-09-05 21:47:34 +00002200 break;
2201 }
2202 status=MagickFalse;
2203 break;
2204 }
2205 case 'p':
2206 case 'P':
2207 {
2208 if (LocaleCompare("path",keyword) == 0)
2209 {
2210 primitive_type=PathPrimitive;
2211 break;
2212 }
2213 if (LocaleCompare("point",keyword) == 0)
2214 {
2215 primitive_type=PointPrimitive;
2216 break;
2217 }
2218 if (LocaleCompare("polyline",keyword) == 0)
2219 {
2220 primitive_type=PolylinePrimitive;
2221 break;
2222 }
2223 if (LocaleCompare("polygon",keyword) == 0)
2224 {
2225 primitive_type=PolygonPrimitive;
2226 break;
2227 }
2228 if (LocaleCompare("pop",keyword) == 0)
2229 {
2230 GetMagickToken(q,&q,token);
2231 if (LocaleCompare("clip-path",token) == 0)
2232 break;
2233 if (LocaleCompare("defs",token) == 0)
2234 break;
2235 if (LocaleCompare("gradient",token) == 0)
2236 break;
2237 if (LocaleCompare("graphic-context",token) == 0)
2238 {
2239 if (n <= 0)
2240 {
cristy947cb4c2011-10-20 18:41:46 +00002241 (void) ThrowMagickException(exception,GetMagickModule(),
2242 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002243 n=0;
2244 break;
2245 }
2246 if (graphic_context[n]->clip_mask != (char *) NULL)
2247 if (LocaleCompare(graphic_context[n]->clip_mask,
2248 graphic_context[n-1]->clip_mask) != 0)
cristye42f6582012-02-11 17:59:50 +00002249 (void) SetImageMask(image,(Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002250 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2251 n--;
2252 break;
2253 }
2254 if (LocaleCompare("pattern",token) == 0)
2255 break;
2256 status=MagickFalse;
2257 break;
2258 }
2259 if (LocaleCompare("push",keyword) == 0)
2260 {
2261 GetMagickToken(q,&q,token);
2262 if (LocaleCompare("clip-path",token) == 0)
2263 {
2264 char
2265 name[MaxTextExtent];
2266
2267 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002268 (void) FormatLocaleString(name,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002269 for (p=q; *q != '\0'; )
2270 {
2271 GetMagickToken(q,&q,token);
2272 if (LocaleCompare(token,"pop") != 0)
2273 continue;
2274 GetMagickToken(q,(const char **) NULL,token);
2275 if (LocaleCompare(token,"clip-path") != 0)
2276 continue;
2277 break;
2278 }
2279 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2280 (void) SetImageArtifact(image,name,token);
2281 GetMagickToken(q,&q,token);
2282 break;
2283 }
2284 if (LocaleCompare("gradient",token) == 0)
2285 {
2286 char
2287 key[2*MaxTextExtent],
2288 name[MaxTextExtent],
2289 type[MaxTextExtent];
2290
cristy3ed852e2009-09-05 21:47:34 +00002291 SegmentInfo
2292 segment;
2293
2294 GetMagickToken(q,&q,token);
2295 (void) CopyMagickString(name,token,MaxTextExtent);
2296 GetMagickToken(q,&q,token);
2297 (void) CopyMagickString(type,token,MaxTextExtent);
2298 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002299 segment.x1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002300 GetMagickToken(q,&q,token);
2301 if (*token == ',')
2302 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002303 segment.y1=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002304 GetMagickToken(q,&q,token);
2305 if (*token == ',')
2306 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002307 segment.x2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002308 GetMagickToken(q,&q,token);
2309 if (*token == ',')
2310 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002311 segment.y2=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002312 if (LocaleCompare(type,"radial") == 0)
2313 {
2314 GetMagickToken(q,&q,token);
2315 if (*token == ',')
2316 GetMagickToken(q,&q,token);
cristy3ed852e2009-09-05 21:47:34 +00002317 }
2318 for (p=q; *q != '\0'; )
2319 {
2320 GetMagickToken(q,&q,token);
2321 if (LocaleCompare(token,"pop") != 0)
2322 continue;
2323 GetMagickToken(q,(const char **) NULL,token);
2324 if (LocaleCompare(token,"gradient") != 0)
2325 continue;
2326 break;
2327 }
2328 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
2329 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
2330 graphic_context[n]->affine.ry*segment.y1+
2331 graphic_context[n]->affine.tx;
2332 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
2333 graphic_context[n]->affine.sy*segment.y1+
2334 graphic_context[n]->affine.ty;
2335 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
2336 graphic_context[n]->affine.ry*segment.y2+
2337 graphic_context[n]->affine.tx;
2338 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
2339 graphic_context[n]->affine.sy*segment.y2+
2340 graphic_context[n]->affine.ty;
cristyb51dff52011-05-19 16:55:47 +00002341 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002342 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002343 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2344 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002345 "%gx%g%+.15g%+.15g",
cristy3ed852e2009-09-05 21:47:34 +00002346 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
2347 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
2348 bounds.x1,bounds.y1);
2349 (void) SetImageArtifact(image,key,geometry);
2350 GetMagickToken(q,&q,token);
2351 break;
2352 }
2353 if (LocaleCompare("pattern",token) == 0)
2354 {
2355 RectangleInfo
2356 bounds;
2357
2358 GetMagickToken(q,&q,token);
2359 (void) CopyMagickString(name,token,MaxTextExtent);
2360 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002361 bounds.x=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2362 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002363 GetMagickToken(q,&q,token);
2364 if (*token == ',')
2365 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002366 bounds.y=(ssize_t) ceil(StringToDouble(token,(char **) NULL)-
2367 0.5);
cristy3ed852e2009-09-05 21:47:34 +00002368 GetMagickToken(q,&q,token);
2369 if (*token == ',')
2370 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002371 bounds.width=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002372 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002373 GetMagickToken(q,&q,token);
2374 if (*token == ',')
2375 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002376 bounds.height=(size_t) floor(StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002377 (char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002378 for (p=q; *q != '\0'; )
2379 {
2380 GetMagickToken(q,&q,token);
2381 if (LocaleCompare(token,"pop") != 0)
2382 continue;
2383 GetMagickToken(q,(const char **) NULL,token);
2384 if (LocaleCompare(token,"pattern") != 0)
2385 continue;
2386 break;
2387 }
2388 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
cristyb51dff52011-05-19 16:55:47 +00002389 (void) FormatLocaleString(key,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00002390 (void) SetImageArtifact(image,key,token);
cristyb51dff52011-05-19 16:55:47 +00002391 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name);
2392 (void) FormatLocaleString(geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002393 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002394 bounds.height,(double) bounds.x,(double) bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00002395 (void) SetImageArtifact(image,key,geometry);
2396 GetMagickToken(q,&q,token);
2397 break;
2398 }
2399 if (LocaleCompare("graphic-context",token) == 0)
2400 {
2401 n++;
2402 graphic_context=(DrawInfo **) ResizeQuantumMemory(
2403 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
2404 if (graphic_context == (DrawInfo **) NULL)
2405 {
cristy947cb4c2011-10-20 18:41:46 +00002406 (void) ThrowMagickException(exception,GetMagickModule(),
2407 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2408 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002409 break;
2410 }
2411 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
2412 graphic_context[n-1]);
2413 break;
2414 }
2415 if (LocaleCompare("defs",token) == 0)
2416 break;
2417 status=MagickFalse;
2418 break;
2419 }
2420 status=MagickFalse;
2421 break;
2422 }
2423 case 'r':
2424 case 'R':
2425 {
2426 if (LocaleCompare("rectangle",keyword) == 0)
2427 {
2428 primitive_type=RectanglePrimitive;
2429 break;
2430 }
2431 if (LocaleCompare("rotate",keyword) == 0)
2432 {
2433 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002434 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002435 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
2436 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
2437 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
2438 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
2439 break;
2440 }
2441 if (LocaleCompare("roundRectangle",keyword) == 0)
2442 {
2443 primitive_type=RoundRectanglePrimitive;
2444 break;
2445 }
2446 status=MagickFalse;
2447 break;
2448 }
2449 case 's':
2450 case 'S':
2451 {
2452 if (LocaleCompare("scale",keyword) == 0)
2453 {
2454 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002455 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002456 GetMagickToken(q,&q,token);
2457 if (*token == ',')
2458 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002459 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002460 break;
2461 }
2462 if (LocaleCompare("skewX",keyword) == 0)
2463 {
2464 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002465 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002466 affine.ry=sin(DegreesToRadians(angle));
2467 break;
2468 }
2469 if (LocaleCompare("skewY",keyword) == 0)
2470 {
2471 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002472 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002473 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
2474 break;
2475 }
2476 if (LocaleCompare("stop-color",keyword) == 0)
2477 {
cristy101ab702011-10-13 13:06:32 +00002478 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002479 stop_color;
2480
2481 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002482 (void) QueryColorCompliance(token,AllCompliance,&stop_color,
cristy947cb4c2011-10-20 18:41:46 +00002483 exception);
cristy3ed852e2009-09-05 21:47:34 +00002484 (void) GradientImage(image,LinearGradient,ReflectSpread,
cristy947cb4c2011-10-20 18:41:46 +00002485 &start_color,&stop_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00002486 start_color=stop_color;
2487 GetMagickToken(q,&q,token);
2488 break;
2489 }
2490 if (LocaleCompare("stroke",keyword) == 0)
2491 {
2492 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00002493 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token);
cristy3ed852e2009-09-05 21:47:34 +00002494 if (GetImageArtifact(image,pattern) != (const char *) NULL)
2495 (void) DrawPatternPath(image,draw_info,token,
cristy018f07f2011-09-04 21:15:19 +00002496 &graphic_context[n]->stroke_pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002497 else
2498 {
cristy9950d572011-10-01 18:22:35 +00002499 status=QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002500 &graphic_context[n]->stroke,exception);
cristy3ed852e2009-09-05 21:47:34 +00002501 if (status == MagickFalse)
2502 {
2503 ImageInfo
2504 *pattern_info;
2505
2506 pattern_info=AcquireImageInfo();
2507 (void) CopyMagickString(pattern_info->filename,token,
2508 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00002509 graphic_context[n]->stroke_pattern=ReadImage(pattern_info,
2510 exception);
2511 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002512 pattern_info=DestroyImageInfo(pattern_info);
2513 }
2514 }
2515 break;
2516 }
2517 if (LocaleCompare("stroke-antialias",keyword) == 0)
2518 {
2519 GetMagickToken(q,&q,token);
2520 graphic_context[n]->stroke_antialias=
cristyf2f27272009-12-17 14:48:46 +00002521 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002522 break;
2523 }
2524 if (LocaleCompare("stroke-dasharray",keyword) == 0)
2525 {
2526 if (graphic_context[n]->dash_pattern != (double *) NULL)
2527 graphic_context[n]->dash_pattern=(double *)
2528 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
2529 if (IsPoint(q) != MagickFalse)
2530 {
2531 const char
2532 *p;
2533
2534 p=q;
2535 GetMagickToken(p,&p,token);
2536 if (*token == ',')
2537 GetMagickToken(p,&p,token);
2538 for (x=0; IsPoint(token) != MagickFalse; x++)
2539 {
2540 GetMagickToken(p,&p,token);
2541 if (*token == ',')
2542 GetMagickToken(p,&p,token);
2543 }
2544 graphic_context[n]->dash_pattern=(double *)
2545 AcquireQuantumMemory((size_t) (2UL*x+1UL),
2546 sizeof(*graphic_context[n]->dash_pattern));
2547 if (graphic_context[n]->dash_pattern == (double *) NULL)
2548 {
cristy947cb4c2011-10-20 18:41:46 +00002549 (void) ThrowMagickException(exception,GetMagickModule(),
2550 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2551 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002552 break;
2553 }
2554 for (j=0; j < x; j++)
2555 {
2556 GetMagickToken(q,&q,token);
2557 if (*token == ',')
2558 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002559 graphic_context[n]->dash_pattern[j]=StringToDouble(token,
2560 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002561 }
2562 if ((x & 0x01) != 0)
2563 for ( ; j < (2*x); j++)
2564 graphic_context[n]->dash_pattern[j]=
2565 graphic_context[n]->dash_pattern[j-x];
2566 graphic_context[n]->dash_pattern[j]=0.0;
2567 break;
2568 }
2569 GetMagickToken(q,&q,token);
2570 break;
2571 }
2572 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
2573 {
2574 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002575 graphic_context[n]->dash_offset=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002576 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002577 break;
2578 }
2579 if (LocaleCompare("stroke-linecap",keyword) == 0)
2580 {
cristybb503372010-05-27 20:51:26 +00002581 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002582 linecap;
2583
2584 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002585 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002586 if (linecap == -1)
anthony2a021472011-10-08 11:29:29 +00002587 status=MagickFalse;
2588 else
2589 graphic_context[n]->linecap=(LineCap) linecap;
cristy3ed852e2009-09-05 21:47:34 +00002590 break;
2591 }
2592 if (LocaleCompare("stroke-linejoin",keyword) == 0)
2593 {
cristybb503372010-05-27 20:51:26 +00002594 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002595 linejoin;
2596
2597 GetMagickToken(q,&q,token);
cristy7118edf2011-10-08 13:33:25 +00002598 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
2599 token);
cristy3ed852e2009-09-05 21:47:34 +00002600 if (linejoin == -1)
anthony2a021472011-10-08 11:29:29 +00002601 status=MagickFalse;
2602 else
2603 graphic_context[n]->linejoin=(LineJoin) linejoin;
cristy3ed852e2009-09-05 21:47:34 +00002604 break;
2605 }
2606 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
2607 {
2608 GetMagickToken(q,&q,token);
cristye27293e2009-12-18 02:53:20 +00002609 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
cristy3ed852e2009-09-05 21:47:34 +00002610 break;
2611 }
2612 if (LocaleCompare("stroke-opacity",keyword) == 0)
2613 {
2614 GetMagickToken(q,&q,token);
2615 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
cristyda1f9c12011-10-02 21:39:49 +00002616 graphic_context[n]->stroke.alpha=(MagickRealType) QuantumRange*
cristydbdd0e32011-11-04 23:29:40 +00002617 factor*StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002618 break;
2619 }
2620 if (LocaleCompare("stroke-width",keyword) == 0)
2621 {
2622 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002623 graphic_context[n]->stroke_width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002624 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002625 break;
2626 }
2627 status=MagickFalse;
2628 break;
2629 }
2630 case 't':
2631 case 'T':
2632 {
2633 if (LocaleCompare("text",keyword) == 0)
2634 {
2635 primitive_type=TextPrimitive;
2636 break;
2637 }
2638 if (LocaleCompare("text-align",keyword) == 0)
2639 {
cristybb503372010-05-27 20:51:26 +00002640 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002641 align;
2642
2643 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002644 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002645 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002646 status=MagickFalse;
2647 else
2648 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002649 break;
2650 }
2651 if (LocaleCompare("text-anchor",keyword) == 0)
2652 {
cristybb503372010-05-27 20:51:26 +00002653 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002654 align;
2655
2656 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00002657 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00002658 if (align == -1)
anthony2a021472011-10-08 11:29:29 +00002659 status=MagickFalse;
2660 else
2661 graphic_context[n]->align=(AlignType) align;
cristy3ed852e2009-09-05 21:47:34 +00002662 break;
2663 }
2664 if (LocaleCompare("text-antialias",keyword) == 0)
2665 {
2666 GetMagickToken(q,&q,token);
2667 graphic_context[n]->text_antialias=
cristyf2f27272009-12-17 14:48:46 +00002668 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002669 break;
2670 }
2671 if (LocaleCompare("text-undercolor",keyword) == 0)
2672 {
2673 GetMagickToken(q,&q,token);
cristy9950d572011-10-01 18:22:35 +00002674 (void) QueryColorCompliance(token,AllCompliance,
cristy947cb4c2011-10-20 18:41:46 +00002675 &graphic_context[n]->undercolor,exception);
cristy3ed852e2009-09-05 21:47:34 +00002676 break;
2677 }
2678 if (LocaleCompare("translate",keyword) == 0)
2679 {
2680 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002681 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002682 GetMagickToken(q,&q,token);
2683 if (*token == ',')
2684 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002685 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002686 break;
2687 }
2688 status=MagickFalse;
2689 break;
2690 }
2691 case 'v':
2692 case 'V':
2693 {
2694 if (LocaleCompare("viewbox",keyword) == 0)
2695 {
2696 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002697 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token,
2698 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002699 GetMagickToken(q,&q,token);
2700 if (*token == ',')
2701 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002702 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token,
2703 (char **) NULL)-0.5);
cristy06609ee2010-03-17 20:21:27 +00002704 GetMagickToken(q,&q,token);
2705 if (*token == ',')
2706 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002707 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble(
2708 token,(char **) NULL)+0.5);
cristy06609ee2010-03-17 20:21:27 +00002709 GetMagickToken(q,&q,token);
2710 if (*token == ',')
2711 GetMagickToken(q,&q,token);
cristy9b34e302011-11-05 02:15:45 +00002712 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble(
2713 token,(char **) NULL)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002714 break;
2715 }
2716 status=MagickFalse;
2717 break;
2718 }
2719 default:
2720 {
2721 status=MagickFalse;
2722 break;
2723 }
2724 }
2725 if (status == MagickFalse)
2726 break;
2727 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
2728 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
2729 {
cristyfbd70092010-12-01 19:31:14 +00002730 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
2731 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
2732 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
2733 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
2734 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
2735 current.tx;
2736 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
2737 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002738 }
2739 if (primitive_type == UndefinedPrimitive)
2740 {
2741 if (image->debug != MagickFalse)
2742 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
2743 (int) (q-p),p);
2744 continue;
2745 }
2746 /*
2747 Parse the primitive attributes.
2748 */
2749 i=0;
2750 j=0;
2751 primitive_info[0].point.x=0.0;
2752 primitive_info[0].point.y=0.0;
2753 for (x=0; *q != '\0'; x++)
2754 {
2755 /*
2756 Define points.
2757 */
2758 if (IsPoint(q) == MagickFalse)
2759 break;
2760 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002761 point.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002762 GetMagickToken(q,&q,token);
2763 if (*token == ',')
2764 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00002765 point.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002766 GetMagickToken(q,(const char **) NULL,token);
2767 if (*token == ',')
2768 GetMagickToken(q,&q,token);
2769 primitive_info[i].primitive=primitive_type;
2770 primitive_info[i].point=point;
2771 primitive_info[i].coordinates=0;
2772 primitive_info[i].method=FloodfillMethod;
2773 i++;
cristybb503372010-05-27 20:51:26 +00002774 if (i < (ssize_t) number_points)
cristy3ed852e2009-09-05 21:47:34 +00002775 continue;
2776 number_points<<=1;
2777 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2778 (size_t) number_points,sizeof(*primitive_info));
2779 if (primitive_info == (PrimitiveInfo *) NULL)
2780 {
cristy947cb4c2011-10-20 18:41:46 +00002781 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002782 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2783 break;
2784 }
2785 }
2786 primitive_info[j].primitive=primitive_type;
cristybb503372010-05-27 20:51:26 +00002787 primitive_info[j].coordinates=(size_t) x;
cristy3ed852e2009-09-05 21:47:34 +00002788 primitive_info[j].method=FloodfillMethod;
2789 primitive_info[j].text=(char *) NULL;
2790 /*
2791 Circumscribe primitive within a circle.
2792 */
2793 bounds.x1=primitive_info[j].point.x;
2794 bounds.y1=primitive_info[j].point.y;
2795 bounds.x2=primitive_info[j].point.x;
2796 bounds.y2=primitive_info[j].point.y;
cristybb503372010-05-27 20:51:26 +00002797 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
cristy3ed852e2009-09-05 21:47:34 +00002798 {
2799 point=primitive_info[j+k].point;
2800 if (point.x < bounds.x1)
2801 bounds.x1=point.x;
2802 if (point.y < bounds.y1)
2803 bounds.y1=point.y;
2804 if (point.x > bounds.x2)
2805 bounds.x2=point.x;
2806 if (point.y > bounds.y2)
2807 bounds.y2=point.y;
2808 }
2809 /*
2810 Speculate how many points our primitive might consume.
2811 */
2812 length=primitive_info[j].coordinates;
2813 switch (primitive_type)
2814 {
2815 case RectanglePrimitive:
2816 {
2817 length*=5;
2818 break;
2819 }
2820 case RoundRectanglePrimitive:
2821 {
cristy78817ad2010-05-07 12:25:34 +00002822 length*=5+8*BezierQuantum;
cristy3ed852e2009-09-05 21:47:34 +00002823 break;
2824 }
2825 case BezierPrimitive:
2826 {
2827 if (primitive_info[j].coordinates > 107)
cristy947cb4c2011-10-20 18:41:46 +00002828 (void) ThrowMagickException(exception,GetMagickModule(),DrawError,
2829 "TooManyBezierCoordinates","`%s'",token);
cristy3ed852e2009-09-05 21:47:34 +00002830 length=BezierQuantum*primitive_info[j].coordinates;
2831 break;
2832 }
2833 case PathPrimitive:
2834 {
2835 char
2836 *s,
2837 *t;
2838
2839 GetMagickToken(q,&q,token);
cristy40a08ad2010-02-09 02:27:44 +00002840 length=1;
cristy3ed852e2009-09-05 21:47:34 +00002841 t=token;
2842 for (s=token; *s != '\0'; s=t)
2843 {
cristy00976d82011-02-20 20:31:28 +00002844 double
2845 value;
2846
cristydbdd0e32011-11-04 23:29:40 +00002847 value=StringToDouble(s,&t);
cristy00976d82011-02-20 20:31:28 +00002848 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00002849 if (s == t)
2850 {
2851 t++;
2852 continue;
2853 }
cristyef7a6ce2011-05-14 15:01:11 +00002854 length++;
cristy3ed852e2009-09-05 21:47:34 +00002855 }
cristyd2f832c2011-06-28 12:35:24 +00002856 length=length*BezierQuantum/2;
cristy3ed852e2009-09-05 21:47:34 +00002857 break;
2858 }
2859 case CirclePrimitive:
2860 case ArcPrimitive:
2861 case EllipsePrimitive:
2862 {
2863 MagickRealType
2864 alpha,
2865 beta,
2866 radius;
2867
2868 alpha=bounds.x2-bounds.x1;
2869 beta=bounds.y2-bounds.y1;
2870 radius=hypot((double) alpha,(double) beta);
cristyf53f26f2011-05-16 00:56:05 +00002871 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
cristy3ed852e2009-09-05 21:47:34 +00002872 break;
2873 }
2874 default:
2875 break;
2876 }
cristybb503372010-05-27 20:51:26 +00002877 if ((size_t) (i+length) >= number_points)
cristy3ed852e2009-09-05 21:47:34 +00002878 {
2879 /*
2880 Resize based on speculative points required by primitive.
2881 */
cristy9ce61b92010-05-12 16:30:26 +00002882 number_points+=length+1;
cristy3ed852e2009-09-05 21:47:34 +00002883 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
2884 (size_t) number_points,sizeof(*primitive_info));
2885 if (primitive_info == (PrimitiveInfo *) NULL)
2886 {
cristy947cb4c2011-10-20 18:41:46 +00002887 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002888 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2889 image->filename);
2890 break;
2891 }
2892 }
2893 switch (primitive_type)
2894 {
2895 case PointPrimitive:
2896 default:
2897 {
2898 if (primitive_info[j].coordinates != 1)
2899 {
2900 status=MagickFalse;
2901 break;
2902 }
2903 TracePoint(primitive_info+j,primitive_info[j].point);
cristybb503372010-05-27 20:51:26 +00002904 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002905 break;
2906 }
2907 case LinePrimitive:
2908 {
2909 if (primitive_info[j].coordinates != 2)
2910 {
2911 status=MagickFalse;
2912 break;
2913 }
2914 TraceLine(primitive_info+j,primitive_info[j].point,
2915 primitive_info[j+1].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 RectanglePrimitive:
2920 {
2921 if (primitive_info[j].coordinates != 2)
2922 {
2923 status=MagickFalse;
2924 break;
2925 }
2926 TraceRectangle(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 RoundRectanglePrimitive:
2932 {
2933 if (primitive_info[j].coordinates != 3)
2934 {
2935 status=MagickFalse;
2936 break;
2937 }
2938 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
2939 primitive_info[j+1].point,primitive_info[j+2].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 ArcPrimitive:
2944 {
2945 if (primitive_info[j].coordinates != 3)
2946 {
2947 primitive_type=UndefinedPrimitive;
2948 break;
2949 }
2950 TraceArc(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 EllipsePrimitive:
2956 {
2957 if (primitive_info[j].coordinates != 3)
2958 {
2959 status=MagickFalse;
2960 break;
2961 }
2962 TraceEllipse(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 CirclePrimitive:
2968 {
2969 if (primitive_info[j].coordinates != 2)
2970 {
2971 status=MagickFalse;
2972 break;
2973 }
2974 TraceCircle(primitive_info+j,primitive_info[j].point,
2975 primitive_info[j+1].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 PolylinePrimitive:
2980 break;
2981 case PolygonPrimitive:
2982 {
2983 primitive_info[i]=primitive_info[j];
2984 primitive_info[i].coordinates=0;
2985 primitive_info[j].coordinates++;
2986 i++;
2987 break;
2988 }
2989 case BezierPrimitive:
2990 {
2991 if (primitive_info[j].coordinates < 3)
2992 {
2993 status=MagickFalse;
2994 break;
2995 }
2996 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
cristybb503372010-05-27 20:51:26 +00002997 i=(ssize_t) (j+primitive_info[j].coordinates);
cristy3ed852e2009-09-05 21:47:34 +00002998 break;
2999 }
3000 case PathPrimitive:
3001 {
cristybb503372010-05-27 20:51:26 +00003002 i=(ssize_t) (j+TracePath(primitive_info+j,token));
cristy3ed852e2009-09-05 21:47:34 +00003003 break;
3004 }
3005 case ColorPrimitive:
3006 case MattePrimitive:
3007 {
cristybb503372010-05-27 20:51:26 +00003008 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003009 method;
3010
3011 if (primitive_info[j].coordinates != 1)
3012 {
3013 status=MagickFalse;
3014 break;
3015 }
3016 GetMagickToken(q,&q,token);
cristy042ee782011-04-22 18:48:30 +00003017 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
cristy3ed852e2009-09-05 21:47:34 +00003018 if (method == -1)
anthony2a021472011-10-08 11:29:29 +00003019 status=MagickFalse;
3020 else
3021 primitive_info[j].method=(PaintMethod) method;
cristy3ed852e2009-09-05 21:47:34 +00003022 break;
3023 }
3024 case TextPrimitive:
3025 {
3026 if (primitive_info[j].coordinates != 1)
3027 {
3028 status=MagickFalse;
3029 break;
3030 }
3031 if (*token != ',')
3032 GetMagickToken(q,&q,token);
3033 primitive_info[j].text=AcquireString(token);
3034 break;
3035 }
3036 case ImagePrimitive:
3037 {
3038 if (primitive_info[j].coordinates != 2)
3039 {
3040 status=MagickFalse;
3041 break;
3042 }
3043 GetMagickToken(q,&q,token);
3044 primitive_info[j].text=AcquireString(token);
3045 break;
3046 }
3047 }
3048 if (primitive_info == (PrimitiveInfo *) NULL)
3049 break;
3050 if (image->debug != MagickFalse)
3051 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
3052 if (status == MagickFalse)
3053 break;
3054 primitive_info[i].primitive=UndefinedPrimitive;
3055 if (i == 0)
3056 continue;
3057 /*
3058 Transform points.
3059 */
3060 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
3061 {
3062 point=primitive_info[i].point;
3063 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
3064 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
3065 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
3066 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
3067 point=primitive_info[i].point;
3068 if (point.x < graphic_context[n]->bounds.x1)
3069 graphic_context[n]->bounds.x1=point.x;
3070 if (point.y < graphic_context[n]->bounds.y1)
3071 graphic_context[n]->bounds.y1=point.y;
3072 if (point.x > graphic_context[n]->bounds.x2)
3073 graphic_context[n]->bounds.x2=point.x;
3074 if (point.y > graphic_context[n]->bounds.y2)
3075 graphic_context[n]->bounds.y2=point.y;
3076 if (primitive_info[i].primitive == ImagePrimitive)
3077 break;
cristybb503372010-05-27 20:51:26 +00003078 if (i >= (ssize_t) number_points)
cristy9ce61b92010-05-12 16:30:26 +00003079 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00003080 }
cristy3ed852e2009-09-05 21:47:34 +00003081 if (graphic_context[n]->render != MagickFalse)
3082 {
3083 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
3084 (LocaleCompare(graphic_context[n]->clip_mask,
3085 graphic_context[n-1]->clip_mask) != 0))
3086 (void) DrawClipPath(image,graphic_context[n],
cristy018f07f2011-09-04 21:15:19 +00003087 graphic_context[n]->clip_mask,exception);
cristy947cb4c2011-10-20 18:41:46 +00003088 (void) DrawPrimitive(image,graphic_context[n],primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003089 }
3090 if (primitive_info->text != (char *) NULL)
3091 primitive_info->text=(char *) RelinquishMagickMemory(
3092 primitive_info->text);
3093 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
3094 primitive_extent);
3095 if (proceed == MagickFalse)
3096 break;
3097 }
3098 if (image->debug != MagickFalse)
3099 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
3100 /*
3101 Relinquish resources.
3102 */
3103 token=DestroyString(token);
3104 if (primitive_info != (PrimitiveInfo *) NULL)
3105 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
3106 primitive=DestroyString(primitive);
3107 for ( ; n >= 0; n--)
3108 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3109 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
3110 if (status == MagickFalse)
3111 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
3112 keyword);
3113 return(status);
3114}
3115
3116/*
3117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3118% %
3119% %
3120% %
3121% D r a w G r a d i e n t I m a g e %
3122% %
3123% %
3124% %
3125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3126%
3127% DrawGradientImage() draws a linear gradient on the image.
3128%
3129% The format of the DrawGradientImage method is:
3130%
3131% MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003132% const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003133%
3134% A description of each parameter follows:
3135%
3136% o image: the image.
3137%
anthony2a021472011-10-08 11:29:29 +00003138% o draw_info: the draw info.
cristy3ed852e2009-09-05 21:47:34 +00003139%
cristy947cb4c2011-10-20 18:41:46 +00003140% o exception: return any errors or warnings in this structure.
3141%
cristy3ed852e2009-09-05 21:47:34 +00003142*/
3143
3144static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
cristybb503372010-05-27 20:51:26 +00003145 const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00003146{
3147 switch (gradient->type)
3148 {
3149 case UndefinedGradient:
3150 case LinearGradient:
3151 {
3152 MagickRealType
3153 gamma,
3154 length,
3155 offset,
3156 scale;
3157
3158 PointInfo
3159 p,
3160 q;
3161
3162 const SegmentInfo
3163 *gradient_vector;
3164
3165 gradient_vector=(&gradient->gradient_vector);
3166 p.x=gradient_vector->x2-gradient_vector->x1;
3167 p.y=gradient_vector->y2-gradient_vector->y1;
3168 q.x=(double) x-gradient_vector->x1;
3169 q.y=(double) y-gradient_vector->y1;
3170 length=sqrt(q.x*q.x+q.y*q.y);
3171 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
3172 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
3173 scale=p.x*q.x+p.y*q.y;
3174 offset=gamma*scale*length;
3175 return(offset);
3176 }
3177 case RadialGradient:
3178 {
3179 MagickRealType
3180 length,
3181 offset;
3182
3183 PointInfo
3184 v;
3185
3186 v.x=(double) x-gradient->center.x;
3187 v.y=(double) y-gradient->center.y;
3188 length=sqrt(v.x*v.x+v.y*v.y);
3189 if (gradient->spread == RepeatSpread)
3190 return(length);
3191 offset=length/gradient->radius;
3192 return(offset);
3193 }
3194 }
3195 return(0.0);
3196}
3197
3198MagickExport MagickBooleanType DrawGradientImage(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003199 const DrawInfo *draw_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003200{
cristyc4c8d132010-01-07 01:58:38 +00003201 CacheView
3202 *image_view;
3203
cristy3ed852e2009-09-05 21:47:34 +00003204 const GradientInfo
3205 *gradient;
3206
3207 const SegmentInfo
3208 *gradient_vector;
3209
cristy3ed852e2009-09-05 21:47:34 +00003210 MagickBooleanType
3211 status;
3212
cristy4c08aed2011-07-01 19:47:50 +00003213 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003214 zero;
3215
3216 MagickRealType
3217 length;
3218
3219 PointInfo
3220 point;
3221
3222 RectangleInfo
3223 bounding_box;
3224
cristy826a5472010-08-31 23:21:38 +00003225 ssize_t
3226 y;
3227
cristy3ed852e2009-09-05 21:47:34 +00003228 /*
3229 Draw linear or radial gradient on image.
3230 */
3231 assert(image != (Image *) NULL);
3232 assert(image->signature == MagickSignature);
3233 if (image->debug != MagickFalse)
3234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3235 assert(draw_info != (const DrawInfo *) NULL);
3236 gradient=(&draw_info->gradient);
3237 gradient_vector=(&gradient->gradient_vector);
3238 point.x=gradient_vector->x2-gradient_vector->x1;
3239 point.y=gradient_vector->y2-gradient_vector->y1;
3240 length=sqrt(point.x*point.x+point.y*point.y);
3241 bounding_box=gradient->bounding_box;
3242 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003243 GetPixelInfo(image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00003244 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003245#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00003246 #pragma omp parallel for schedule(static) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003247#endif
cristybb503372010-05-27 20:51:26 +00003248 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
cristy3ed852e2009-09-05 21:47:34 +00003249 {
cristy4c08aed2011-07-01 19:47:50 +00003250 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003251 composite,
3252 pixel;
3253
3254 MagickRealType
3255 alpha,
3256 offset;
3257
cristy4c08aed2011-07-01 19:47:50 +00003258 register Quantum
3259 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003260
cristybb503372010-05-27 20:51:26 +00003261 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003262 i,
3263 x;
3264
cristy826a5472010-08-31 23:21:38 +00003265 ssize_t
3266 j;
3267
cristy3ed852e2009-09-05 21:47:34 +00003268 if (status == MagickFalse)
3269 continue;
3270 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003271 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003272 {
3273 status=MagickFalse;
3274 continue;
3275 }
cristy3ed852e2009-09-05 21:47:34 +00003276 pixel=zero;
3277 composite=zero;
3278 offset=GetStopColorOffset(gradient,0,y);
3279 if (gradient->type != RadialGradient)
3280 offset/=length;
cristybb503372010-05-27 20:51:26 +00003281 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
cristy3ed852e2009-09-05 21:47:34 +00003282 {
cristy803640d2011-11-17 02:11:32 +00003283 GetPixelInfoPixel(image,q,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003284 switch (gradient->spread)
3285 {
3286 case UndefinedSpread:
3287 case PadSpread:
3288 {
cristybb503372010-05-27 20:51:26 +00003289 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3290 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003291 {
3292 offset=GetStopColorOffset(gradient,x,y);
3293 if (gradient->type != RadialGradient)
3294 offset/=length;
3295 }
cristybb503372010-05-27 20:51:26 +00003296 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003297 if (offset < gradient->stops[i].offset)
3298 break;
3299 if ((offset < 0.0) || (i == 0))
3300 composite=gradient->stops[0].color;
3301 else
cristybb503372010-05-27 20:51:26 +00003302 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
cristy3ed852e2009-09-05 21:47:34 +00003303 composite=gradient->stops[gradient->number_stops-1].color;
3304 else
3305 {
3306 j=i;
3307 i--;
3308 alpha=(offset-gradient->stops[i].offset)/
3309 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003310 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003311 &gradient->stops[j].color,alpha,&composite);
3312 }
3313 break;
3314 }
3315 case ReflectSpread:
3316 {
cristybb503372010-05-27 20:51:26 +00003317 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3318 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003319 {
3320 offset=GetStopColorOffset(gradient,x,y);
3321 if (gradient->type != RadialGradient)
3322 offset/=length;
3323 }
3324 if (offset < 0.0)
3325 offset=(-offset);
cristybb503372010-05-27 20:51:26 +00003326 if ((ssize_t) fmod(offset,2.0) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003327 offset=fmod(offset,1.0);
3328 else
3329 offset=1.0-fmod(offset,1.0);
cristybb503372010-05-27 20:51:26 +00003330 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003331 if (offset < gradient->stops[i].offset)
3332 break;
3333 if (i == 0)
3334 composite=gradient->stops[0].color;
3335 else
cristybb503372010-05-27 20:51:26 +00003336 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003337 composite=gradient->stops[gradient->number_stops-1].color;
3338 else
3339 {
3340 j=i;
3341 i--;
3342 alpha=(offset-gradient->stops[i].offset)/
3343 (gradient->stops[j].offset-gradient->stops[i].offset);
cristy4c08aed2011-07-01 19:47:50 +00003344 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003345 &gradient->stops[j].color,alpha,&composite);
3346 }
3347 break;
3348 }
3349 case RepeatSpread:
3350 {
3351 MagickBooleanType
3352 antialias;
3353
3354 MagickRealType
3355 repeat;
3356
3357 antialias=MagickFalse;
3358 repeat=0.0;
cristybb503372010-05-27 20:51:26 +00003359 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
3360 (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003361 {
3362 offset=GetStopColorOffset(gradient,x,y);
3363 if (gradient->type == LinearGradient)
3364 {
3365 repeat=fmod(offset,length);
3366 if (repeat < 0.0)
3367 repeat=length-fmod(-repeat,length);
3368 else
3369 repeat=fmod(offset,length);
3370 antialias=(repeat < length) && ((repeat+1.0) > length) ?
3371 MagickTrue : MagickFalse;
3372 offset=repeat/length;
3373 }
3374 else
3375 {
3376 repeat=fmod(offset,gradient->radius);
3377 if (repeat < 0.0)
3378 repeat=gradient->radius-fmod(-repeat,gradient->radius);
3379 else
3380 repeat=fmod(offset,gradient->radius);
cristy4c08aed2011-07-01 19:47:50 +00003381 antialias=repeat+1.0 > gradient->radius ? MagickTrue :
3382 MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003383 offset=repeat/gradient->radius;
3384 }
3385 }
cristybb503372010-05-27 20:51:26 +00003386 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +00003387 if (offset < gradient->stops[i].offset)
3388 break;
3389 if (i == 0)
3390 composite=gradient->stops[0].color;
3391 else
cristybb503372010-05-27 20:51:26 +00003392 if (i == (ssize_t) gradient->number_stops)
cristy3ed852e2009-09-05 21:47:34 +00003393 composite=gradient->stops[gradient->number_stops-1].color;
3394 else
3395 {
3396 j=i;
3397 i--;
3398 alpha=(offset-gradient->stops[i].offset)/
3399 (gradient->stops[j].offset-gradient->stops[i].offset);
3400 if (antialias != MagickFalse)
3401 {
3402 if (gradient->type == LinearGradient)
3403 alpha=length-repeat;
3404 else
3405 alpha=gradient->radius-repeat;
3406 i=0;
cristybb503372010-05-27 20:51:26 +00003407 j=(ssize_t) gradient->number_stops-1L;
cristy3ed852e2009-09-05 21:47:34 +00003408 }
cristy4c08aed2011-07-01 19:47:50 +00003409 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
cristy3ed852e2009-09-05 21:47:34 +00003410 &gradient->stops[j].color,alpha,&composite);
3411 }
3412 break;
3413 }
3414 }
cristy4c08aed2011-07-01 19:47:50 +00003415 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
3416 &pixel);
cristy803640d2011-11-17 02:11:32 +00003417 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003418 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003419 }
3420 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3421 status=MagickFalse;
3422 }
3423 image_view=DestroyCacheView(image_view);
3424 return(status);
3425}
3426
3427/*
3428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3429% %
3430% %
3431% %
3432% D r a w P a t t e r n P a t h %
3433% %
3434% %
3435% %
3436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3437%
3438% DrawPatternPath() draws a pattern.
3439%
3440% The format of the DrawPatternPath method is:
3441%
3442% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
cristy018f07f2011-09-04 21:15:19 +00003443% const char *name,Image **pattern,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003444%
3445% A description of each parameter follows:
3446%
3447% o image: the image.
3448%
3449% o draw_info: the draw info.
3450%
3451% o name: the pattern name.
3452%
3453% o image: the image.
3454%
cristy018f07f2011-09-04 21:15:19 +00003455% o exception: return any errors or warnings in this structure.
3456%
cristy3ed852e2009-09-05 21:47:34 +00003457*/
3458MagickExport MagickBooleanType DrawPatternPath(Image *image,
cristy018f07f2011-09-04 21:15:19 +00003459 const DrawInfo *draw_info,const char *name,Image **pattern,
3460 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003461{
3462 char
3463 property[MaxTextExtent];
3464
3465 const char
3466 *geometry,
3467 *path;
3468
3469 DrawInfo
3470 *clone_info;
3471
3472 ImageInfo
3473 *image_info;
3474
3475 MagickBooleanType
3476 status;
3477
3478 assert(image != (Image *) NULL);
3479 assert(image->signature == MagickSignature);
3480 if (image->debug != MagickFalse)
3481 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3482 assert(draw_info != (const DrawInfo *) NULL);
3483 assert(name != (const char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00003484 (void) FormatLocaleString(property,MaxTextExtent,"%s",name);
cristy3ed852e2009-09-05 21:47:34 +00003485 path=GetImageArtifact(image,property);
3486 if (path == (const char *) NULL)
3487 return(MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00003488 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name);
cristy3ed852e2009-09-05 21:47:34 +00003489 geometry=GetImageArtifact(image,property);
3490 if (geometry == (const char *) NULL)
3491 return(MagickFalse);
3492 if ((*pattern) != (Image *) NULL)
3493 *pattern=DestroyImage(*pattern);
3494 image_info=AcquireImageInfo();
3495 image_info->size=AcquireString(geometry);
cristy947cb4c2011-10-20 18:41:46 +00003496 *pattern=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003497 image_info=DestroyImageInfo(image_info);
cristyfad60c92012-01-19 18:32:39 +00003498 (void) QueryColorCompliance("#00000000",AllCompliance,
cristyea1a8aa2011-10-20 13:24:06 +00003499 &(*pattern)->background_color,exception);
3500 (void) SetImageBackgroundColor(*pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00003501 if (image->debug != MagickFalse)
3502 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3503 "begin pattern-path %s %s",name,geometry);
3504 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
3505 clone_info->fill_pattern=NewImageList();
3506 clone_info->stroke_pattern=NewImageList();
3507 (void) CloneString(&clone_info->primitive,path);
cristy018f07f2011-09-04 21:15:19 +00003508 status=DrawImage(*pattern,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003509 clone_info=DestroyDrawInfo(clone_info);
3510 if (image->debug != MagickFalse)
3511 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
3512 return(status);
3513}
3514
3515/*
3516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3517% %
3518% %
3519% %
3520+ D r a w P o l y g o n P r i m i t i v e %
3521% %
3522% %
3523% %
3524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3525%
3526% DrawPolygonPrimitive() draws a polygon on the image.
3527%
3528% The format of the DrawPolygonPrimitive method is:
3529%
3530% MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003531% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3532% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003533%
3534% A description of each parameter follows:
3535%
3536% o image: the image.
3537%
3538% o draw_info: the draw info.
3539%
3540% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
3541%
cristy947cb4c2011-10-20 18:41:46 +00003542% o exception: return any errors or warnings in this structure.
3543%
cristy3ed852e2009-09-05 21:47:34 +00003544*/
3545
3546static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
3547{
cristybb503372010-05-27 20:51:26 +00003548 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003549 i;
3550
3551 assert(polygon_info != (PolygonInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00003552 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00003553 if (polygon_info[i] != (PolygonInfo *) NULL)
3554 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
cristyb41ee102010-10-04 16:46:15 +00003555 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
cristy3ed852e2009-09-05 21:47:34 +00003556 return(polygon_info);
3557}
3558
3559static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
3560 const PrimitiveInfo *primitive_info)
3561{
3562 PathInfo
cristyfa112112010-01-04 17:48:07 +00003563 *restrict path_info;
cristy3ed852e2009-09-05 21:47:34 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 PolygonInfo
3566 **polygon_info;
3567
cristy826a5472010-08-31 23:21:38 +00003568 register ssize_t
3569 i;
3570
cristybb503372010-05-27 20:51:26 +00003571 size_t
cristy3ed852e2009-09-05 21:47:34 +00003572 number_threads;
3573
3574 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00003575 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00003576 sizeof(*polygon_info));
3577 if (polygon_info == (PolygonInfo **) NULL)
3578 return((PolygonInfo **) NULL);
3579 (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()*
3580 sizeof(*polygon_info));
3581 path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
3582 if (path_info == (PathInfo *) NULL)
3583 return(DestroyPolygonThreadSet(polygon_info));
cristybb503372010-05-27 20:51:26 +00003584 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003585 {
3586 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
3587 if (polygon_info[i] == (PolygonInfo *) NULL)
3588 return(DestroyPolygonThreadSet(polygon_info));
3589 }
3590 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
3591 return(polygon_info);
3592}
3593
cristyb2e6b992011-12-17 16:42:29 +00003594static MagickRealType GetFillAlpha(PolygonInfo *polygon_info,
cristy3ed852e2009-09-05 21:47:34 +00003595 const MagickRealType mid,const MagickBooleanType fill,
cristy77f38fb2010-04-22 15:51:47 +00003596 const FillRule fill_rule,const double x,const double y,
cristyb2e6b992011-12-17 16:42:29 +00003597 MagickRealType *stroke_alpha)
cristy3ed852e2009-09-05 21:47:34 +00003598{
cristy3ed852e2009-09-05 21:47:34 +00003599 MagickRealType
cristyb32b90a2009-09-07 21:45:48 +00003600 alpha,
3601 beta,
cristy3ed852e2009-09-05 21:47:34 +00003602 distance,
cristyb2e6b992011-12-17 16:42:29 +00003603 subpath_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003604
3605 PointInfo
cristyb32b90a2009-09-07 21:45:48 +00003606 delta;
cristy3ed852e2009-09-05 21:47:34 +00003607
3608 register EdgeInfo
3609 *p;
3610
cristyb32b90a2009-09-07 21:45:48 +00003611 register const PointInfo
3612 *q;
3613
cristybb503372010-05-27 20:51:26 +00003614 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003615 i;
3616
cristycee97112010-05-28 00:44:52 +00003617 ssize_t
3618 j,
3619 winding_number;
3620
cristy3ed852e2009-09-05 21:47:34 +00003621 /*
3622 Compute fill & stroke opacity for this (x,y) point.
3623 */
cristyb2e6b992011-12-17 16:42:29 +00003624 *stroke_alpha=0.0;
3625 subpath_alpha=0.0;
cristy3ed852e2009-09-05 21:47:34 +00003626 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003627 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristy3ed852e2009-09-05 21:47:34 +00003628 {
cristyb32b90a2009-09-07 21:45:48 +00003629 if (y <= (p->bounds.y1-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003630 break;
cristyb32b90a2009-09-07 21:45:48 +00003631 if (y > (p->bounds.y2+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003632 {
cristybb503372010-05-27 20:51:26 +00003633 (void) DestroyEdge(polygon_info,(size_t) j);
cristy3ed852e2009-09-05 21:47:34 +00003634 continue;
3635 }
cristyb32b90a2009-09-07 21:45:48 +00003636 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5)))
cristy3ed852e2009-09-05 21:47:34 +00003637 continue;
cristybb503372010-05-27 20:51:26 +00003638 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3639 for ( ; i < (ssize_t) p->number_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00003640 {
cristyb32b90a2009-09-07 21:45:48 +00003641 if (y <= (p->points[i-1].y-mid-0.5))
cristy3ed852e2009-09-05 21:47:34 +00003642 break;
cristyb32b90a2009-09-07 21:45:48 +00003643 if (y > (p->points[i].y+mid+0.5))
cristy3ed852e2009-09-05 21:47:34 +00003644 continue;
cristyb32b90a2009-09-07 21:45:48 +00003645 if (p->scanline != y)
cristy3ed852e2009-09-05 21:47:34 +00003646 {
cristyb32b90a2009-09-07 21:45:48 +00003647 p->scanline=y;
cristybb503372010-05-27 20:51:26 +00003648 p->highwater=(size_t) i;
cristy3ed852e2009-09-05 21:47:34 +00003649 }
3650 /*
3651 Compute distance between a point and an edge.
3652 */
cristyb32b90a2009-09-07 21:45:48 +00003653 q=p->points+i-1;
3654 delta.x=(q+1)->x-q->x;
3655 delta.y=(q+1)->y-q->y;
3656 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
cristy3ed852e2009-09-05 21:47:34 +00003657 if (beta < 0.0)
3658 {
cristyb32b90a2009-09-07 21:45:48 +00003659 delta.x=x-q->x;
3660 delta.y=y-q->y;
cristy3ed852e2009-09-05 21:47:34 +00003661 distance=delta.x*delta.x+delta.y*delta.y;
3662 }
3663 else
3664 {
3665 alpha=delta.x*delta.x+delta.y*delta.y;
3666 if (beta > alpha)
3667 {
cristyb32b90a2009-09-07 21:45:48 +00003668 delta.x=x-(q+1)->x;
3669 delta.y=y-(q+1)->y;
cristy3ed852e2009-09-05 21:47:34 +00003670 distance=delta.x*delta.x+delta.y*delta.y;
3671 }
3672 else
3673 {
cristyb32b90a2009-09-07 21:45:48 +00003674 alpha=1.0/alpha;
3675 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
3676 distance=alpha*beta*beta;
cristy3ed852e2009-09-05 21:47:34 +00003677 }
3678 }
3679 /*
3680 Compute stroke & subpath opacity.
3681 */
3682 beta=0.0;
3683 if (p->ghostline == MagickFalse)
3684 {
cristyb32b90a2009-09-07 21:45:48 +00003685 alpha=mid+0.5;
cristyb2e6b992011-12-17 16:42:29 +00003686 if ((*stroke_alpha < 1.0) &&
cristy3ed852e2009-09-05 21:47:34 +00003687 (distance <= ((alpha+0.25)*(alpha+0.25))))
3688 {
3689 alpha=mid-0.5;
3690 if (distance <= ((alpha+0.25)*(alpha+0.25)))
cristyb2e6b992011-12-17 16:42:29 +00003691 *stroke_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003692 else
3693 {
3694 beta=1.0;
3695 if (distance != 1.0)
3696 beta=sqrt((double) distance);
cristyb32b90a2009-09-07 21:45:48 +00003697 alpha=beta-mid-0.5;
cristyb2e6b992011-12-17 16:42:29 +00003698 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
3699 *stroke_alpha=(alpha-0.25)*(alpha-0.25);
cristy3ed852e2009-09-05 21:47:34 +00003700 }
3701 }
3702 }
cristyb2e6b992011-12-17 16:42:29 +00003703 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
cristy3ed852e2009-09-05 21:47:34 +00003704 continue;
3705 if (distance <= 0.0)
3706 {
cristyb2e6b992011-12-17 16:42:29 +00003707 subpath_alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +00003708 continue;
3709 }
3710 if (distance > 1.0)
3711 continue;
3712 if (beta == 0.0)
3713 {
3714 beta=1.0;
3715 if (distance != 1.0)
cristyb32b90a2009-09-07 21:45:48 +00003716 beta=sqrt(distance);
cristy3ed852e2009-09-05 21:47:34 +00003717 }
3718 alpha=beta-1.0;
cristyb2e6b992011-12-17 16:42:29 +00003719 if (subpath_alpha < (alpha*alpha))
3720 subpath_alpha=alpha*alpha;
cristy3ed852e2009-09-05 21:47:34 +00003721 }
cristy3ed852e2009-09-05 21:47:34 +00003722 }
3723 /*
3724 Compute fill opacity.
3725 */
3726 if (fill == MagickFalse)
3727 return(0.0);
cristyb2e6b992011-12-17 16:42:29 +00003728 if (subpath_alpha >= 1.0)
cristy3ed852e2009-09-05 21:47:34 +00003729 return(1.0);
cristyb32b90a2009-09-07 21:45:48 +00003730 /*
3731 Determine winding number.
3732 */
3733 winding_number=0;
3734 p=polygon_info->edges;
cristybb503372010-05-27 20:51:26 +00003735 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
cristyb32b90a2009-09-07 21:45:48 +00003736 {
3737 if (y <= p->bounds.y1)
3738 break;
3739 if ((y > p->bounds.y2) || (x <= p->bounds.x1))
3740 continue;
3741 if (x > p->bounds.x2)
3742 {
3743 winding_number+=p->direction ? 1 : -1;
3744 continue;
3745 }
cristybb503372010-05-27 20:51:26 +00003746 i=(ssize_t) MagickMax((double) p->highwater,1.0);
3747 for ( ; i < (ssize_t) p->number_points; i++)
cristyb32b90a2009-09-07 21:45:48 +00003748 if (y <= p->points[i].y)
3749 break;
3750 q=p->points+i-1;
3751 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
3752 winding_number+=p->direction ? 1 : -1;
3753 }
cristy3ed852e2009-09-05 21:47:34 +00003754 if (fill_rule != NonZeroRule)
3755 {
3756 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
3757 return(1.0);
3758 }
3759 else
3760 if (MagickAbsoluteValue(winding_number) != 0)
3761 return(1.0);
cristyb2e6b992011-12-17 16:42:29 +00003762 return(subpath_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003763}
3764
3765static MagickBooleanType DrawPolygonPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00003766 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
3767 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003768{
cristyfa112112010-01-04 17:48:07 +00003769 CacheView
3770 *image_view;
3771
cristy3ed852e2009-09-05 21:47:34 +00003772 MagickBooleanType
3773 fill,
3774 status;
3775
3776 MagickRealType
3777 mid;
3778
3779 PolygonInfo
cristyfa112112010-01-04 17:48:07 +00003780 **restrict polygon_info;
cristy3ed852e2009-09-05 21:47:34 +00003781
3782 register EdgeInfo
3783 *p;
3784
cristybb503372010-05-27 20:51:26 +00003785 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003786 i;
3787
3788 SegmentInfo
3789 bounds;
3790
cristy826a5472010-08-31 23:21:38 +00003791 ssize_t
3792 start,
3793 stop,
3794 y;
3795
cristy3ed852e2009-09-05 21:47:34 +00003796 /*
3797 Compute bounding box.
3798 */
3799 assert(image != (Image *) NULL);
3800 assert(image->signature == MagickSignature);
3801 if (image->debug != MagickFalse)
3802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3803 assert(draw_info != (DrawInfo *) NULL);
3804 assert(draw_info->signature == MagickSignature);
3805 assert(primitive_info != (PrimitiveInfo *) NULL);
3806 if (primitive_info->coordinates == 0)
3807 return(MagickTrue);
3808 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
3809 if (polygon_info == (PolygonInfo **) NULL)
3810 return(MagickFalse);
3811 if (0)
cristy947cb4c2011-10-20 18:41:46 +00003812 DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
cristy3ed852e2009-09-05 21:47:34 +00003813 if (image->debug != MagickFalse)
3814 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
3815 fill=(primitive_info->method == FillToBorderMethod) ||
3816 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
3817 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
3818 bounds=polygon_info[0]->edges[0].bounds;
cristybb503372010-05-27 20:51:26 +00003819 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
cristy3ed852e2009-09-05 21:47:34 +00003820 {
3821 p=polygon_info[0]->edges+i;
3822 if (p->bounds.x1 < bounds.x1)
3823 bounds.x1=p->bounds.x1;
3824 if (p->bounds.y1 < bounds.y1)
3825 bounds.y1=p->bounds.y1;
3826 if (p->bounds.x2 > bounds.x2)
3827 bounds.x2=p->bounds.x2;
3828 if (p->bounds.y2 > bounds.y2)
3829 bounds.y2=p->bounds.y2;
3830 }
3831 bounds.x1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003832 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003833 image->columns ? (double) image->columns-1.0 : bounds.x1;
3834 bounds.y1-=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003835 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003836 image->rows ? (double) image->rows-1.0 : bounds.y1;
3837 bounds.x2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003838 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003839 image->columns ? (double) image->columns-1.0 : bounds.x2;
3840 bounds.y2+=(mid+1.0);
cristybb503372010-05-27 20:51:26 +00003841 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >=
cristy3ed852e2009-09-05 21:47:34 +00003842 image->rows ? (double) image->rows-1.0 : bounds.y2;
3843 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00003844 image_view=AcquireCacheView(image);
3845 if (primitive_info->coordinates == 1)
3846 {
3847 /*
3848 Draw point.
3849 */
cristy564a5692012-01-20 23:56:26 +00003850 start=(ssize_t) ceil(bounds.y1-0.5);
3851 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003852#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +00003853 #pragma omp parallel for schedule(static) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003854#endif
cristy564a5692012-01-20 23:56:26 +00003855 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003856 {
3857 MagickBooleanType
3858 sync;
3859
cristy101ab702011-10-13 13:06:32 +00003860 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00003861 pixel;
3862
cristybb503372010-05-27 20:51:26 +00003863 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003864 x;
3865
cristy4c08aed2011-07-01 19:47:50 +00003866 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003867 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003868
cristy564a5692012-01-20 23:56:26 +00003869 ssize_t
3870 start,
3871 stop;
3872
cristy3ed852e2009-09-05 21:47:34 +00003873 if (status == MagickFalse)
3874 continue;
cristy564a5692012-01-20 23:56:26 +00003875 start=(ssize_t) ceil(bounds.x1-0.5);
3876 stop=(ssize_t) floor(bounds.x2+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003877 x=start;
cristybb503372010-05-27 20:51:26 +00003878 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop-x+1),
cristy3ed852e2009-09-05 21:47:34 +00003879 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003880 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003881 {
3882 status=MagickFalse;
3883 continue;
3884 }
cristy101ab702011-10-13 13:06:32 +00003885 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003886 for ( ; x <= stop; x++)
3887 {
cristybb503372010-05-27 20:51:26 +00003888 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) &&
3889 (y == (ssize_t) ceil(primitive_info->point.y-0.5)))
cristy4c08aed2011-07-01 19:47:50 +00003890 {
cristy2ed42f62011-10-02 19:49:57 +00003891 (void) GetStrokeColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00003892 SetPixelInfoPixel(image,&pixel,q);
cristy4c08aed2011-07-01 19:47:50 +00003893 }
cristyed231572011-07-14 02:18:59 +00003894 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003895 }
3896 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3897 if (sync == MagickFalse)
3898 status=MagickFalse;
3899 }
3900 image_view=DestroyCacheView(image_view);
3901 polygon_info=DestroyPolygonThreadSet(polygon_info);
3902 if (image->debug != MagickFalse)
3903 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
3904 " end draw-polygon");
3905 return(status);
3906 }
3907 /*
3908 Draw polygon or line.
3909 */
3910 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00003911 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy564a5692012-01-20 23:56:26 +00003912 start=(ssize_t) ceil(bounds.y1-0.5);
3913 stop=(ssize_t) floor(bounds.y2+0.5);
cristyb5d5f722009-11-04 03:03:49 +00003914#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00003915 #pragma omp parallel for schedule(static) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003916#endif
cristy564a5692012-01-20 23:56:26 +00003917 for (y=start; y <= stop; y++)
cristy3ed852e2009-09-05 21:47:34 +00003918 {
cristy5c9e6f22010-09-17 17:31:01 +00003919 const int
3920 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003921
cristy3ed852e2009-09-05 21:47:34 +00003922 MagickRealType
cristyb2e6b992011-12-17 16:42:29 +00003923 fill_alpha,
3924 stroke_alpha;
cristy3ed852e2009-09-05 21:47:34 +00003925
cristy101ab702011-10-13 13:06:32 +00003926 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003927 fill_color,
3928 stroke_color;
3929
cristy4c08aed2011-07-01 19:47:50 +00003930 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003931 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003932
cristy826a5472010-08-31 23:21:38 +00003933 register ssize_t
3934 x;
3935
cristy564a5692012-01-20 23:56:26 +00003936 ssize_t
3937 start,
3938 stop;
3939
cristy3ed852e2009-09-05 21:47:34 +00003940 if (status == MagickFalse)
3941 continue;
cristy564a5692012-01-20 23:56:26 +00003942 start=(ssize_t) ceil(bounds.x1-0.5);
3943 stop=(ssize_t) floor(bounds.x2+0.5);
cristyb2e6b992011-12-17 16:42:29 +00003944 q=GetCacheViewAuthenticPixels(image_view,start,y,(size_t) (stop-start+1),1,
3945 exception);
cristy4c08aed2011-07-01 19:47:50 +00003946 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003947 {
3948 status=MagickFalse;
3949 continue;
3950 }
cristy3ed852e2009-09-05 21:47:34 +00003951 for (x=start; x <= stop; x++)
3952 {
3953 /*
3954 Fill and/or stroke.
3955 */
cristyb2e6b992011-12-17 16:42:29 +00003956 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
3957 (double) x,(double) y,&stroke_alpha);
cristy3ed852e2009-09-05 21:47:34 +00003958 if (draw_info->stroke_antialias == MagickFalse)
3959 {
cristyb2e6b992011-12-17 16:42:29 +00003960 fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0;
3961 stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0;
cristy3ed852e2009-09-05 21:47:34 +00003962 }
cristy2ed42f62011-10-02 19:49:57 +00003963 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003964 fill_alpha=fill_alpha*fill_color.alpha;
3965 CompositePixelOver(image,&fill_color,fill_alpha,q,(MagickRealType)
cristy4c08aed2011-07-01 19:47:50 +00003966 GetPixelAlpha(image,q),q);
cristy2ed42f62011-10-02 19:49:57 +00003967 (void) GetStrokeColor(draw_info,x,y,&stroke_color,exception);
cristyb2e6b992011-12-17 16:42:29 +00003968 stroke_alpha=stroke_alpha*stroke_color.alpha;
3969 CompositePixelOver(image,&stroke_color,stroke_alpha,q,(MagickRealType)
cristy4c08aed2011-07-01 19:47:50 +00003970 GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00003971 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003972 }
3973 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3974 status=MagickFalse;
3975 }
3976 image_view=DestroyCacheView(image_view);
3977 polygon_info=DestroyPolygonThreadSet(polygon_info);
3978 if (image->debug != MagickFalse)
3979 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
3980 return(status);
3981}
3982
3983/*
3984%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3985% %
3986% %
3987% %
3988% D r a w P r i m i t i v e %
3989% %
3990% %
3991% %
3992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3993%
3994% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
3995%
3996% The format of the DrawPrimitive method is:
3997%
3998% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00003999% PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004000%
4001% A description of each parameter follows:
4002%
4003% o image: the image.
4004%
4005% o draw_info: the draw info.
4006%
4007% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4008%
cristy947cb4c2011-10-20 18:41:46 +00004009% o exception: return any errors or warnings in this structure.
4010%
cristy3ed852e2009-09-05 21:47:34 +00004011*/
4012
4013static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
4014{
4015 const char
4016 *methods[] =
4017 {
4018 "point",
4019 "replace",
4020 "floodfill",
4021 "filltoborder",
4022 "reset",
4023 "?"
4024 };
4025
cristy3ed852e2009-09-05 21:47:34 +00004026 PointInfo
4027 p,
4028 q,
4029 point;
4030
cristybb503372010-05-27 20:51:26 +00004031 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004032 i,
4033 x;
4034
cristy826a5472010-08-31 23:21:38 +00004035 ssize_t
4036 coordinates,
4037 y;
4038
cristybb503372010-05-27 20:51:26 +00004039 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4040 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004041 switch (primitive_info->primitive)
4042 {
4043 case PointPrimitive:
4044 {
4045 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004046 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004047 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004048 return;
4049 }
4050 case ColorPrimitive:
4051 {
4052 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004053 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004054 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004055 return;
4056 }
4057 case MattePrimitive:
4058 {
4059 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004060 "MattePrimitive %.20g,%.20g %s",(double) x,(double) y,
cristyf2faecf2010-05-28 19:19:36 +00004061 methods[primitive_info->method]);
cristy3ed852e2009-09-05 21:47:34 +00004062 return;
4063 }
4064 case TextPrimitive:
4065 {
4066 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004067 "TextPrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004068 return;
4069 }
4070 case ImagePrimitive:
4071 {
4072 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004073 "ImagePrimitive %.20g,%.20g",(double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00004074 return;
4075 }
4076 default:
4077 break;
4078 }
4079 coordinates=0;
4080 p=primitive_info[0].point;
4081 q.x=(-1.0);
4082 q.y=(-1.0);
4083 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4084 {
4085 point=primitive_info[i].point;
4086 if (coordinates <= 0)
4087 {
cristybb503372010-05-27 20:51:26 +00004088 coordinates=(ssize_t) primitive_info[i].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004089 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004090 " begin open (%.20g)",(double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004091 p=point;
4092 }
4093 point=primitive_info[i].point;
4094 if ((fabs(q.x-point.x) > MagickEpsilon) ||
4095 (fabs(q.y-point.y) > MagickEpsilon))
cristy8cd5b312010-01-07 01:10:24 +00004096 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004097 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004098 else
4099 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004100 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
cristy3ed852e2009-09-05 21:47:34 +00004101 q=point;
4102 coordinates--;
4103 if (coordinates > 0)
4104 continue;
4105 if ((fabs(p.x-point.x) > MagickEpsilon) ||
4106 (fabs(p.y-point.y) > MagickEpsilon))
cristye8c25f92010-06-03 00:53:06 +00004107 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)",
4108 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004109 else
cristye8c25f92010-06-03 00:53:06 +00004110 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)",
4111 (double) coordinates);
cristy3ed852e2009-09-05 21:47:34 +00004112 }
4113}
4114
4115MagickExport MagickBooleanType DrawPrimitive(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004116 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4117 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004118{
cristyc4c8d132010-01-07 01:58:38 +00004119 CacheView
4120 *image_view;
4121
cristy3ed852e2009-09-05 21:47:34 +00004122 MagickStatusType
4123 status;
4124
cristybb503372010-05-27 20:51:26 +00004125 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004126 i,
4127 x;
4128
cristy826a5472010-08-31 23:21:38 +00004129 ssize_t
4130 y;
4131
cristy3ed852e2009-09-05 21:47:34 +00004132 if (image->debug != MagickFalse)
4133 {
4134 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4135 " begin draw-primitive");
4136 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
cristy14388de2011-05-15 14:57:16 +00004137 " affine: %g %g %g %g %g %g",draw_info->affine.sx,
cristy3ed852e2009-09-05 21:47:34 +00004138 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
4139 draw_info->affine.tx,draw_info->affine.ty);
4140 }
4141 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004142 x=(ssize_t) ceil(primitive_info->point.x-0.5);
4143 y=(ssize_t) ceil(primitive_info->point.y-0.5);
cristy3ed852e2009-09-05 21:47:34 +00004144 image_view=AcquireCacheView(image);
4145 switch (primitive_info->primitive)
4146 {
4147 case PointPrimitive:
4148 {
cristy101ab702011-10-13 13:06:32 +00004149 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004150 fill_color;
4151
cristy4c08aed2011-07-01 19:47:50 +00004152 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004153 *q;
4154
cristybb503372010-05-27 20:51:26 +00004155 if ((y < 0) || (y >= (ssize_t) image->rows))
cristyb32b90a2009-09-07 21:45:48 +00004156 break;
cristybb503372010-05-27 20:51:26 +00004157 if ((x < 0) || (x >= (ssize_t) image->columns))
cristyb32b90a2009-09-07 21:45:48 +00004158 break;
cristy3ed852e2009-09-05 21:47:34 +00004159 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004160 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004161 break;
cristy2ed42f62011-10-02 19:49:57 +00004162 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
cristy4c08aed2011-07-01 19:47:50 +00004163 CompositePixelOver(image,&fill_color,(MagickRealType) fill_color.alpha,q,
4164 (MagickRealType) GetPixelAlpha(image,q),q);
cristy3ed852e2009-09-05 21:47:34 +00004165 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4166 break;
4167 }
4168 case ColorPrimitive:
4169 {
4170 switch (primitive_info->method)
4171 {
4172 case PointMethod:
4173 default:
4174 {
cristy101ab702011-10-13 13:06:32 +00004175 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004176 pixel;
4177
4178 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004179 *q;
4180
4181 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004182 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004183 break;
cristy101ab702011-10-13 13:06:32 +00004184 GetPixelInfo(image,&pixel);
cristy2ed42f62011-10-02 19:49:57 +00004185 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004186 SetPixelInfoPixel(image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004187 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4188 break;
4189 }
4190 case ReplaceMethod:
4191 {
4192 MagickBooleanType
4193 sync;
4194
cristy101ab702011-10-13 13:06:32 +00004195 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004196 pixel,
cristy3ed852e2009-09-05 21:47:34 +00004197 target;
4198
cristy2ed42f62011-10-02 19:49:57 +00004199 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004200 virtual_pixel[CompositePixelChannel];
cristy2ed42f62011-10-02 19:49:57 +00004201
4202 (void) GetOneCacheViewVirtualPixel(image_view,x,y,virtual_pixel,
4203 exception);
cristyda1f9c12011-10-02 21:39:49 +00004204 target.red=(double) virtual_pixel[RedPixelChannel];
4205 target.green=(double) virtual_pixel[GreenPixelChannel];
4206 target.blue=(double) virtual_pixel[BluePixelChannel];
4207 target.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristybb503372010-05-27 20:51:26 +00004208 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004209 {
cristy4c08aed2011-07-01 19:47:50 +00004210 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004211 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004212
4213 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4214 exception);
cristy4c08aed2011-07-01 19:47:50 +00004215 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004216 break;
cristybb503372010-05-27 20:51:26 +00004217 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004218 {
cristy101ab702011-10-13 13:06:32 +00004219 GetPixelInfoPixel(image,q,&pixel);
4220 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004221 {
cristyed231572011-07-14 02:18:59 +00004222 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004223 continue;
4224 }
cristy2ed42f62011-10-02 19:49:57 +00004225 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004226 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004227 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004228 }
4229 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4230 if (sync == MagickFalse)
4231 break;
4232 }
4233 break;
4234 }
4235 case FloodfillMethod:
4236 case FillToBorderMethod:
4237 {
cristy4c08aed2011-07-01 19:47:50 +00004238 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004239 target;
4240
cristy3aa93752011-12-18 15:54:24 +00004241 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004242 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004243 if (primitive_info->method == FillToBorderMethod)
4244 {
4245 target.red=(MagickRealType) draw_info->border_color.red;
4246 target.green=(MagickRealType) draw_info->border_color.green;
4247 target.blue=(MagickRealType) draw_info->border_color.blue;
4248 }
cristyd42d9952011-07-08 14:21:50 +00004249 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
4250 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004251 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004252 break;
4253 }
4254 case ResetMethod:
4255 {
4256 MagickBooleanType
4257 sync;
4258
cristy101ab702011-10-13 13:06:32 +00004259 PixelInfo
cristy4c08aed2011-07-01 19:47:50 +00004260 pixel;
4261
cristy101ab702011-10-13 13:06:32 +00004262 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00004263 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004264 {
cristy4c08aed2011-07-01 19:47:50 +00004265 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004266 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004267
cristy826a5472010-08-31 23:21:38 +00004268 register ssize_t
4269 x;
4270
cristy3ed852e2009-09-05 21:47:34 +00004271 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4272 exception);
cristy4c08aed2011-07-01 19:47:50 +00004273 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004274 break;
cristybb503372010-05-27 20:51:26 +00004275 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004276 {
cristy2ed42f62011-10-02 19:49:57 +00004277 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +00004278 SetPixelInfoPixel(image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004279 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004280 }
4281 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4282 if (sync == MagickFalse)
4283 break;
4284 }
4285 break;
4286 }
4287 }
4288 break;
4289 }
4290 case MattePrimitive:
4291 {
4292 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004293 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004294 switch (primitive_info->method)
4295 {
4296 case PointMethod:
4297 default:
4298 {
cristy101ab702011-10-13 13:06:32 +00004299 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004300 pixel;
4301
cristy4c08aed2011-07-01 19:47:50 +00004302 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004303 *q;
4304
4305 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004306 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004307 break;
cristy2ed42f62011-10-02 19:49:57 +00004308 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004309 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00004310 (void) SyncCacheViewAuthenticPixels(image_view,exception);
4311 break;
4312 }
4313 case ReplaceMethod:
4314 {
4315 MagickBooleanType
4316 sync;
4317
cristy101ab702011-10-13 13:06:32 +00004318 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004319 pixel,
4320 target;
4321
cristy2ed42f62011-10-02 19:49:57 +00004322 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004323 virtual_pixel[CompositePixelChannel];
cristy2ed42f62011-10-02 19:49:57 +00004324
4325 (void) GetOneCacheViewVirtualPixel(image_view,x,y,virtual_pixel,
4326 exception);
cristyda1f9c12011-10-02 21:39:49 +00004327 target.red=(double) virtual_pixel[RedPixelChannel];
4328 target.green=(double) virtual_pixel[GreenPixelChannel];
4329 target.blue=(double) virtual_pixel[BluePixelChannel];
4330 target.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristybb503372010-05-27 20:51:26 +00004331 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004332 {
cristy4c08aed2011-07-01 19:47:50 +00004333 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004334 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004335
cristy826a5472010-08-31 23:21:38 +00004336 register ssize_t
4337 x;
4338
cristy3ed852e2009-09-05 21:47:34 +00004339 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4340 exception);
cristy4c08aed2011-07-01 19:47:50 +00004341 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004342 break;
cristybb503372010-05-27 20:51:26 +00004343 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004344 {
cristy101ab702011-10-13 13:06:32 +00004345 GetPixelInfoPixel(image,q,&pixel);
4346 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004347 {
cristyed231572011-07-14 02:18:59 +00004348 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004349 continue;
4350 }
cristy2ed42f62011-10-02 19:49:57 +00004351 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004352 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004353 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004354 }
4355 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4356 if (sync == MagickFalse)
4357 break;
4358 }
4359 break;
4360 }
4361 case FloodfillMethod:
4362 case FillToBorderMethod:
4363 {
cristybd5a96c2011-08-21 00:04:26 +00004364 ChannelType
4365 channel_mask;
4366
cristy4c08aed2011-07-01 19:47:50 +00004367 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004368 target;
4369
cristy3aa93752011-12-18 15:54:24 +00004370 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y,
cristy52010022011-10-21 18:07:37 +00004371 &target,exception);
cristy3ed852e2009-09-05 21:47:34 +00004372 if (primitive_info->method == FillToBorderMethod)
4373 {
4374 target.red=(MagickRealType) draw_info->border_color.red;
4375 target.green=(MagickRealType) draw_info->border_color.green;
4376 target.blue=(MagickRealType) draw_info->border_color.blue;
4377 }
cristybd5a96c2011-08-21 00:04:26 +00004378 channel_mask=SetPixelChannelMask(image,AlphaChannel);
cristyd42d9952011-07-08 14:21:50 +00004379 (void) FloodfillPaintImage(image,draw_info,&target,x,y,
cristy3ed852e2009-09-05 21:47:34 +00004380 primitive_info->method == FloodfillMethod ? MagickFalse :
cristy189e84c2011-08-27 18:08:53 +00004381 MagickTrue,exception);
cristybd5a96c2011-08-21 00:04:26 +00004382 (void) SetPixelChannelMask(image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004383 break;
4384 }
4385 case ResetMethod:
4386 {
4387 MagickBooleanType
4388 sync;
4389
cristy101ab702011-10-13 13:06:32 +00004390 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004391 pixel;
4392
cristybb503372010-05-27 20:51:26 +00004393 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004394 {
cristy4c08aed2011-07-01 19:47:50 +00004395 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004396 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004397
cristy826a5472010-08-31 23:21:38 +00004398 register ssize_t
4399 x;
4400
cristy3ed852e2009-09-05 21:47:34 +00004401 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4402 exception);
cristy4c08aed2011-07-01 19:47:50 +00004403 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004404 break;
cristybb503372010-05-27 20:51:26 +00004405 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004406 {
cristy2ed42f62011-10-02 19:49:57 +00004407 (void) GetFillColor(draw_info,x,y,&pixel,exception);
cristyda1f9c12011-10-02 21:39:49 +00004408 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004409 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004410 }
4411 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4412 if (sync == MagickFalse)
4413 break;
4414 }
4415 break;
4416 }
4417 }
4418 break;
4419 }
4420 case TextPrimitive:
4421 {
4422 char
4423 geometry[MaxTextExtent];
4424
4425 DrawInfo
4426 *clone_info;
4427
4428 if (primitive_info->text == (char *) NULL)
4429 break;
4430 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4431 (void) CloneString(&clone_info->text,primitive_info->text);
cristyb51dff52011-05-19 16:55:47 +00004432 (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
cristy3ed852e2009-09-05 21:47:34 +00004433 primitive_info->point.x,primitive_info->point.y);
4434 (void) CloneString(&clone_info->geometry,geometry);
cristy5cbc0162011-08-29 00:36:28 +00004435 status=AnnotateImage(image,clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004436 clone_info=DestroyDrawInfo(clone_info);
4437 break;
4438 }
4439 case ImagePrimitive:
4440 {
4441 AffineMatrix
4442 affine;
4443
4444 char
4445 composite_geometry[MaxTextExtent];
4446
4447 Image
4448 *composite_image;
4449
4450 ImageInfo
4451 *clone_info;
4452
cristy826a5472010-08-31 23:21:38 +00004453 RectangleInfo
4454 geometry;
4455
cristybb503372010-05-27 20:51:26 +00004456 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004457 x1,
4458 y1;
4459
cristy3ed852e2009-09-05 21:47:34 +00004460 if (primitive_info->text == (char *) NULL)
4461 break;
4462 clone_info=AcquireImageInfo();
4463 if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
4464 composite_image=ReadInlineImage(clone_info,primitive_info->text,
cristy947cb4c2011-10-20 18:41:46 +00004465 exception);
cristy3ed852e2009-09-05 21:47:34 +00004466 else
4467 {
4468 (void) CopyMagickString(clone_info->filename,primitive_info->text,
4469 MaxTextExtent);
cristy947cb4c2011-10-20 18:41:46 +00004470 composite_image=ReadImage(clone_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004471 }
4472 clone_info=DestroyImageInfo(clone_info);
4473 if (composite_image == (Image *) NULL)
4474 break;
4475 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
4476 NULL,(void *) NULL);
cristybb503372010-05-27 20:51:26 +00004477 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5);
4478 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5);
4479 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
4480 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
cristy3ed852e2009-09-05 21:47:34 +00004481 {
4482 char
4483 geometry[MaxTextExtent];
4484
4485 /*
4486 Resize image.
4487 */
cristyb51dff52011-05-19 16:55:47 +00004488 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g!",
cristy3ed852e2009-09-05 21:47:34 +00004489 primitive_info[1].point.x,primitive_info[1].point.y);
4490 composite_image->filter=image->filter;
cristye941a752011-10-15 01:52:48 +00004491 (void) TransformImage(&composite_image,(char *) NULL,geometry,
4492 exception);
cristy3ed852e2009-09-05 21:47:34 +00004493 }
4494 if (composite_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004495 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,
4496 exception);
cristy4c08aed2011-07-01 19:47:50 +00004497 if (draw_info->alpha != OpaqueAlpha)
cristye941a752011-10-15 01:52:48 +00004498 (void) SetImageAlpha(composite_image,draw_info->alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00004499 SetGeometry(image,&geometry);
4500 image->gravity=draw_info->gravity;
4501 geometry.x=x;
4502 geometry.y=y;
cristyb51dff52011-05-19 16:55:47 +00004503 (void) FormatLocaleString(composite_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00004504 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double)
cristye8c25f92010-06-03 00:53:06 +00004505 composite_image->rows,(double) geometry.x,(double) geometry.y);
cristy947cb4c2011-10-20 18:41:46 +00004506 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00004507 affine=draw_info->affine;
4508 affine.tx=(double) geometry.x;
4509 affine.ty=(double) geometry.y;
4510 composite_image->interpolate=image->interpolate;
cristyd5f3fc32011-04-26 14:44:36 +00004511 if (draw_info->compose == OverCompositeOp)
cristy947cb4c2011-10-20 18:41:46 +00004512 (void) DrawAffineImage(image,composite_image,&affine,exception);
cristyd5f3fc32011-04-26 14:44:36 +00004513 else
4514 (void) CompositeImage(image,draw_info->compose,composite_image,
cristye941a752011-10-15 01:52:48 +00004515 geometry.x,geometry.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00004516 composite_image=DestroyImage(composite_image);
4517 break;
4518 }
4519 default:
4520 {
4521 MagickRealType
4522 mid,
4523 scale;
4524
4525 DrawInfo
4526 *clone_info;
4527
4528 if (IsEventLogging() != MagickFalse)
4529 LogPrimitiveInfo(primitive_info);
4530 scale=ExpandAffine(&draw_info->affine);
4531 if ((draw_info->dash_pattern != (double *) NULL) &&
4532 (draw_info->dash_pattern[0] != 0.0) &&
4533 ((scale*draw_info->stroke_width) > MagickEpsilon) &&
cristy4c08aed2011-07-01 19:47:50 +00004534 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004535 {
4536 /*
4537 Draw dash polygon.
4538 */
4539 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4540 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004541 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004542 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4543 exception);
cristy3ed852e2009-09-05 21:47:34 +00004544 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004545 (void) DrawDashPolygon(draw_info,primitive_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00004546 break;
4547 }
4548 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
4549 if ((mid > 1.0) &&
cristy4c08aed2011-07-01 19:47:50 +00004550 (draw_info->stroke.alpha != (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00004551 {
4552 MagickBooleanType
4553 closed_path;
4554
4555 /*
4556 Draw strokes while respecting line cap/join attributes.
4557 */
4558 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
4559 closed_path=
4560 (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
4561 (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
4562 MagickTrue : MagickFalse;
cristybb503372010-05-27 20:51:26 +00004563 i=(ssize_t) primitive_info[0].coordinates;
cristy3ed852e2009-09-05 21:47:34 +00004564 if ((((draw_info->linecap == RoundCap) ||
4565 (closed_path != MagickFalse)) &&
4566 (draw_info->linejoin == RoundJoin)) ||
4567 (primitive_info[i].primitive != UndefinedPrimitive))
4568 {
cristy947cb4c2011-10-20 18:41:46 +00004569 (void) DrawPolygonPrimitive(image,draw_info,primitive_info,
4570 exception);
cristy3ed852e2009-09-05 21:47:34 +00004571 break;
4572 }
4573 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4574 clone_info->stroke_width=0.0;
cristy4c08aed2011-07-01 19:47:50 +00004575 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy947cb4c2011-10-20 18:41:46 +00004576 status=DrawPolygonPrimitive(image,clone_info,primitive_info,
4577 exception);
cristy3ed852e2009-09-05 21:47:34 +00004578 clone_info=DestroyDrawInfo(clone_info);
cristy947cb4c2011-10-20 18:41:46 +00004579 status|=DrawStrokePolygon(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004580 break;
4581 }
cristy947cb4c2011-10-20 18:41:46 +00004582 status=DrawPolygonPrimitive(image,draw_info,primitive_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004583 break;
4584 }
4585 }
4586 image_view=DestroyCacheView(image_view);
4587 if (image->debug != MagickFalse)
4588 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
4589 return(status != 0 ? MagickTrue : MagickFalse);
4590}
4591
4592/*
4593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4594% %
4595% %
4596% %
4597+ D r a w S t r o k e P o l y g o n %
4598% %
4599% %
4600% %
4601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4602%
4603% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
4604% the image while respecting the line cap and join attributes.
4605%
4606% The format of the DrawStrokePolygon method is:
4607%
4608% MagickBooleanType DrawStrokePolygon(Image *image,
4609% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
4610%
4611% A description of each parameter follows:
4612%
4613% o image: the image.
4614%
4615% o draw_info: the draw info.
4616%
4617% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4618%
4619%
4620*/
4621
4622static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
cristy947cb4c2011-10-20 18:41:46 +00004623 const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004624{
4625 PrimitiveInfo
4626 linecap[5];
4627
cristybb503372010-05-27 20:51:26 +00004628 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004629 i;
4630
4631 for (i=0; i < 4; i++)
4632 linecap[i]=(*primitive_info);
4633 linecap[0].coordinates=4;
4634 linecap[1].point.x+=(double) (10.0*MagickEpsilon);
4635 linecap[2].point.x+=(double) (10.0*MagickEpsilon);
4636 linecap[2].point.y+=(double) (10.0*MagickEpsilon);
4637 linecap[3].point.y+=(double) (10.0*MagickEpsilon);
4638 linecap[4].primitive=UndefinedPrimitive;
cristy947cb4c2011-10-20 18:41:46 +00004639 (void) DrawPolygonPrimitive(image,draw_info,linecap,exception);
cristy3ed852e2009-09-05 21:47:34 +00004640}
4641
4642static MagickBooleanType DrawStrokePolygon(Image *image,
cristy947cb4c2011-10-20 18:41:46 +00004643 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4644 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004645{
4646 DrawInfo
4647 *clone_info;
4648
4649 MagickBooleanType
4650 closed_path,
4651 status;
4652
4653 PrimitiveInfo
4654 *stroke_polygon;
4655
4656 register const PrimitiveInfo
4657 *p,
4658 *q;
4659
4660 /*
4661 Draw stroked polygon.
4662 */
4663 if (image->debug != MagickFalse)
4664 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4665 " begin draw-stroke-polygon");
4666 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4667 clone_info->fill=draw_info->stroke;
cristy2d2d5622012-01-19 19:11:29 +00004668 if (clone_info->fill_pattern != (Image *) NULL)
4669 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
cristy2195b752012-01-19 16:04:04 +00004670 if (clone_info->stroke_pattern != (Image *) NULL)
cristy2d2d5622012-01-19 19:11:29 +00004671 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
4672 MagickTrue,exception);
cristy4c08aed2011-07-01 19:47:50 +00004673 clone_info->stroke.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004674 clone_info->stroke_width=0.0;
4675 clone_info->fill_rule=NonZeroRule;
4676 status=MagickTrue;
4677 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
4678 {
4679 stroke_polygon=TraceStrokePolygon(draw_info,p);
cristy947cb4c2011-10-20 18:41:46 +00004680 status=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception);
cristy3ed852e2009-09-05 21:47:34 +00004681 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
4682 q=p+p->coordinates-1;
4683 closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
4684 MagickTrue : MagickFalse;
4685 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
4686 {
cristy947cb4c2011-10-20 18:41:46 +00004687 DrawRoundLinecap(image,draw_info,p,exception);
4688 DrawRoundLinecap(image,draw_info,q,exception);
cristy3ed852e2009-09-05 21:47:34 +00004689 }
4690 }
4691 clone_info=DestroyDrawInfo(clone_info);
4692 if (image->debug != MagickFalse)
4693 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4694 " end draw-stroke-polygon");
4695 return(status);
4696}
4697
4698/*
4699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4700% %
4701% %
4702% %
4703% G e t A f f i n e M a t r i x %
4704% %
4705% %
4706% %
4707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4708%
4709% GetAffineMatrix() returns an AffineMatrix initialized to the identity
4710% matrix.
4711%
4712% The format of the GetAffineMatrix method is:
4713%
4714% void GetAffineMatrix(AffineMatrix *affine_matrix)
4715%
4716% A description of each parameter follows:
4717%
4718% o affine_matrix: the affine matrix.
4719%
4720*/
4721MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
4722{
4723 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4724 assert(affine_matrix != (AffineMatrix *) NULL);
4725 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
4726 affine_matrix->sx=1.0;
4727 affine_matrix->sy=1.0;
4728}
4729
4730/*
4731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4732% %
4733% %
4734% %
4735+ G e t D r a w I n f o %
4736% %
4737% %
4738% %
4739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4740%
anthony9c88e8f2011-09-29 11:31:53 +00004741% GetDrawInfo() initializes draw_info to default values from image_info.
cristy3ed852e2009-09-05 21:47:34 +00004742%
4743% The format of the GetDrawInfo method is:
4744%
4745% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4746%
4747% A description of each parameter follows:
4748%
4749% o image_info: the image info..
4750%
4751% o draw_info: the draw info.
4752%
4753*/
4754MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
4755{
4756 const char
4757 *option;
4758
4759 ExceptionInfo
4760 *exception;
4761
cristy3ed852e2009-09-05 21:47:34 +00004762 /*
4763 Initialize draw attributes.
4764 */
4765 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4766 assert(draw_info != (DrawInfo *) NULL);
4767 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
cristy3ed852e2009-09-05 21:47:34 +00004768 GetAffineMatrix(&draw_info->affine);
4769 exception=AcquireExceptionInfo();
cristyfad60c92012-01-19 18:32:39 +00004770 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
cristy9950d572011-10-01 18:22:35 +00004771 exception);
cristyfad60c92012-01-19 18:32:39 +00004772 (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
cristy9950d572011-10-01 18:22:35 +00004773 exception);
cristy3ed852e2009-09-05 21:47:34 +00004774 draw_info->stroke_width=1.0;
cristy4c08aed2011-07-01 19:47:50 +00004775 draw_info->alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004776 draw_info->fill_rule=EvenOddRule;
4777 draw_info->linecap=ButtCap;
4778 draw_info->linejoin=MiterJoin;
4779 draw_info->miterlimit=10;
4780 draw_info->decorate=NoDecoration;
cristy3ed852e2009-09-05 21:47:34 +00004781 draw_info->pointsize=12.0;
cristy4c08aed2011-07-01 19:47:50 +00004782 draw_info->undercolor.alpha=(Quantum) TransparentAlpha;
cristy3ed852e2009-09-05 21:47:34 +00004783 draw_info->compose=OverCompositeOp;
cristy3ed852e2009-09-05 21:47:34 +00004784 draw_info->render=MagickTrue;
4785 draw_info->debug=IsEventLogging();
cristy0245b5d2011-10-05 12:03:49 +00004786 if (image_info != (ImageInfo *) NULL)
4787 {
4788 draw_info->stroke_antialias=image_info->antialias;
4789 if (image_info->font != (char *) NULL)
4790 draw_info->font=AcquireString(image_info->font);
4791 if (image_info->density != (char *) NULL)
4792 draw_info->density=AcquireString(image_info->density);
4793 draw_info->text_antialias=image_info->antialias;
4794 if (image_info->pointsize != 0.0)
4795 draw_info->pointsize=image_info->pointsize;
4796 draw_info->border_color=image_info->border_color;
4797 if (image_info->server_name != (char *) NULL)
4798 draw_info->server_name=AcquireString(image_info->server_name);
4799 option=GetImageOption(image_info,"encoding");
4800 if (option != (const char *) NULL)
4801 (void) CloneString(&draw_info->encoding,option);
4802 option=GetImageOption(image_info,"kerning");
4803 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004804 draw_info->kerning=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004805 option=GetImageOption(image_info,"interline-spacing");
4806 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004807 draw_info->interline_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004808 option=GetImageOption(image_info,"interword-spacing");
4809 if (option != (const char *) NULL)
cristy9b34e302011-11-05 02:15:45 +00004810 draw_info->interword_spacing=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004811 option=GetImageOption(image_info,"direction");
4812 if (option != (const char *) NULL)
4813 draw_info->direction=(DirectionType) ParseCommandOption(
4814 MagickDirectionOptions,MagickFalse,option);
anthony42f6c202011-10-23 10:28:55 +00004815 else
4816 draw_info->direction=UndefinedDirection;
cristy0245b5d2011-10-05 12:03:49 +00004817 option=GetImageOption(image_info,"fill");
4818 if (option != (const char *) NULL)
4819 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
4820 exception);
4821 option=GetImageOption(image_info,"stroke");
4822 if (option != (const char *) NULL)
4823 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
4824 exception);
4825 option=GetImageOption(image_info,"strokewidth");
4826 if (option != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00004827 draw_info->stroke_width=StringToDouble(option,(char **) NULL);
cristy0245b5d2011-10-05 12:03:49 +00004828 option=GetImageOption(image_info,"undercolor");
4829 if (option != (const char *) NULL)
4830 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
4831 exception);
4832 option=GetImageOption(image_info,"gravity");
4833 if (option != (const char *) NULL)
4834 draw_info->gravity=(GravityType) ParseCommandOption(
4835 MagickGravityOptions,MagickFalse,option);
4836 }
cristy3ed852e2009-09-05 21:47:34 +00004837 exception=DestroyExceptionInfo(exception);
4838 draw_info->signature=MagickSignature;
cristy3ed852e2009-09-05 21:47:34 +00004839}
4840
4841/*
4842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4843% %
4844% %
4845% %
4846+ P e r m u t a t e %
4847% %
4848% %
4849% %
4850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4851%
4852% Permutate() returns the permuation of the (n,k).
4853%
4854% The format of the Permutate method is:
4855%
cristybb503372010-05-27 20:51:26 +00004856% void Permutate(ssize_t n,ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004857%
4858% A description of each parameter follows:
4859%
4860% o n:
4861%
4862% o k:
4863%
4864%
4865*/
cristybb503372010-05-27 20:51:26 +00004866static inline MagickRealType Permutate(const ssize_t n,const ssize_t k)
cristy3ed852e2009-09-05 21:47:34 +00004867{
4868 MagickRealType
4869 r;
4870
cristybb503372010-05-27 20:51:26 +00004871 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004872 i;
4873
4874 r=1.0;
4875 for (i=k+1; i <= n; i++)
4876 r*=i;
4877 for (i=1; i <= (n-k); i++)
4878 r/=i;
4879 return(r);
4880}
4881
4882/*
4883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4884% %
4885% %
4886% %
4887+ T r a c e P r i m i t i v e %
4888% %
4889% %
4890% %
4891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4892%
4893% TracePrimitive is a collection of methods for generating graphic
4894% primitives such as arcs, ellipses, paths, etc.
4895%
4896*/
4897
4898static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
4899 const PointInfo end,const PointInfo degrees)
4900{
4901 PointInfo
4902 center,
4903 radii;
4904
4905 center.x=0.5*(end.x+start.x);
4906 center.y=0.5*(end.y+start.y);
4907 radii.x=fabs(center.x-start.x);
4908 radii.y=fabs(center.y-start.y);
4909 TraceEllipse(primitive_info,center,radii,degrees);
4910}
4911
4912static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
4913 const PointInfo end,const PointInfo arc,const MagickRealType angle,
4914 const MagickBooleanType large_arc,const MagickBooleanType sweep)
4915{
4916 MagickRealType
4917 alpha,
4918 beta,
4919 delta,
4920 factor,
4921 gamma,
4922 theta;
4923
4924 PointInfo
4925 center,
4926 points[3],
4927 radii;
4928
4929 register MagickRealType
4930 cosine,
4931 sine;
4932
4933 register PrimitiveInfo
4934 *p;
4935
cristybb503372010-05-27 20:51:26 +00004936 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004937 i;
4938
cristybb503372010-05-27 20:51:26 +00004939 size_t
cristy3ed852e2009-09-05 21:47:34 +00004940 arc_segments;
4941
4942 if ((start.x == end.x) && (start.y == end.y))
4943 {
4944 TracePoint(primitive_info,end);
4945 return;
4946 }
4947 radii.x=fabs(arc.x);
4948 radii.y=fabs(arc.y);
4949 if ((radii.x == 0.0) || (radii.y == 0.0))
4950 {
4951 TraceLine(primitive_info,start,end);
4952 return;
4953 }
4954 cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
4955 sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
4956 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
4957 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
4958 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
4959 (radii.y*radii.y);
4960 if (delta < MagickEpsilon)
4961 {
4962 TraceLine(primitive_info,start,end);
4963 return;
4964 }
4965 if (delta > 1.0)
4966 {
4967 radii.x*=sqrt((double) delta);
4968 radii.y*=sqrt((double) delta);
4969 }
4970 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
4971 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
4972 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
4973 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
4974 alpha=points[1].x-points[0].x;
4975 beta=points[1].y-points[0].y;
4976 factor=1.0/(alpha*alpha+beta*beta)-0.25;
4977 if (factor <= 0.0)
4978 factor=0.0;
4979 else
4980 {
4981 factor=sqrt((double) factor);
4982 if (sweep == large_arc)
4983 factor=(-factor);
4984 }
4985 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
4986 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
4987 alpha=atan2(points[0].y-center.y,points[0].x-center.x);
4988 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
4989 if ((theta < 0.0) && (sweep != MagickFalse))
4990 theta+=(MagickRealType) (2.0*MagickPI);
4991 else
4992 if ((theta > 0.0) && (sweep == MagickFalse))
4993 theta-=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00004994 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+
cristy20be8a02010-08-17 00:23:28 +00004995 MagickEpsilon))));
cristy3ed852e2009-09-05 21:47:34 +00004996 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00004997 for (i=0; i < (ssize_t) arc_segments; i++)
cristy3ed852e2009-09-05 21:47:34 +00004998 {
4999 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
5000 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
5001 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
5002 sin(fmod((double) beta,DegreesToRadians(360.0)));
5003 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
5004 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
5005 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5006 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
5007 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
5008 (double) i*theta/arc_segments),DegreesToRadians(360.0))));
5009 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
5010 theta/arc_segments),DegreesToRadians(360.0))));
5011 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
5012 theta/arc_segments),DegreesToRadians(360.0))));
5013 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
5014 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5015 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
5016 (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
5017 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
5018 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
5019 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
5020 points[0].y);
5021 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
5022 points[0].y);
5023 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
5024 points[1].y);
5025 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
5026 points[1].y);
5027 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
5028 points[2].y);
5029 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
5030 points[2].y);
cristybb503372010-05-27 20:51:26 +00005031 if (i == (ssize_t) (arc_segments-1))
cristy3ed852e2009-09-05 21:47:34 +00005032 (p+3)->point=end;
5033 TraceBezier(p,4);
5034 p+=p->coordinates;
5035 }
cristybb503372010-05-27 20:51:26 +00005036 primitive_info->coordinates=(size_t) (p-primitive_info);
5037 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005038 {
5039 p->primitive=primitive_info->primitive;
5040 p--;
5041 }
5042}
5043
5044static void TraceBezier(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005045 const size_t number_coordinates)
cristy3ed852e2009-09-05 21:47:34 +00005046{
5047 MagickRealType
5048 alpha,
5049 *coefficients,
5050 weight;
5051
5052 PointInfo
5053 end,
5054 point,
5055 *points;
5056
cristy826a5472010-08-31 23:21:38 +00005057 register PrimitiveInfo
5058 *p;
5059
cristybb503372010-05-27 20:51:26 +00005060 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005061 i,
5062 j;
5063
cristybb503372010-05-27 20:51:26 +00005064 size_t
cristy3ed852e2009-09-05 21:47:34 +00005065 control_points,
5066 quantum;
5067
5068 /*
5069 Allocate coeficients.
5070 */
5071 quantum=number_coordinates;
cristybb503372010-05-27 20:51:26 +00005072 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005073 {
cristybb503372010-05-27 20:51:26 +00005074 for (j=i+1; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005075 {
5076 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
5077 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005078 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005079 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
5080 if (alpha > (MagickRealType) quantum)
cristybb503372010-05-27 20:51:26 +00005081 quantum=(size_t) alpha;
cristy3ed852e2009-09-05 21:47:34 +00005082 }
5083 }
cristybb503372010-05-27 20:51:26 +00005084 quantum=(size_t) MagickMin((double) quantum/number_coordinates,
cristy3ed852e2009-09-05 21:47:34 +00005085 (double) BezierQuantum);
5086 control_points=quantum*number_coordinates;
5087 coefficients=(MagickRealType *) AcquireQuantumMemory((size_t)
5088 number_coordinates,sizeof(*coefficients));
5089 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
5090 sizeof(*points));
5091 if ((coefficients == (MagickRealType *) NULL) ||
5092 (points == (PointInfo *) NULL))
5093 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
5094 /*
5095 Compute bezier points.
5096 */
5097 end=primitive_info[number_coordinates-1].point;
cristybb503372010-05-27 20:51:26 +00005098 for (i=0; i < (ssize_t) number_coordinates; i++)
5099 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
cristy3ed852e2009-09-05 21:47:34 +00005100 weight=0.0;
cristybb503372010-05-27 20:51:26 +00005101 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005102 {
5103 p=primitive_info;
5104 point.x=0.0;
5105 point.y=0.0;
5106 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
cristybb503372010-05-27 20:51:26 +00005107 for (j=0; j < (ssize_t) number_coordinates; j++)
cristy3ed852e2009-09-05 21:47:34 +00005108 {
5109 point.x+=alpha*coefficients[j]*p->point.x;
5110 point.y+=alpha*coefficients[j]*p->point.y;
5111 alpha*=weight/(1.0-weight);
5112 p++;
5113 }
5114 points[i]=point;
5115 weight+=1.0/control_points;
5116 }
5117 /*
5118 Bezier curves are just short segmented polys.
5119 */
5120 p=primitive_info;
cristybb503372010-05-27 20:51:26 +00005121 for (i=0; i < (ssize_t) control_points; i++)
cristy3ed852e2009-09-05 21:47:34 +00005122 {
5123 TracePoint(p,points[i]);
5124 p+=p->coordinates;
5125 }
5126 TracePoint(p,end);
5127 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005128 primitive_info->coordinates=(size_t) (p-primitive_info);
5129 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005130 {
5131 p->primitive=primitive_info->primitive;
5132 p--;
5133 }
5134 points=(PointInfo *) RelinquishMagickMemory(points);
5135 coefficients=(MagickRealType *) RelinquishMagickMemory(coefficients);
5136}
5137
5138static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
5139 const PointInfo end)
5140{
5141 MagickRealType
5142 alpha,
5143 beta,
5144 radius;
5145
5146 PointInfo
5147 offset,
5148 degrees;
5149
5150 alpha=end.x-start.x;
5151 beta=end.y-start.y;
5152 radius=hypot((double) alpha,(double) beta);
5153 offset.x=(double) radius;
5154 offset.y=(double) radius;
5155 degrees.x=0.0;
5156 degrees.y=360.0;
5157 TraceEllipse(primitive_info,start,offset,degrees);
5158}
5159
5160static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
5161 const PointInfo stop,const PointInfo degrees)
5162{
5163 MagickRealType
5164 delta,
5165 step,
5166 y;
5167
5168 PointInfo
5169 angle,
5170 point;
5171
5172 register PrimitiveInfo
5173 *p;
5174
cristybb503372010-05-27 20:51:26 +00005175 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005176 i;
5177
5178 /*
5179 Ellipses are just short segmented polys.
5180 */
5181 if ((stop.x == 0.0) && (stop.y == 0.0))
5182 {
5183 TracePoint(primitive_info,start);
5184 return;
5185 }
5186 delta=2.0/MagickMax(stop.x,stop.y);
5187 step=(MagickRealType) (MagickPI/8.0);
cristyd4e3ffa2011-01-20 13:47:55 +00005188 if ((delta >= 0.0) && (delta < (MagickRealType) (MagickPI/8.0)))
5189 step=(MagickRealType) (MagickPI/(4*(MagickPI/delta/2+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00005190 angle.x=DegreesToRadians(degrees.x);
5191 y=degrees.y;
5192 while (y < degrees.x)
5193 y+=360.0;
5194 angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
5195 for (p=primitive_info; angle.x < angle.y; angle.x+=step)
5196 {
5197 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
5198 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
5199 TracePoint(p,point);
5200 p+=p->coordinates;
5201 }
5202 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
5203 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
5204 TracePoint(p,point);
5205 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005206 primitive_info->coordinates=(size_t) (p-primitive_info);
5207 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005208 {
5209 p->primitive=primitive_info->primitive;
5210 p--;
5211 }
5212}
5213
5214static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
5215 const PointInfo end)
5216{
5217 TracePoint(primitive_info,start);
5218 if ((fabs(start.x-end.x) <= MagickEpsilon) &&
5219 (fabs(start.y-end.y) <= MagickEpsilon))
5220 {
5221 primitive_info->primitive=PointPrimitive;
5222 primitive_info->coordinates=1;
5223 return;
5224 }
5225 TracePoint(primitive_info+1,end);
5226 (primitive_info+1)->primitive=primitive_info->primitive;
5227 primitive_info->coordinates=2;
5228}
5229
cristybb503372010-05-27 20:51:26 +00005230static size_t TracePath(PrimitiveInfo *primitive_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00005231{
5232 char
5233 token[MaxTextExtent];
5234
5235 const char
5236 *p;
5237
5238 int
5239 attribute,
5240 last_attribute;
5241
5242 MagickRealType
5243 x,
5244 y;
5245
5246 PointInfo
5247 end,
5248 points[4],
5249 point,
5250 start;
5251
5252 PrimitiveType
5253 primitive_type;
5254
5255 register PrimitiveInfo
5256 *q;
5257
cristybb503372010-05-27 20:51:26 +00005258 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005259 i;
5260
cristybb503372010-05-27 20:51:26 +00005261 size_t
cristy3ed852e2009-09-05 21:47:34 +00005262 number_coordinates,
5263 z_count;
5264
5265 attribute=0;
5266 point.x=0.0;
5267 point.y=0.0;
5268 start.x=0.0;
5269 start.y=0.0;
5270 number_coordinates=0;
5271 z_count=0;
5272 primitive_type=primitive_info->primitive;
5273 q=primitive_info;
5274 for (p=path; *p != '\0'; )
5275 {
5276 while (isspace((int) ((unsigned char) *p)) != 0)
5277 p++;
5278 if (*p == '\0')
5279 break;
5280 last_attribute=attribute;
5281 attribute=(int) (*p++);
5282 switch (attribute)
5283 {
5284 case 'a':
5285 case 'A':
5286 {
5287 MagickBooleanType
5288 large_arc,
5289 sweep;
5290
5291 MagickRealType
5292 angle;
5293
5294 PointInfo
5295 arc;
5296
5297 /*
5298 Compute arc points.
5299 */
5300 do
5301 {
5302 GetMagickToken(p,&p,token);
5303 if (*token == ',')
5304 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005305 arc.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005306 GetMagickToken(p,&p,token);
5307 if (*token == ',')
5308 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005309 arc.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005310 GetMagickToken(p,&p,token);
5311 if (*token == ',')
5312 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005313 angle=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005314 GetMagickToken(p,&p,token);
5315 if (*token == ',')
5316 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005317 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005318 GetMagickToken(p,&p,token);
5319 if (*token == ',')
5320 GetMagickToken(p,&p,token);
cristyf2f27272009-12-17 14:48:46 +00005321 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005322 GetMagickToken(p,&p,token);
5323 if (*token == ',')
5324 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005325 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005326 GetMagickToken(p,&p,token);
5327 if (*token == ',')
5328 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005329 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005330 end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
5331 end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
5332 TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
5333 q+=q->coordinates;
5334 point=end;
cristy671fcfc2011-12-13 13:28:31 +00005335 while (isspace((int) ((unsigned char) *p)) != 0)
5336 p++;
5337 if (*p == ',')
5338 p++;
cristy3ed852e2009-09-05 21:47:34 +00005339 } while (IsPoint(p) != MagickFalse);
5340 break;
5341 }
5342 case 'c':
5343 case 'C':
5344 {
5345 /*
5346 Compute bezier points.
5347 */
5348 do
5349 {
5350 points[0]=point;
5351 for (i=1; i < 4; i++)
5352 {
5353 GetMagickToken(p,&p,token);
5354 if (*token == ',')
5355 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005356 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005357 GetMagickToken(p,&p,token);
5358 if (*token == ',')
5359 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005360 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005361 end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
5362 end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
5363 points[i]=end;
5364 }
5365 for (i=0; i < 4; i++)
5366 (q+i)->point=points[i];
5367 TraceBezier(q,4);
5368 q+=q->coordinates;
5369 point=end;
5370 } while (IsPoint(p) != MagickFalse);
5371 break;
5372 }
5373 case 'H':
5374 case 'h':
5375 {
5376 do
5377 {
5378 GetMagickToken(p,&p,token);
5379 if (*token == ',')
5380 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005381 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005382 point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
5383 TracePoint(q,point);
5384 q+=q->coordinates;
5385 } while (IsPoint(p) != MagickFalse);
5386 break;
5387 }
5388 case 'l':
5389 case 'L':
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 GetMagickToken(p,&p,token);
5398 if (*token == ',')
5399 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005400 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005401 point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
5402 point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
5403 TracePoint(q,point);
5404 q+=q->coordinates;
5405 } while (IsPoint(p) != MagickFalse);
5406 break;
5407 }
5408 case 'M':
5409 case 'm':
5410 {
5411 if (q != primitive_info)
5412 {
cristybb503372010-05-27 20:51:26 +00005413 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005414 number_coordinates+=primitive_info->coordinates;
5415 primitive_info=q;
5416 }
5417 i=0;
5418 do
5419 {
5420 GetMagickToken(p,&p,token);
5421 if (*token == ',')
5422 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005423 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005424 GetMagickToken(p,&p,token);
5425 if (*token == ',')
5426 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005427 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005428 point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
5429 point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
5430 if (i == 0)
5431 start=point;
5432 i++;
5433 TracePoint(q,point);
5434 q+=q->coordinates;
cristy826a5472010-08-31 23:21:38 +00005435 if ((i != 0) && (attribute == (int) 'M'))
cristy3ed852e2009-09-05 21:47:34 +00005436 {
5437 TracePoint(q,point);
5438 q+=q->coordinates;
5439 }
5440 } while (IsPoint(p) != MagickFalse);
5441 break;
5442 }
5443 case 'q':
5444 case 'Q':
5445 {
5446 /*
5447 Compute bezier points.
5448 */
5449 do
5450 {
5451 points[0]=point;
5452 for (i=1; i < 3; i++)
5453 {
5454 GetMagickToken(p,&p,token);
5455 if (*token == ',')
5456 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005457 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005458 GetMagickToken(p,&p,token);
5459 if (*token == ',')
5460 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005461 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005462 if (*p == ',')
5463 p++;
5464 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
5465 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
5466 points[i]=end;
5467 }
5468 for (i=0; i < 3; i++)
5469 (q+i)->point=points[i];
5470 TraceBezier(q,3);
5471 q+=q->coordinates;
5472 point=end;
5473 } while (IsPoint(p) != MagickFalse);
5474 break;
5475 }
5476 case 's':
5477 case 'S':
5478 {
5479 /*
5480 Compute bezier points.
5481 */
5482 do
5483 {
5484 points[0]=points[3];
5485 points[1].x=2.0*points[3].x-points[2].x;
5486 points[1].y=2.0*points[3].y-points[2].y;
5487 for (i=2; i < 4; i++)
5488 {
5489 GetMagickToken(p,&p,token);
5490 if (*token == ',')
5491 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005492 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005493 GetMagickToken(p,&p,token);
5494 if (*token == ',')
5495 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005496 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005497 if (*p == ',')
5498 p++;
5499 end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
5500 end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
5501 points[i]=end;
5502 }
5503 if (strchr("CcSs",last_attribute) == (char *) NULL)
5504 {
5505 points[0]=points[2];
5506 points[1]=points[3];
5507 }
5508 for (i=0; i < 4; i++)
5509 (q+i)->point=points[i];
5510 TraceBezier(q,4);
5511 q+=q->coordinates;
5512 point=end;
5513 } while (IsPoint(p) != MagickFalse);
5514 break;
5515 }
5516 case 't':
5517 case 'T':
5518 {
5519 /*
5520 Compute bezier points.
5521 */
5522 do
5523 {
5524 points[0]=points[2];
5525 points[1].x=2.0*points[2].x-points[1].x;
5526 points[1].y=2.0*points[2].y-points[1].y;
5527 for (i=2; i < 3; i++)
5528 {
5529 GetMagickToken(p,&p,token);
5530 if (*token == ',')
5531 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005532 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005533 GetMagickToken(p,&p,token);
5534 if (*token == ',')
5535 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005536 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005537 end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
5538 end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
5539 points[i]=end;
5540 }
5541 if (strchr("QqTt",last_attribute) == (char *) NULL)
5542 {
5543 points[0]=points[2];
5544 points[1]=points[3];
5545 }
5546 for (i=0; i < 3; i++)
5547 (q+i)->point=points[i];
5548 TraceBezier(q,3);
5549 q+=q->coordinates;
5550 point=end;
5551 } while (IsPoint(p) != MagickFalse);
5552 break;
5553 }
5554 case 'v':
5555 case 'V':
5556 {
5557 do
5558 {
5559 GetMagickToken(p,&p,token);
5560 if (*token == ',')
5561 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00005562 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00005563 point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
5564 TracePoint(q,point);
5565 q+=q->coordinates;
5566 } while (IsPoint(p) != MagickFalse);
5567 break;
5568 }
5569 case 'z':
5570 case 'Z':
5571 {
5572 point=start;
5573 TracePoint(q,point);
5574 q+=q->coordinates;
cristybb503372010-05-27 20:51:26 +00005575 primitive_info->coordinates=(size_t) (q-primitive_info);
cristy3ed852e2009-09-05 21:47:34 +00005576 number_coordinates+=primitive_info->coordinates;
5577 primitive_info=q;
5578 z_count++;
5579 break;
5580 }
5581 default:
5582 {
5583 if (isalpha((int) ((unsigned char) attribute)) != 0)
cristy1e604812011-05-19 18:07:50 +00005584 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n",
5585 attribute);
cristy3ed852e2009-09-05 21:47:34 +00005586 break;
5587 }
5588 }
5589 }
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;
cristybb503372010-05-27 20:51:26 +00005592 for (i=0; i < (ssize_t) number_coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005593 {
5594 q--;
5595 q->primitive=primitive_type;
5596 if (z_count > 1)
5597 q->method=FillToBorderMethod;
5598 }
5599 q=primitive_info;
5600 return(number_coordinates);
5601}
5602
5603static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
5604 const PointInfo end)
5605{
5606 PointInfo
5607 point;
5608
5609 register PrimitiveInfo
5610 *p;
5611
cristybb503372010-05-27 20:51:26 +00005612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005613 i;
5614
5615 p=primitive_info;
5616 TracePoint(p,start);
5617 p+=p->coordinates;
5618 point.x=start.x;
5619 point.y=end.y;
5620 TracePoint(p,point);
5621 p+=p->coordinates;
5622 TracePoint(p,end);
5623 p+=p->coordinates;
5624 point.x=end.x;
5625 point.y=start.y;
5626 TracePoint(p,point);
5627 p+=p->coordinates;
5628 TracePoint(p,start);
5629 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005630 primitive_info->coordinates=(size_t) (p-primitive_info);
5631 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005632 {
5633 p->primitive=primitive_info->primitive;
5634 p--;
5635 }
5636}
5637
5638static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
5639 const PointInfo start,const PointInfo end,PointInfo arc)
5640{
5641 PointInfo
5642 degrees,
5643 offset,
5644 point;
5645
5646 register PrimitiveInfo
5647 *p;
5648
cristybb503372010-05-27 20:51:26 +00005649 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005650 i;
5651
5652 p=primitive_info;
5653 offset.x=fabs(end.x-start.x);
5654 offset.y=fabs(end.y-start.y);
5655 if (arc.x > (0.5*offset.x))
5656 arc.x=0.5*offset.x;
5657 if (arc.y > (0.5*offset.y))
5658 arc.y=0.5*offset.y;
5659 point.x=start.x+offset.x-arc.x;
5660 point.y=start.y+arc.y;
5661 degrees.x=270.0;
5662 degrees.y=360.0;
5663 TraceEllipse(p,point,arc,degrees);
5664 p+=p->coordinates;
5665 point.x=start.x+offset.x-arc.x;
5666 point.y=start.y+offset.y-arc.y;
5667 degrees.x=0.0;
5668 degrees.y=90.0;
5669 TraceEllipse(p,point,arc,degrees);
5670 p+=p->coordinates;
5671 point.x=start.x+arc.x;
5672 point.y=start.y+offset.y-arc.y;
5673 degrees.x=90.0;
5674 degrees.y=180.0;
5675 TraceEllipse(p,point,arc,degrees);
5676 p+=p->coordinates;
5677 point.x=start.x+arc.x;
5678 point.y=start.y+arc.y;
5679 degrees.x=180.0;
5680 degrees.y=270.0;
5681 TraceEllipse(p,point,arc,degrees);
5682 p+=p->coordinates;
5683 TracePoint(p,primitive_info->point);
5684 p+=p->coordinates;
cristybb503372010-05-27 20:51:26 +00005685 primitive_info->coordinates=(size_t) (p-primitive_info);
5686 for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
cristy3ed852e2009-09-05 21:47:34 +00005687 {
5688 p->primitive=primitive_info->primitive;
5689 p--;
5690 }
5691}
5692
5693static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
cristybb503372010-05-27 20:51:26 +00005694 const size_t number_vertices,const MagickRealType offset)
cristy3ed852e2009-09-05 21:47:34 +00005695{
5696 MagickRealType
5697 distance;
5698
cristy3ed852e2009-09-05 21:47:34 +00005699 register MagickRealType
5700 dx,
5701 dy;
5702
cristybb503372010-05-27 20:51:26 +00005703 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005704 i;
5705
cristy826a5472010-08-31 23:21:38 +00005706 ssize_t
5707 j;
5708
cristy3ed852e2009-09-05 21:47:34 +00005709 dx=0.0;
5710 dy=0.0;
cristybb503372010-05-27 20:51:26 +00005711 for (i=1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005712 {
5713 dx=primitive_info[0].point.x-primitive_info[i].point.x;
5714 dy=primitive_info[0].point.y-primitive_info[i].point.y;
5715 if ((fabs((double) dx) >= MagickEpsilon) ||
5716 (fabs((double) dy) >= MagickEpsilon))
5717 break;
5718 }
cristybb503372010-05-27 20:51:26 +00005719 if (i == (ssize_t) number_vertices)
5720 i=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005721 distance=hypot((double) dx,(double) dy);
5722 primitive_info[0].point.x=(double) (primitive_info[i].point.x+
5723 dx*(distance+offset)/distance);
5724 primitive_info[0].point.y=(double) (primitive_info[i].point.y+
5725 dy*(distance+offset)/distance);
cristybb503372010-05-27 20:51:26 +00005726 for (j=(ssize_t) number_vertices-2; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00005727 {
5728 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
5729 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
5730 if ((fabs((double) dx) >= MagickEpsilon) ||
5731 (fabs((double) dy) >= MagickEpsilon))
5732 break;
5733 }
5734 distance=hypot((double) dx,(double) dy);
5735 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
5736 dx*(distance+offset)/distance);
5737 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
5738 dy*(distance+offset)/distance);
5739}
5740
5741static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
5742 const PrimitiveInfo *primitive_info)
5743{
5744 typedef struct _LineSegment
5745 {
5746 double
5747 p,
5748 q;
5749 } LineSegment;
5750
5751 LineSegment
5752 dx,
5753 dy,
5754 inverse_slope,
5755 slope,
5756 theta;
5757
cristy3ed852e2009-09-05 21:47:34 +00005758 MagickBooleanType
5759 closed_path;
5760
5761 MagickRealType
5762 delta_theta,
5763 dot_product,
5764 mid,
5765 miterlimit;
5766
5767 PointInfo
5768 box_p[5],
5769 box_q[5],
5770 center,
5771 offset,
5772 *path_p,
5773 *path_q;
5774
5775 PrimitiveInfo
5776 *polygon_primitive,
5777 *stroke_polygon;
5778
cristybb503372010-05-27 20:51:26 +00005779 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005780 i;
5781
cristybb503372010-05-27 20:51:26 +00005782 size_t
cristy3ed852e2009-09-05 21:47:34 +00005783 arc_segments,
5784 max_strokes,
5785 number_vertices;
5786
cristy826a5472010-08-31 23:21:38 +00005787 ssize_t
5788 j,
5789 n,
5790 p,
5791 q;
5792
cristy3ed852e2009-09-05 21:47:34 +00005793 /*
5794 Allocate paths.
5795 */
5796 number_vertices=primitive_info->coordinates;
5797 max_strokes=2*number_vertices+6*BezierQuantum+360;
5798 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5799 sizeof(*path_p));
5800 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
5801 sizeof(*path_q));
5802 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
5803 number_vertices+2UL,sizeof(*polygon_primitive));
5804 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
5805 (polygon_primitive == (PrimitiveInfo *) NULL))
5806 return((PrimitiveInfo *) NULL);
5807 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
5808 number_vertices*sizeof(*polygon_primitive));
5809 closed_path=
5810 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
5811 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
5812 MagickTrue : MagickFalse;
5813 if ((draw_info->linejoin == RoundJoin) ||
5814 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
5815 {
5816 polygon_primitive[number_vertices]=primitive_info[1];
5817 number_vertices++;
5818 }
5819 polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
5820 /*
5821 Compute the slope for the first line segment, p.
5822 */
5823 dx.p=0.0;
5824 dy.p=0.0;
cristybb503372010-05-27 20:51:26 +00005825 for (n=1; n < (ssize_t) number_vertices; n++)
cristy3ed852e2009-09-05 21:47:34 +00005826 {
5827 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
5828 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
5829 if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
5830 break;
5831 }
cristybb503372010-05-27 20:51:26 +00005832 if (n == (ssize_t) number_vertices)
5833 n=(ssize_t) number_vertices-1L;
cristy3ed852e2009-09-05 21:47:34 +00005834 slope.p=0.0;
5835 inverse_slope.p=0.0;
5836 if (fabs(dx.p) <= MagickEpsilon)
5837 {
5838 if (dx.p >= 0.0)
5839 slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5840 else
5841 slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5842 }
5843 else
5844 if (fabs(dy.p) <= MagickEpsilon)
5845 {
5846 if (dy.p >= 0.0)
5847 inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5848 else
5849 inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5850 }
5851 else
5852 {
5853 slope.p=dy.p/dx.p;
5854 inverse_slope.p=(-1.0/slope.p);
5855 }
5856 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5857 miterlimit=(MagickRealType) (draw_info->miterlimit*draw_info->miterlimit*
5858 mid*mid);
5859 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
5860 TraceSquareLinecap(polygon_primitive,number_vertices,mid);
5861 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
5862 offset.y=(double) (offset.x*inverse_slope.p);
5863 if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
5864 {
5865 box_p[0].x=polygon_primitive[0].point.x-offset.x;
5866 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
5867 box_p[1].x=polygon_primitive[n].point.x-offset.x;
5868 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
5869 box_q[0].x=polygon_primitive[0].point.x+offset.x;
5870 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
5871 box_q[1].x=polygon_primitive[n].point.x+offset.x;
5872 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
5873 }
5874 else
5875 {
5876 box_p[0].x=polygon_primitive[0].point.x+offset.x;
5877 box_p[0].y=polygon_primitive[0].point.y+offset.y;
5878 box_p[1].x=polygon_primitive[n].point.x+offset.x;
5879 box_p[1].y=polygon_primitive[n].point.y+offset.y;
5880 box_q[0].x=polygon_primitive[0].point.x-offset.x;
5881 box_q[0].y=polygon_primitive[0].point.y-offset.y;
5882 box_q[1].x=polygon_primitive[n].point.x-offset.x;
5883 box_q[1].y=polygon_primitive[n].point.y-offset.y;
5884 }
5885 /*
5886 Create strokes for the line join attribute: bevel, miter, round.
5887 */
5888 p=0;
5889 q=0;
5890 path_q[p++]=box_q[0];
5891 path_p[q++]=box_p[0];
cristybb503372010-05-27 20:51:26 +00005892 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
cristy3ed852e2009-09-05 21:47:34 +00005893 {
5894 /*
5895 Compute the slope for this line segment, q.
5896 */
5897 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
5898 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
5899 dot_product=dx.q*dx.q+dy.q*dy.q;
5900 if (dot_product < 0.25)
5901 continue;
5902 slope.q=0.0;
5903 inverse_slope.q=0.0;
5904 if (fabs(dx.q) < MagickEpsilon)
5905 {
5906 if (dx.q >= 0.0)
5907 slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5908 else
5909 slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5910 }
5911 else
5912 if (fabs(dy.q) <= MagickEpsilon)
5913 {
5914 if (dy.q >= 0.0)
5915 inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
5916 else
5917 inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
5918 }
5919 else
5920 {
5921 slope.q=dy.q/dx.q;
5922 inverse_slope.q=(-1.0/slope.q);
5923 }
5924 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
5925 offset.y=(double) (offset.x*inverse_slope.q);
5926 dot_product=dy.q*offset.x-dx.q*offset.y;
5927 if (dot_product > 0.0)
5928 {
5929 box_p[2].x=polygon_primitive[n].point.x-offset.x;
5930 box_p[2].y=polygon_primitive[n].point.y-offset.y;
5931 box_p[3].x=polygon_primitive[i].point.x-offset.x;
5932 box_p[3].y=polygon_primitive[i].point.y-offset.y;
5933 box_q[2].x=polygon_primitive[n].point.x+offset.x;
5934 box_q[2].y=polygon_primitive[n].point.y+offset.y;
5935 box_q[3].x=polygon_primitive[i].point.x+offset.x;
5936 box_q[3].y=polygon_primitive[i].point.y+offset.y;
5937 }
5938 else
5939 {
5940 box_p[2].x=polygon_primitive[n].point.x+offset.x;
5941 box_p[2].y=polygon_primitive[n].point.y+offset.y;
5942 box_p[3].x=polygon_primitive[i].point.x+offset.x;
5943 box_p[3].y=polygon_primitive[i].point.y+offset.y;
5944 box_q[2].x=polygon_primitive[n].point.x-offset.x;
5945 box_q[2].y=polygon_primitive[n].point.y-offset.y;
5946 box_q[3].x=polygon_primitive[i].point.x-offset.x;
5947 box_q[3].y=polygon_primitive[i].point.y-offset.y;
5948 }
5949 if (fabs((double) (slope.p-slope.q)) <= MagickEpsilon)
5950 {
5951 box_p[4]=box_p[1];
5952 box_q[4]=box_q[1];
5953 }
5954 else
5955 {
5956 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
5957 box_p[3].y)/(slope.p-slope.q));
5958 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
5959 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
5960 box_q[3].y)/(slope.p-slope.q));
5961 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
5962 }
cristybb503372010-05-27 20:51:26 +00005963 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00005964 {
5965 max_strokes+=6*BezierQuantum+360;
5966 path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
5967 sizeof(*path_p));
5968 path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
5969 sizeof(*path_q));
5970 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
5971 {
5972 polygon_primitive=(PrimitiveInfo *)
5973 RelinquishMagickMemory(polygon_primitive);
5974 return((PrimitiveInfo *) NULL);
5975 }
5976 }
5977 dot_product=dx.q*dy.p-dx.p*dy.q;
5978 if (dot_product <= 0.0)
5979 switch (draw_info->linejoin)
5980 {
5981 case BevelJoin:
5982 {
5983 path_q[q++]=box_q[1];
5984 path_q[q++]=box_q[2];
5985 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5986 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
5987 if (dot_product <= miterlimit)
5988 path_p[p++]=box_p[4];
5989 else
5990 {
5991 path_p[p++]=box_p[1];
5992 path_p[p++]=box_p[2];
5993 }
5994 break;
5995 }
5996 case MiterJoin:
5997 {
5998 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
5999 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6000 if (dot_product <= miterlimit)
6001 {
6002 path_q[q++]=box_q[4];
6003 path_p[p++]=box_p[4];
6004 }
6005 else
6006 {
6007 path_q[q++]=box_q[1];
6008 path_q[q++]=box_q[2];
6009 path_p[p++]=box_p[1];
6010 path_p[p++]=box_p[2];
6011 }
6012 break;
6013 }
6014 case RoundJoin:
6015 {
6016 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6017 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6018 if (dot_product <= miterlimit)
6019 path_p[p++]=box_p[4];
6020 else
6021 {
6022 path_p[p++]=box_p[1];
6023 path_p[p++]=box_p[2];
6024 }
6025 center=polygon_primitive[n].point;
6026 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
6027 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
6028 if (theta.q < theta.p)
6029 theta.q+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006030 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/
cristy3ed852e2009-09-05 21:47:34 +00006031 (2.0*sqrt((double) (1.0/mid)))));
6032 path_q[q].x=box_q[1].x;
6033 path_q[q].y=box_q[1].y;
6034 q++;
cristybb503372010-05-27 20:51:26 +00006035 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006036 {
6037 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6038 path_q[q].x=(double) (center.x+mid*cos(fmod((double)
6039 (theta.p+delta_theta),DegreesToRadians(360.0))));
6040 path_q[q].y=(double) (center.y+mid*sin(fmod((double)
6041 (theta.p+delta_theta),DegreesToRadians(360.0))));
6042 q++;
6043 }
6044 path_q[q++]=box_q[2];
6045 break;
6046 }
6047 default:
6048 break;
6049 }
6050 else
6051 switch (draw_info->linejoin)
6052 {
6053 case BevelJoin:
6054 {
6055 path_p[p++]=box_p[1];
6056 path_p[p++]=box_p[2];
6057 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6058 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6059 if (dot_product <= miterlimit)
6060 path_q[q++]=box_q[4];
6061 else
6062 {
6063 path_q[q++]=box_q[1];
6064 path_q[q++]=box_q[2];
6065 }
6066 break;
6067 }
6068 case MiterJoin:
6069 {
6070 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6071 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6072 if (dot_product <= miterlimit)
6073 {
6074 path_q[q++]=box_q[4];
6075 path_p[p++]=box_p[4];
6076 }
6077 else
6078 {
6079 path_q[q++]=box_q[1];
6080 path_q[q++]=box_q[2];
6081 path_p[p++]=box_p[1];
6082 path_p[p++]=box_p[2];
6083 }
6084 break;
6085 }
6086 case RoundJoin:
6087 {
6088 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
6089 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
6090 if (dot_product <= miterlimit)
6091 path_q[q++]=box_q[4];
6092 else
6093 {
6094 path_q[q++]=box_q[1];
6095 path_q[q++]=box_q[2];
6096 }
6097 center=polygon_primitive[n].point;
6098 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
6099 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
6100 if (theta.p < theta.q)
6101 theta.p+=(MagickRealType) (2.0*MagickPI);
cristybb503372010-05-27 20:51:26 +00006102 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/
cristy3ed852e2009-09-05 21:47:34 +00006103 (2.0*sqrt((double) (1.0/mid)))));
6104 path_p[p++]=box_p[1];
cristybb503372010-05-27 20:51:26 +00006105 for (j=1; j < (ssize_t) arc_segments; j++)
cristy3ed852e2009-09-05 21:47:34 +00006106 {
6107 delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
6108 path_p[p].x=(double) (center.x+mid*cos(fmod((double)
6109 (theta.p+delta_theta),DegreesToRadians(360.0))));
6110 path_p[p].y=(double) (center.y+mid*sin(fmod((double)
6111 (theta.p+delta_theta),DegreesToRadians(360.0))));
6112 p++;
6113 }
6114 path_p[p++]=box_p[2];
6115 break;
6116 }
6117 default:
6118 break;
6119 }
6120 slope.p=slope.q;
6121 inverse_slope.p=inverse_slope.q;
6122 box_p[0]=box_p[2];
6123 box_p[1]=box_p[3];
6124 box_q[0]=box_q[2];
6125 box_q[1]=box_q[3];
6126 dx.p=dx.q;
6127 dy.p=dy.q;
6128 n=i;
6129 }
6130 path_p[p++]=box_p[1];
6131 path_q[q++]=box_q[1];
6132 /*
6133 Trace stroked polygon.
6134 */
6135 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
6136 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
6137 if (stroke_polygon != (PrimitiveInfo *) NULL)
6138 {
cristybb503372010-05-27 20:51:26 +00006139 for (i=0; i < (ssize_t) p; i++)
cristy3ed852e2009-09-05 21:47:34 +00006140 {
6141 stroke_polygon[i]=polygon_primitive[0];
6142 stroke_polygon[i].point=path_p[i];
6143 }
6144 if (closed_path != MagickFalse)
6145 {
6146 stroke_polygon[i]=polygon_primitive[0];
6147 stroke_polygon[i].point=stroke_polygon[0].point;
6148 i++;
6149 }
cristybb503372010-05-27 20:51:26 +00006150 for ( ; i < (ssize_t) (p+q+closed_path); i++)
cristy3ed852e2009-09-05 21:47:34 +00006151 {
6152 stroke_polygon[i]=polygon_primitive[0];
6153 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
6154 }
6155 if (closed_path != MagickFalse)
6156 {
6157 stroke_polygon[i]=polygon_primitive[0];
6158 stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
6159 i++;
6160 }
6161 stroke_polygon[i]=polygon_primitive[0];
6162 stroke_polygon[i].point=stroke_polygon[0].point;
6163 i++;
6164 stroke_polygon[i].primitive=UndefinedPrimitive;
cristybb503372010-05-27 20:51:26 +00006165 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
cristy3ed852e2009-09-05 21:47:34 +00006166 }
6167 path_p=(PointInfo *) RelinquishMagickMemory(path_p);
6168 path_q=(PointInfo *) RelinquishMagickMemory(path_q);
6169 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
6170 return(stroke_polygon);
6171}