Coverage Report

Created: 2026-07-20 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/draw.c
Line
Count
Source
1
/*
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
%                                   Cristy                                    %
18
%                                 July 1998                                   %
19
%                                                                             %
20
%                                                                             %
21
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
22
%  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
%    https://imagemagick.org/license/                                         %
28
%                                                                             %
29
%  Unless required by applicable law or agreed to in writing, software        %
30
%  distributed under the License is distributed on an "AS IS" BASIS,          %
31
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32
%  See the License for the specific language governing permissions and        %
33
%  limitations under the License.                                             %
34
%                                                                             %
35
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36
%
37
% Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon
38
% rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion",
39
% Graphics Gems, 1990.  Leonard Rosenthal and David Harr of Appligent
40
% (www.appligent.com) contributed the dash pattern, linecap stroking
41
% algorithm, and minor rendering improvements.
42
%
43
*/
44

45
/*
46
  Include declarations.
47
*/
48
#include "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-private.h"
54
#include "MagickCore/cache-view.h"
55
#include "MagickCore/channel.h"
56
#include "MagickCore/color.h"
57
#include "MagickCore/colorspace-private.h"
58
#include "MagickCore/composite.h"
59
#include "MagickCore/composite-private.h"
60
#include "MagickCore/constitute.h"
61
#include "MagickCore/constitute-private.h"
62
#include "MagickCore/draw.h"
63
#include "MagickCore/draw-private.h"
64
#include "MagickCore/enhance.h"
65
#include "MagickCore/exception.h"
66
#include "MagickCore/exception-private.h"
67
#include "MagickCore/gem.h"
68
#include "MagickCore/geometry.h"
69
#include "MagickCore/image-private.h"
70
#include "MagickCore/list.h"
71
#include "MagickCore/log.h"
72
#include "MagickCore/magick.h"
73
#include "MagickCore/memory-private.h"
74
#include "MagickCore/monitor.h"
75
#include "MagickCore/monitor-private.h"
76
#include "MagickCore/option.h"
77
#include "MagickCore/paint.h"
78
#include "MagickCore/pixel-accessor.h"
79
#include "MagickCore/property.h"
80
#include "MagickCore/resample.h"
81
#include "MagickCore/resample-private.h"
82
#include "MagickCore/resource_.h"
83
#include "MagickCore/splay-tree.h"
84
#include "MagickCore/string_.h"
85
#include "MagickCore/string-private.h"
86
#include "MagickCore/thread-private.h"
87
#include "MagickCore/token.h"
88
#include "MagickCore/transform-private.h"
89
#include "MagickCore/utility.h"
90

91
/*
92
  Define declarations.
93
*/
94
68
#define AntialiasThreshold  (1.0/3.0)
95
213k
#define BezierQuantum  200
96
2.07M
#define PrimitiveExtentPad  4296.0
97
1.11M
#define MaxBezierCoordinates  67108864
98
154
#define ThrowPointExpectedException(token,exception) \
99
154
{ \
100
29.4k
  (void) ThrowMagickException(exception,GetMagickModule(),DrawError, \
101
154
    "NonconformingDrawingPrimitiveDefinition","`%s'",token); \
102
154
  status=MagickFalse; \
103
154
  break; \
104
29.4k
}
105

106
/*
107
  Typedef declarations.
108
*/
109
typedef struct _EdgeInfo
110
{
111
  SegmentInfo
112
    bounds;
113
114
  double
115
    scanline;
116
117
  PointInfo
118
    *points;
119
120
  size_t
121
    number_points;
122
123
  ssize_t
124
    direction;
125
126
  MagickBooleanType
127
    ghostline;
128
129
  size_t
130
    highwater;
131
} EdgeInfo;
132
133
typedef struct _ElementInfo
134
{
135
  double
136
    cx,
137
    cy,
138
    major,
139
    minor,
140
    angle;
141
} ElementInfo;
142
143
typedef struct _MVGInfo
144
{
145
  PrimitiveInfo
146
    **primitive_info;
147
148
  size_t
149
    *extent;
150
151
  ssize_t
152
    offset;
153
154
  PointInfo
155
    point;
156
157
  ExceptionInfo
158
    *exception;
159
} MVGInfo;
160
161
typedef struct _PolygonInfo
162
{
163
  EdgeInfo
164
    *edges;
165
166
  size_t
167
    number_edges;
168
} PolygonInfo;
169
170
typedef enum
171
{
172
  MoveToCode,
173
  OpenCode,
174
  GhostlineCode,
175
  LineToCode,
176
  EndCode
177
} PathInfoCode;
178
179
typedef struct _PathInfo
180
{
181
  PointInfo
182
    point;
183
184
  PathInfoCode
185
    code;
186
} PathInfo;
187

188
/*
189
  Forward declarations.
190
*/
191
static Image
192
  *DrawClippingMask(Image *,const DrawInfo *,const char *,const char *,
193
    ExceptionInfo *);
194
195
static MagickBooleanType
196
  DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *,
197
    ExceptionInfo *),
198
  RenderMVGContent(Image *,const DrawInfo *,const size_t,ExceptionInfo *),
199
  TraceArc(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
200
  TraceArcPath(MVGInfo *,const PointInfo,const PointInfo,const PointInfo,
201
    const double,const MagickBooleanType,const MagickBooleanType),
202
  TraceBezier(MVGInfo *,const size_t),
203
  TraceCircle(MVGInfo *,const PointInfo,const PointInfo),
204
  TraceEllipse(MVGInfo *,const PointInfo,const PointInfo,const PointInfo),
205
  TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
206
  TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
207
  TraceRoundRectangle(MVGInfo *,const PointInfo,const PointInfo,PointInfo),
208
  TraceSquareLinecap(PrimitiveInfo *,const size_t,const double);
209
210
static PrimitiveInfo
211
  *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *,ExceptionInfo *);
212
213
static ssize_t
214
  TracePath(MVGInfo *,const char *,ExceptionInfo *);
215

216
/*
217
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218
%                                                                             %
219
%                                                                             %
220
%                                                                             %
221
%   A c q u i r e D r a w I n f o                                             %
222
%                                                                             %
223
%                                                                             %
224
%                                                                             %
225
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
%
227
%  AcquireDrawInfo() returns a DrawInfo structure properly initialized.
228
%
229
%  The format of the AcquireDrawInfo method is:
230
%
231
%      DrawInfo *AcquireDrawInfo(void)
232
%
233
*/
234
MagickExport DrawInfo *AcquireDrawInfo(void)
235
9.06k
{
236
9.06k
  DrawInfo
237
9.06k
    *draw_info;
238
239
9.06k
  draw_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*draw_info));
240
9.06k
  GetDrawInfo((ImageInfo *) NULL,draw_info);
241
9.06k
  return(draw_info);
242
9.06k
}
243

244
/*
245
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246
%                                                                             %
247
%                                                                             %
248
%                                                                             %
249
%   C l o n e D r a w I n f o                                                 %
250
%                                                                             %
251
%                                                                             %
252
%                                                                             %
253
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254
%
255
%  CloneDrawInfo() makes a copy of the given draw_info structure.  If NULL
256
%  is specified, a new DrawInfo structure is created initialized to default
257
%  values.
258
%
259
%  The format of the CloneDrawInfo method is:
260
%
261
%      DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
262
%        const DrawInfo *draw_info)
263
%
264
%  A description of each parameter follows:
265
%
266
%    o image_info: the image info.
267
%
268
%    o draw_info: the draw info.
269
%
270
*/
271
MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
272
  const DrawInfo *draw_info)
273
1.48M
{
274
1.48M
  DrawInfo
275
1.48M
    *clone_info;
276
277
1.48M
  ExceptionInfo
278
1.48M
    *exception;
279
280
1.48M
  clone_info=(DrawInfo *) AcquireCriticalMemory(sizeof(*clone_info));
281
1.48M
  GetDrawInfo(image_info,clone_info);
282
1.48M
  if (draw_info == (DrawInfo *) NULL)
283
23.9k
    return(clone_info);
284
1.46M
  exception=AcquireExceptionInfo();
285
1.46M
  if (draw_info->id != (char *) NULL)
286
2.53k
    (void) CloneString(&clone_info->id,draw_info->id);
287
1.46M
  if (draw_info->primitive != (char *) NULL)
288
1.41M
    (void) CloneString(&clone_info->primitive,draw_info->primitive);
289
1.46M
  if (draw_info->geometry != (char *) NULL)
290
360k
    (void) CloneString(&clone_info->geometry,draw_info->geometry);
291
1.46M
  clone_info->compliance=draw_info->compliance;
292
1.46M
  clone_info->viewbox=draw_info->viewbox;
293
1.46M
  clone_info->affine=draw_info->affine;
294
1.46M
  clone_info->gravity=draw_info->gravity;
295
1.46M
  clone_info->fill=draw_info->fill;
296
1.46M
  clone_info->stroke=draw_info->stroke;
297
1.46M
  clone_info->stroke_width=draw_info->stroke_width;
298
1.46M
  if (draw_info->fill_pattern != (Image *) NULL)
299
40.4k
    clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
300
40.4k
      exception);
301
1.46M
  if (draw_info->stroke_pattern != (Image *) NULL)
302
271k
    clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
303
271k
      MagickTrue,exception);
304
1.46M
  clone_info->stroke_antialias=draw_info->stroke_antialias;
305
1.46M
  clone_info->text_antialias=draw_info->text_antialias;
306
1.46M
  clone_info->fill_rule=draw_info->fill_rule;
307
1.46M
  clone_info->linecap=draw_info->linecap;
308
1.46M
  clone_info->linejoin=draw_info->linejoin;
309
1.46M
  clone_info->miterlimit=draw_info->miterlimit;
310
1.46M
  clone_info->dash_offset=draw_info->dash_offset;
311
1.46M
  clone_info->decorate=draw_info->decorate;
312
1.46M
  clone_info->compose=draw_info->compose;
313
1.46M
  if (draw_info->text != (char *) NULL)
314
530k
    (void) CloneString(&clone_info->text,draw_info->text);
315
1.46M
  if (draw_info->font != (char *) NULL)
316
639k
    (void) CloneString(&clone_info->font,draw_info->font);
317
1.46M
  if (draw_info->metrics != (char *) NULL)
318
0
    (void) CloneString(&clone_info->metrics,draw_info->metrics);
319
1.46M
  if (draw_info->family != (char *) NULL)
320
7.42k
    (void) CloneString(&clone_info->family,draw_info->family);
321
1.46M
  clone_info->style=draw_info->style;
322
1.46M
  clone_info->stretch=draw_info->stretch;
323
1.46M
  clone_info->weight=draw_info->weight;
324
1.46M
  if (draw_info->encoding != (char *) NULL)
325
483
    (void) CloneString(&clone_info->encoding,draw_info->encoding);
326
1.46M
  clone_info->pointsize=draw_info->pointsize;
327
1.46M
  clone_info->kerning=draw_info->kerning;
328
1.46M
  clone_info->interline_spacing=draw_info->interline_spacing;
329
1.46M
  clone_info->interword_spacing=draw_info->interword_spacing;
330
1.46M
  clone_info->direction=draw_info->direction;
331
1.46M
  clone_info->word_break=draw_info->word_break;
332
1.46M
  if (draw_info->density != (char *) NULL)
333
291k
    (void) CloneString(&clone_info->density,draw_info->density);
334
1.46M
  clone_info->align=draw_info->align;
335
1.46M
  clone_info->undercolor=draw_info->undercolor;
336
1.46M
  clone_info->border_color=draw_info->border_color;
337
1.46M
  if (draw_info->server_name != (char *) NULL)
338
0
    (void) CloneString(&clone_info->server_name,draw_info->server_name);
339
1.46M
  if (draw_info->dash_pattern != (double *) NULL)
340
47.3k
    {
341
47.3k
      ssize_t
342
47.3k
        x;
343
344
146k
      for (x=0; fabs(draw_info->dash_pattern[x]) >= MagickEpsilon; x++) ;
345
47.3k
      clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) (x+1),
346
47.3k
        sizeof(*clone_info->dash_pattern));
347
47.3k
      if (clone_info->dash_pattern == (double *) NULL)
348
0
        ThrowFatalException(ResourceLimitFatalError,
349
47.3k
          "UnableToAllocateDashPattern");
350
47.3k
      (void) memset(clone_info->dash_pattern,0,(size_t) (x+1)*
351
47.3k
        sizeof(*clone_info->dash_pattern));
352
47.3k
      (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,(size_t)
353
47.3k
        x*sizeof(*clone_info->dash_pattern));
354
47.3k
    }
355
1.46M
  clone_info->gradient=draw_info->gradient;
356
1.46M
  if (draw_info->gradient.stops != (StopInfo *) NULL)
357
0
    {
358
0
      size_t
359
0
        number_stops;
360
361
0
      number_stops=clone_info->gradient.number_stops;
362
0
      clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
363
0
        number_stops,sizeof(*clone_info->gradient.stops));
364
0
      if (clone_info->gradient.stops == (StopInfo *) NULL)
365
0
        ThrowFatalException(ResourceLimitFatalError,
366
0
          "UnableToAllocateDashPattern");
367
0
      (void) memcpy(clone_info->gradient.stops,draw_info->gradient.stops,
368
0
        (size_t) number_stops*sizeof(*clone_info->gradient.stops));
369
0
    }
370
1.46M
  clone_info->bounds=draw_info->bounds;
371
1.46M
  clone_info->fill_alpha=draw_info->fill_alpha;
372
1.46M
  clone_info->stroke_alpha=draw_info->stroke_alpha;
373
1.46M
  clone_info->element_reference=draw_info->element_reference;
374
1.46M
  clone_info->clip_path=draw_info->clip_path;
375
1.46M
  clone_info->clip_units=draw_info->clip_units;
376
1.46M
  if (draw_info->clip_mask != (char *) NULL)
377
491k
    (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
378
1.46M
  if (draw_info->clipping_mask != (Image *) NULL)
379
5.88k
    clone_info->clipping_mask=CloneImage(draw_info->clipping_mask,0,0,
380
5.88k
      MagickTrue,exception);
381
1.46M
  if (draw_info->composite_mask != (Image *) NULL)
382
18.4k
    clone_info->composite_mask=CloneImage(draw_info->composite_mask,0,0,
383
18.4k
      MagickTrue,exception);
384
1.46M
  clone_info->render=draw_info->render;
385
1.46M
  clone_info->debug=draw_info->debug;
386
1.46M
  exception=DestroyExceptionInfo(exception);
387
1.46M
  return(clone_info);
388
1.46M
}
389

390
/*
391
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392
%                                                                             %
393
%                                                                             %
394
%                                                                             %
395
+   C o n v e r t P a t h T o P o l y g o n                                   %
396
%                                                                             %
397
%                                                                             %
398
%                                                                             %
399
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400
%
401
%  ConvertPathToPolygon() converts a path to the more efficient sorted
402
%  rendering form.
403
%
404
%  The format of the ConvertPathToPolygon method is:
405
%
406
%      PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info,
407
%        ExceptionInfo *exception)
408
%
409
%  A description of each parameter follows:
410
%
411
%    o ConvertPathToPolygon() returns the path in a more efficient sorted
412
%      rendering form of type PolygonInfo.
413
%
414
%    o draw_info: Specifies a pointer to an DrawInfo structure.
415
%
416
%    o path_info: Specifies a pointer to an PathInfo structure.
417
%
418
%
419
*/
420
421
static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
422
61.0k
{
423
61.0k
  ssize_t
424
61.0k
    i;
425
426
61.0k
  if (polygon_info->edges != (EdgeInfo *) NULL)
427
61.0k
    {
428
253k
      for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
429
192k
        if (polygon_info->edges[i].points != (PointInfo *) NULL)
430
192k
          polygon_info->edges[i].points=(PointInfo *)
431
192k
            RelinquishMagickMemory(polygon_info->edges[i].points);
432
61.0k
      polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(
433
61.0k
        polygon_info->edges);
434
61.0k
    }
435
61.0k
  return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
436
61.0k
}
437
#if defined(__cplusplus) || defined(c_plusplus)
438
extern "C" {
439
#endif
440
441
static int DrawCompareEdges(const void *p_edge,const void *q_edge)
442
397k
{
443
755k
#define DrawCompareEdge(p,q) \
444
755k
{ \
445
755k
  if (((p)-(q)) < 0.0) \
446
755k
    return(-1); \
447
755k
  if (((p)-(q)) > 0.0) \
448
595k
    return(1); \
449
595k
}
450
451
397k
  const PointInfo
452
397k
    *p,
453
397k
    *q;
454
455
  /*
456
    Edge sorting for right-handed coordinate system.
457
  */
458
397k
  p=((const EdgeInfo *) p_edge)->points;
459
397k
  q=((const EdgeInfo *) q_edge)->points;
460
397k
  DrawCompareEdge(p[0].y,q[0].y);
461
140k
  DrawCompareEdge(p[0].x,q[0].x);
462
136k
  DrawCompareEdge((p[1].x-p[0].x)*(q[1].y-q[0].y),(p[1].y-p[0].y)*
463
120k
    (q[1].x-q[0].x));
464
64.2k
  DrawCompareEdge(p[1].y,q[1].y);
465
17.2k
  DrawCompareEdge(p[1].x,q[1].x);
466
15.8k
  return(0);
467
16.9k
}
468
469
#if defined(__cplusplus) || defined(c_plusplus)
470
}
471
#endif
472
473
static void LogPolygonInfo(const PolygonInfo *polygon_info)
474
0
{
475
0
  EdgeInfo
476
0
    *p;
477
478
0
  ssize_t
479
0
    i,
480
0
    j;
481
482
0
  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    begin active-edge");
483
0
  p=polygon_info->edges;
484
0
  for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
485
0
  {
486
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"      edge %.17g:",
487
0
      (double) i);
488
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"      direction: %s",
489
0
      p->direction != MagickFalse ? "down" : "up");
490
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"      ghostline: %s",
491
0
      p->ghostline != MagickFalse ? "transparent" : "opaque");
492
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),
493
0
      "      bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
494
0
      p->bounds.x2,p->bounds.y2);
495
0
    for (j=0; j < (ssize_t) p->number_points; j++)
496
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"        %g,%g",
497
0
        p->points[j].x,p->points[j].y);
498
0
    p++;
499
0
  }
500
0
  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end active-edge");
501
0
}
502
503
static void ReversePoints(PointInfo *points,const size_t number_points)
504
128k
{
505
128k
  PointInfo
506
128k
    point;
507
508
128k
  size_t
509
128k
    i;
510
511
1.81M
  for (i=0; i < (number_points >> 1); i++)
512
1.68M
  {
513
1.68M
    point=points[i];
514
1.68M
    points[i]=points[number_points-(i+1)];
515
1.68M
    points[number_points-(i+1)]=point;
516
1.68M
  }
517
128k
}
518
519
static PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info,
520
  ExceptionInfo *exception)
521
61.0k
{
522
61.0k
  long
523
61.0k
    direction,
524
61.0k
    next_direction;
525
526
61.0k
  PointInfo
527
61.0k
    point,
528
61.0k
    *points;
529
530
61.0k
  PolygonInfo
531
61.0k
    *polygon_info;
532
533
61.0k
  SegmentInfo
534
61.0k
    bounds;
535
536
61.0k
  ssize_t
537
61.0k
    i,
538
61.0k
    n;
539
540
61.0k
  MagickBooleanType
541
61.0k
    ghostline;
542
543
61.0k
  size_t
544
61.0k
    edge,
545
61.0k
    number_edges,
546
61.0k
    number_points;
547
548
  /*
549
    Convert a path to the more efficient sorted rendering form.
550
  */
551
61.0k
  polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
552
61.0k
  if (polygon_info == (PolygonInfo *) NULL)
553
0
    {
554
0
      (void) ThrowMagickException(exception,GetMagickModule(),
555
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
556
0
      return((PolygonInfo *) NULL);
557
0
    }
558
61.0k
  number_edges=16;
559
61.0k
  polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges,
560
61.0k
    sizeof(*polygon_info->edges));
561
61.0k
  if (polygon_info->edges == (EdgeInfo *) NULL)
562
0
    {
563
0
      (void) ThrowMagickException(exception,GetMagickModule(),
564
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
565
0
      return(DestroyPolygonInfo(polygon_info));
566
0
    }
567
61.0k
  (void) memset(polygon_info->edges,0,number_edges*
568
61.0k
    sizeof(*polygon_info->edges));
569
61.0k
  direction=0;
570
61.0k
  edge=0;
571
61.0k
  ghostline=MagickFalse;
572
61.0k
  n=0;
573
61.0k
  number_points=0;
574
61.0k
  points=(PointInfo *) NULL;
575
61.0k
  (void) memset(&point,0,sizeof(point));
576
61.0k
  (void) memset(&bounds,0,sizeof(bounds));
577
61.0k
  polygon_info->edges[edge].number_points=(size_t) n;
578
61.0k
  polygon_info->edges[edge].scanline=0.0;
579
61.0k
  polygon_info->edges[edge].highwater=0;
580
61.0k
  polygon_info->edges[edge].ghostline=ghostline;
581
61.0k
  polygon_info->edges[edge].direction=(ssize_t) direction;
582
61.0k
  polygon_info->edges[edge].points=points;
583
61.0k
  polygon_info->edges[edge].bounds=bounds;
584
61.0k
  polygon_info->number_edges=0;
585
6.52M
  for (i=0; path_info[i].code != EndCode; i++)
586
6.45M
  {
587
6.45M
    if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
588
6.39M
        (path_info[i].code == GhostlineCode))
589
121k
      {
590
        /*
591
          Move to.
592
        */
593
121k
        if ((points != (PointInfo *) NULL) && (n >= 2))
594
60.2k
          {
595
60.2k
            if (edge == number_edges)
596
128
              {
597
128
                number_edges<<=1;
598
128
                polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
599
128
                  polygon_info->edges,(size_t) number_edges,
600
128
                  sizeof(*polygon_info->edges));
601
128
                if (polygon_info->edges == (EdgeInfo *) NULL)
602
0
                  {
603
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
604
0
                      ResourceLimitError,"MemoryAllocationFailed","`%s'","");
605
0
                    points=(PointInfo *) RelinquishMagickMemory(points);
606
0
                    return(DestroyPolygonInfo(polygon_info));
607
0
                  }
608
128
              }
609
60.2k
            polygon_info->edges[edge].number_points=(size_t) n;
610
60.2k
            polygon_info->edges[edge].scanline=(-1.0);
611
60.2k
            polygon_info->edges[edge].highwater=0;
612
60.2k
            polygon_info->edges[edge].ghostline=ghostline;
613
60.2k
            polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
614
60.2k
            if (direction < 0)
615
15.3k
              ReversePoints(points,(size_t) n);
616
60.2k
            polygon_info->edges[edge].points=points;
617
60.2k
            polygon_info->edges[edge].bounds=bounds;
618
60.2k
            polygon_info->edges[edge].bounds.y1=points[0].y;
619
60.2k
            polygon_info->edges[edge].bounds.y2=points[n-1].y;
620
60.2k
            points=(PointInfo *) NULL;
621
60.2k
            ghostline=MagickFalse;
622
60.2k
            edge++;
623
60.2k
            polygon_info->number_edges=edge;
624
60.2k
          }
625
121k
        if (points == (PointInfo *) NULL)
626
121k
          {
627
121k
            number_points=16;
628
121k
            points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
629
121k
              sizeof(*points));
630
121k
            if (points == (PointInfo *) NULL)
631
0
              {
632
0
                (void) ThrowMagickException(exception,GetMagickModule(),
633
0
                  ResourceLimitError,"MemoryAllocationFailed","`%s'","");
634
0
                return(DestroyPolygonInfo(polygon_info));
635
0
              }
636
121k
          }
637
121k
        ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
638
121k
        point=path_info[i].point;
639
121k
        points[0]=point;
640
121k
        bounds.x1=point.x;
641
121k
        bounds.x2=point.x;
642
121k
        direction=0;
643
121k
        n=1;
644
121k
        continue;
645
121k
      }
646
    /*
647
      Line to.
648
    */
649
6.33M
    next_direction=((path_info[i].point.y > point.y) ||
650
3.28M
      ((fabs(path_info[i].point.y-point.y) < MagickEpsilon) &&
651
3.27M
       (path_info[i].point.x > point.x))) ? 1 : -1;
652
6.33M
    if ((points != (PointInfo *) NULL) && (direction != 0) &&
653
6.21M
        (direction != next_direction))
654
135k
      {
655
        /*
656
          New edge.
657
        */
658
135k
        point=points[n-1];
659
135k
        if (edge == number_edges)
660
2.60k
          {
661
2.60k
            number_edges<<=1;
662
2.60k
            polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
663
2.60k
              polygon_info->edges,(size_t) number_edges,
664
2.60k
              sizeof(*polygon_info->edges));
665
2.60k
            if (polygon_info->edges == (EdgeInfo *) NULL)
666
0
              {
667
0
                (void) ThrowMagickException(exception,GetMagickModule(),
668
0
                  ResourceLimitError,"MemoryAllocationFailed","`%s'","");
669
0
                points=(PointInfo *) RelinquishMagickMemory(points);
670
0
                return(DestroyPolygonInfo(polygon_info));
671
0
              }
672
2.60k
          }
673
135k
        polygon_info->edges[edge].number_points=(size_t) n;
674
135k
        polygon_info->edges[edge].scanline=(-1.0);
675
135k
        polygon_info->edges[edge].highwater=0;
676
135k
        polygon_info->edges[edge].ghostline=ghostline;
677
135k
        polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
678
135k
        if (direction < 0)
679
63.6k
          ReversePoints(points,(size_t) n);
680
135k
        polygon_info->edges[edge].points=points;
681
135k
        polygon_info->edges[edge].bounds=bounds;
682
135k
        polygon_info->edges[edge].bounds.y1=points[0].y;
683
135k
        polygon_info->edges[edge].bounds.y2=points[n-1].y;
684
135k
        polygon_info->number_edges=edge+1;
685
135k
        points=(PointInfo *) NULL;
686
135k
        number_points=16;
687
135k
        points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
688
135k
          sizeof(*points));
689
135k
        if (points == (PointInfo *) NULL)
690
0
          {
691
0
            (void) ThrowMagickException(exception,GetMagickModule(),
692
0
              ResourceLimitError,"MemoryAllocationFailed","`%s'","");
693
0
            return(DestroyPolygonInfo(polygon_info));
694
0
          }
695
135k
        n=1;
696
135k
        ghostline=MagickFalse;
697
135k
        points[0]=point;
698
135k
        bounds.x1=point.x;
699
135k
        bounds.x2=point.x;
700
135k
        edge++;
701
135k
      }
702
6.33M
    direction=next_direction;
703
6.33M
    if (points == (PointInfo *) NULL)
704
0
      continue;
705
6.33M
    if (n == (ssize_t) number_points)
706
46.9k
      {
707
46.9k
        number_points<<=1;
708
46.9k
        points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
709
46.9k
          sizeof(*points));
710
46.9k
        if (points == (PointInfo *) NULL)
711
0
          {
712
0
            (void) ThrowMagickException(exception,GetMagickModule(),
713
0
              ResourceLimitError,"MemoryAllocationFailed","`%s'","");
714
0
            return(DestroyPolygonInfo(polygon_info));
715
0
          }
716
46.9k
      }
717
6.33M
    point=path_info[i].point;
718
6.33M
    points[n]=point;
719
6.33M
    if (point.x < bounds.x1)
720
2.69M
      bounds.x1=point.x;
721
6.33M
    if (point.x > bounds.x2)
722
2.04M
      bounds.x2=point.x;
723
6.33M
    n++;
724
6.33M
  }
725
61.0k
  if (points != (PointInfo *) NULL)
726
61.0k
    {
727
61.0k
      if (n < 2)
728
25
        points=(PointInfo *) RelinquishMagickMemory(points);
729
61.0k
      else
730
61.0k
        {
731
61.0k
          if (edge == number_edges)
732
13
            {
733
13
              number_edges<<=1;
734
13
              polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
735
13
                polygon_info->edges,(size_t) number_edges,
736
13
                sizeof(*polygon_info->edges));
737
13
              if (polygon_info->edges == (EdgeInfo *) NULL)
738
0
                {
739
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
740
0
                    ResourceLimitError,"MemoryAllocationFailed","`%s'","");
741
0
                  return(DestroyPolygonInfo(polygon_info));
742
0
                }
743
13
            }
744
61.0k
          polygon_info->edges[edge].number_points=(size_t) n;
745
61.0k
          polygon_info->edges[edge].scanline=(-1.0);
746
61.0k
          polygon_info->edges[edge].highwater=0;
747
61.0k
          polygon_info->edges[edge].ghostline=ghostline;
748
61.0k
          polygon_info->edges[edge].direction=(ssize_t) (direction > 0);
749
61.0k
          if (direction < 0)
750
49.7k
            ReversePoints(points,(size_t) n);
751
61.0k
          polygon_info->edges[edge].points=points;
752
61.0k
          polygon_info->edges[edge].bounds=bounds;
753
61.0k
          polygon_info->edges[edge].bounds.y1=points[0].y;
754
61.0k
          polygon_info->edges[edge].bounds.y2=points[n-1].y;
755
61.0k
          points=(PointInfo *) NULL;
756
61.0k
          ghostline=MagickFalse;
757
61.0k
          edge++;
758
61.0k
          polygon_info->number_edges=edge;
759
61.0k
        }
760
61.0k
    }
761
61.0k
  polygon_info->number_edges=edge;
762
61.0k
  polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(polygon_info->edges,
763
61.0k
    polygon_info->number_edges,sizeof(*polygon_info->edges));
764
61.0k
  if (polygon_info->edges == (EdgeInfo *) NULL)
765
0
    {
766
0
      (void) ThrowMagickException(exception,GetMagickModule(),
767
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
768
0
      return(DestroyPolygonInfo(polygon_info));
769
0
    }
770
317k
  for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
771
256k
  {
772
256k
    EdgeInfo
773
256k
      *edge_info;
774
775
256k
    edge_info=polygon_info->edges+i;
776
256k
    edge_info->points=(PointInfo *) ResizeQuantumMemory(edge_info->points,
777
256k
      edge_info->number_points,sizeof(*edge_info->points));
778
256k
    if (edge_info->points == (PointInfo *) NULL)
779
0
      {
780
0
        (void) ThrowMagickException(exception,GetMagickModule(),
781
0
          ResourceLimitError,"MemoryAllocationFailed","`%s'","");
782
0
        return(DestroyPolygonInfo(polygon_info));
783
0
      }
784
256k
  }
785
61.0k
  qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
786
61.0k
    sizeof(*polygon_info->edges),DrawCompareEdges);
787
61.0k
  if ((GetLogEventMask() & DrawEvent) != 0)
788
0
    LogPolygonInfo(polygon_info);
789
61.0k
  return(polygon_info);
790
61.0k
}
791

792
/*
793
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
794
%                                                                             %
795
%                                                                             %
796
%                                                                             %
797
+   C o n v e r t P r i m i t i v e T o P a t h                               %
798
%                                                                             %
799
%                                                                             %
800
%                                                                             %
801
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802
%
803
%  ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
804
%  path structure.
805
%
806
%  The format of the ConvertPrimitiveToPath method is:
807
%
808
%      PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
809
%        const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
810
%
811
%  A description of each parameter follows:
812
%
813
%    o ConvertPrimitiveToPath() returns a vector path structure of type
814
%      PathInfo.
815
%
816
%    o draw_info: a structure of type DrawInfo.
817
%
818
%    o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
819
%
820
*/
821
822
static void LogPathInfo(const PathInfo *path_info)
823
0
{
824
0
  const PathInfo
825
0
    *p;
826
827
0
  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    begin vector-path");
828
0
  for (p=path_info; p->code != EndCode; p++)
829
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),
830
0
      "      %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
831
0
      "moveto ghostline" : p->code == OpenCode ? "moveto open" :
832
0
      p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
833
0
      "?");
834
0
  (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end vector-path");
835
0
}
836
837
static PathInfo *ConvertPrimitiveToPath(const PrimitiveInfo *primitive_info,
838
  ExceptionInfo *exception)
839
61.0k
{
840
61.0k
  MagickBooleanType
841
61.0k
    closed_subpath;
842
843
61.0k
  PathInfo
844
61.0k
    *path_info;
845
846
61.0k
  PathInfoCode
847
61.0k
    code;
848
849
61.0k
  PointInfo
850
61.0k
    p,
851
61.0k
    q;
852
853
61.0k
  ssize_t
854
61.0k
    n,
855
61.0k
    start;
856
857
61.0k
  size_t
858
61.0k
    coordinates,
859
61.0k
    i;
860
861
  /*
862
    Converts a PrimitiveInfo structure into a vector path structure.
863
  */
864
61.0k
  switch (primitive_info->primitive)
865
61.0k
  {
866
0
    case AlphaPrimitive:
867
0
    case ColorPrimitive:
868
0
    case ImagePrimitive:
869
0
    case PointPrimitive:
870
0
    case TextPrimitive:
871
0
      return((PathInfo *) NULL);
872
61.0k
    default:
873
61.0k
      break;
874
61.0k
  }
875
50.4M
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
876
61.0k
  path_info=(PathInfo *) AcquireQuantumMemory((size_t) (3UL*i+1UL),
877
61.0k
    sizeof(*path_info));
878
61.0k
  if (path_info == (PathInfo *) NULL)
879
20
    {
880
20
      (void) ThrowMagickException(exception,GetMagickModule(),
881
20
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
882
20
      return((PathInfo *) NULL);
883
20
    }
884
61.0k
  coordinates=0;
885
61.0k
  closed_subpath=MagickFalse;
886
61.0k
  n=0;
887
61.0k
  p.x=(-1.0);
888
61.0k
  p.y=(-1.0);
889
61.0k
  q.x=(-1.0);
890
61.0k
  q.y=(-1.0);
891
61.0k
  start=0;
892
6.43M
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
893
6.37M
  {
894
6.37M
    code=LineToCode;
895
6.37M
    if (coordinates <= 0)
896
61.2k
      {
897
        /*
898
          New subpath.
899
        */
900
61.2k
        coordinates=primitive_info[i].coordinates;
901
61.2k
        p=primitive_info[i].point;
902
61.2k
        start=n;
903
61.2k
        code=MoveToCode;
904
61.2k
        closed_subpath=primitive_info[i].closed_subpath;
905
61.2k
      }
906
6.37M
    coordinates--;
907
6.37M
    if ((code == MoveToCode) || (coordinates <= 0) ||
908
6.25M
        (fabs(q.x-primitive_info[i].point.x) >= MagickEpsilon) ||
909
91.1k
        (fabs(q.y-primitive_info[i].point.y) >= MagickEpsilon))
910
6.33M
      {
911
        /*
912
          Eliminate duplicate points.
913
        */
914
6.33M
        path_info[n].code=code;
915
6.33M
        path_info[n].point=primitive_info[i].point;
916
6.33M
        q=primitive_info[i].point;
917
6.33M
        n++;
918
6.33M
      }
919
6.37M
    if (coordinates > 0)
920
6.31M
      continue;  /* next point in current subpath */
921
61.2k
    if (closed_subpath != MagickFalse)
922
1.04k
      {
923
1.04k
        closed_subpath=MagickFalse;
924
1.04k
        continue;
925
1.04k
      }
926
    /*
927
      Mark the p point as open if the subpath is not closed.
928
    */
929
60.2k
    path_info[start].code=OpenCode;
930
60.2k
    path_info[n].code=GhostlineCode;
931
60.2k
    path_info[n].point=primitive_info[i].point;
932
60.2k
    n++;
933
60.2k
    path_info[n].code=LineToCode;
934
60.2k
    path_info[n].point=p;
935
60.2k
    n++;
936
60.2k
  }
937
61.0k
  path_info[n].code=EndCode;
938
61.0k
  path_info[n].point.x=0.0;
939
61.0k
  path_info[n].point.y=0.0;
940
61.0k
  if (IsEventLogging() != MagickFalse)
941
0
    LogPathInfo(path_info);
942
61.0k
  path_info=(PathInfo *) ResizeQuantumMemory(path_info,(size_t) (n+1),
943
61.0k
    sizeof(*path_info));
944
61.0k
  return(path_info);
945
61.0k
}
946

947
/*
948
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
949
%                                                                             %
950
%                                                                             %
951
%                                                                             %
952
%   D e s t r o y D r a w I n f o                                             %
953
%                                                                             %
954
%                                                                             %
955
%                                                                             %
956
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957
%
958
%  DestroyDrawInfo() deallocates memory associated with an DrawInfo structure.
959
%
960
%  The format of the DestroyDrawInfo method is:
961
%
962
%      DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
963
%
964
%  A description of each parameter follows:
965
%
966
%    o draw_info: the draw info.
967
%
968
*/
969
MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
970
2.03M
{
971
2.03M
  assert(draw_info != (DrawInfo *) NULL);
972
2.03M
  assert(draw_info->signature == MagickCoreSignature);
973
2.03M
  if (IsEventLogging() != MagickFalse)
974
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
975
2.03M
  if (draw_info->id != (char *) NULL)
976
3.61k
    draw_info->id=DestroyString(draw_info->id);
977
2.03M
  if (draw_info->primitive != (char *) NULL)
978
1.42M
    draw_info->primitive=DestroyString(draw_info->primitive);
979
2.03M
  if (draw_info->text != (char *) NULL)
980
682k
    draw_info->text=DestroyString(draw_info->text);
981
2.03M
  if (draw_info->geometry != (char *) NULL)
982
430k
    draw_info->geometry=DestroyString(draw_info->geometry);
983
2.03M
  if (draw_info->fill_pattern != (Image *) NULL)
984
21.3k
    draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
985
2.03M
  if (draw_info->stroke_pattern != (Image *) NULL)
986
268k
    draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
987
2.03M
  if (draw_info->font != (char *) NULL)
988
658k
    draw_info->font=DestroyString(draw_info->font);
989
2.03M
  if (draw_info->metrics != (char *) NULL)
990
0
    draw_info->metrics=DestroyString(draw_info->metrics);
991
2.03M
  if (draw_info->family != (char *) NULL)
992
9.36k
    draw_info->family=DestroyString(draw_info->family);
993
2.03M
  if (draw_info->encoding != (char *) NULL)
994
506
    draw_info->encoding=DestroyString(draw_info->encoding);
995
2.03M
  if (draw_info->density != (char *) NULL)
996
294k
    draw_info->density=DestroyString(draw_info->density);
997
2.03M
  if (draw_info->server_name != (char *) NULL)
998
0
    draw_info->server_name=(char *)
999
0
     RelinquishMagickMemory(draw_info->server_name);
1000
2.03M
  if (draw_info->dash_pattern != (double *) NULL)
1001
59.6k
    draw_info->dash_pattern=(double *) RelinquishMagickMemory(
1002
59.6k
      draw_info->dash_pattern);
1003
2.03M
  if (draw_info->gradient.stops != (StopInfo *) NULL)
1004
9.06k
    draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
1005
9.06k
      draw_info->gradient.stops);
1006
2.03M
  if (draw_info->clip_mask != (char *) NULL)
1007
500k
    draw_info->clip_mask=DestroyString(draw_info->clip_mask);
1008
2.03M
  if (draw_info->clipping_mask != (Image *) NULL)
1009
6.81k
    draw_info->clipping_mask=DestroyImage(draw_info->clipping_mask);
1010
2.03M
  if (draw_info->composite_mask != (Image *) NULL)
1011
189k
    draw_info->composite_mask=DestroyImage(draw_info->composite_mask);
1012
2.03M
  if (draw_info->image_info != (ImageInfo *) NULL)
1013
2.03M
    draw_info->image_info=DestroyImageInfo(draw_info->image_info);
1014
2.03M
  draw_info->signature=(~MagickCoreSignature);
1015
2.03M
  draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
1016
2.03M
  return(draw_info);
1017
2.03M
}
1018

1019
/*
1020
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1021
%                                                                             %
1022
%                                                                             %
1023
%                                                                             %
1024
%     D r a w A f f i n e I m a g e                                           %
1025
%                                                                             %
1026
%                                                                             %
1027
%                                                                             %
1028
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1029
%
1030
%  DrawAffineImage() composites the source over the destination image as
1031
%  dictated by the affine transform.
1032
%
1033
%  The format of the DrawAffineImage method is:
1034
%
1035
%      MagickBooleanType DrawAffineImage(Image *image,const Image *source,
1036
%        const AffineMatrix *affine,ExceptionInfo *exception)
1037
%
1038
%  A description of each parameter follows:
1039
%
1040
%    o image: the image.
1041
%
1042
%    o source: the source image.
1043
%
1044
%    o affine: the affine transform.
1045
%
1046
%    o exception: return any errors or warnings in this structure.
1047
%
1048
*/
1049
1050
static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
1051
  const double y,const SegmentInfo *edge)
1052
0
{
1053
0
  double
1054
0
    intercept,
1055
0
    z;
1056
1057
0
  double
1058
0
    x;
1059
1060
0
  SegmentInfo
1061
0
    inverse_edge;
1062
1063
  /*
1064
    Determine left and right edges.
1065
  */
1066
0
  inverse_edge.x1=edge->x1;
1067
0
  inverse_edge.y1=edge->y1;
1068
0
  inverse_edge.x2=edge->x2;
1069
0
  inverse_edge.y2=edge->y2;
1070
0
  z=affine->ry*y+affine->tx;
1071
0
  if (affine->sx >= MagickEpsilon)
1072
0
    {
1073
0
      intercept=(-z/affine->sx);
1074
0
      x=intercept;
1075
0
      if (x > inverse_edge.x1)
1076
0
        inverse_edge.x1=x;
1077
0
      intercept=(-z+(double) image->columns)/affine->sx;
1078
0
      x=intercept;
1079
0
      if (x < inverse_edge.x2)
1080
0
        inverse_edge.x2=x;
1081
0
    }
1082
0
  else
1083
0
    if (affine->sx < -MagickEpsilon)
1084
0
      {
1085
0
        intercept=(-z+(double) image->columns)/affine->sx;
1086
0
        x=intercept;
1087
0
        if (x > inverse_edge.x1)
1088
0
          inverse_edge.x1=x;
1089
0
        intercept=(-z/affine->sx);
1090
0
        x=intercept;
1091
0
        if (x < inverse_edge.x2)
1092
0
          inverse_edge.x2=x;
1093
0
      }
1094
0
    else
1095
0
      if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns))
1096
0
        {
1097
0
          inverse_edge.x2=edge->x1;
1098
0
          return(inverse_edge);
1099
0
        }
1100
  /*
1101
    Determine top and bottom edges.
1102
  */
1103
0
  z=affine->sy*y+affine->ty;
1104
0
  if (affine->rx >= MagickEpsilon)
1105
0
    {
1106
0
      intercept=(-z/affine->rx);
1107
0
      x=intercept;
1108
0
      if (x > inverse_edge.x1)
1109
0
        inverse_edge.x1=x;
1110
0
      intercept=(-z+(double) image->rows)/affine->rx;
1111
0
      x=intercept;
1112
0
      if (x < inverse_edge.x2)
1113
0
        inverse_edge.x2=x;
1114
0
    }
1115
0
  else
1116
0
    if (affine->rx < -MagickEpsilon)
1117
0
      {
1118
0
        intercept=(-z+(double) image->rows)/affine->rx;
1119
0
        x=intercept;
1120
0
        if (x > inverse_edge.x1)
1121
0
          inverse_edge.x1=x;
1122
0
        intercept=(-z/affine->rx);
1123
0
        x=intercept;
1124
0
        if (x < inverse_edge.x2)
1125
0
          inverse_edge.x2=x;
1126
0
      }
1127
0
    else
1128
0
      if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows))
1129
0
        {
1130
0
          inverse_edge.x2=edge->x2;
1131
0
          return(inverse_edge);
1132
0
        }
1133
0
  return(inverse_edge);
1134
0
}
1135
1136
static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
1137
0
{
1138
0
  AffineMatrix
1139
0
    inverse_affine;
1140
1141
0
  double
1142
0
    determinant;
1143
1144
0
  determinant=MagickSafeReciprocal(affine->sx*affine->sy-affine->rx*
1145
0
    affine->ry);
1146
0
  inverse_affine.sx=determinant*affine->sy;
1147
0
  inverse_affine.rx=determinant*(-affine->rx);
1148
0
  inverse_affine.ry=determinant*(-affine->ry);
1149
0
  inverse_affine.sy=determinant*affine->sx;
1150
0
  inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
1151
0
    inverse_affine.ry;
1152
0
  inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
1153
0
    inverse_affine.sy;
1154
0
  return(inverse_affine);
1155
0
}
1156
1157
MagickExport MagickBooleanType DrawAffineImage(Image *image,
1158
  const Image *source,const AffineMatrix *affine,ExceptionInfo *exception)
1159
0
{
1160
0
  AffineMatrix
1161
0
    inverse_affine;
1162
1163
0
  CacheView
1164
0
    *image_view,
1165
0
    *source_view;
1166
1167
0
  MagickBooleanType
1168
0
    status;
1169
1170
0
  PixelInfo
1171
0
    zero;
1172
1173
0
  PointInfo
1174
0
    extent[4],
1175
0
    min,
1176
0
    max;
1177
1178
0
  ssize_t
1179
0
    i;
1180
1181
0
  SegmentInfo
1182
0
    edge;
1183
1184
0
  ssize_t
1185
0
    start,
1186
0
    stop,
1187
0
    y;
1188
1189
  /*
1190
    Determine bounding box.
1191
  */
1192
0
  assert(image != (Image *) NULL);
1193
0
  assert(image->signature == MagickCoreSignature);
1194
0
  if (IsEventLogging() != MagickFalse)
1195
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1196
0
  assert(source != (const Image *) NULL);
1197
0
  assert(source->signature == MagickCoreSignature);
1198
0
  assert(affine != (AffineMatrix *) NULL);
1199
0
  extent[0].x=0.0;
1200
0
  extent[0].y=0.0;
1201
0
  extent[1].x=(double) source->columns;
1202
0
  extent[1].y=0.0;
1203
0
  extent[2].x=(double) source->columns;
1204
0
  extent[2].y=(double) source->rows;
1205
0
  extent[3].x=0.0;
1206
0
  extent[3].y=(double) source->rows;
1207
0
  for (i=0; i < 4; i++)
1208
0
  {
1209
0
    PointInfo
1210
0
      point;
1211
1212
0
    point=extent[i];
1213
0
    extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
1214
0
    extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
1215
0
  }
1216
0
  min=extent[0];
1217
0
  max=extent[0];
1218
0
  for (i=1; i < 4; i++)
1219
0
  {
1220
0
    if (min.x > extent[i].x)
1221
0
      min.x=extent[i].x;
1222
0
    if (min.y > extent[i].y)
1223
0
      min.y=extent[i].y;
1224
0
    if (max.x < extent[i].x)
1225
0
      max.x=extent[i].x;
1226
0
    if (max.y < extent[i].y)
1227
0
      max.y=extent[i].y;
1228
0
  }
1229
  /*
1230
    Affine transform image.
1231
  */
1232
0
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1233
0
    return(MagickFalse);
1234
0
  status=MagickTrue;
1235
0
  edge.x1=min.x;
1236
0
  edge.y1=min.y;
1237
0
  edge.x2=max.x;
1238
0
  edge.y2=max.y;
1239
0
  inverse_affine=InverseAffineMatrix(affine);
1240
0
  if (edge.y1 < 0.0)
1241
0
    edge.y1=0.0;
1242
0
  if (edge.y2 > ((double) image->rows-1.0))
1243
0
    edge.y2=(double) image->rows-1.0;
1244
0
  GetPixelInfo(image,&zero);
1245
0
  start=CastDoubleToSsizeT(ceil(edge.y1-0.5));
1246
0
  stop=CastDoubleToSsizeT(floor(edge.y2+0.5));
1247
0
  source_view=AcquireVirtualCacheView(source,exception);
1248
0
  image_view=AcquireAuthenticCacheView(image,exception);
1249
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1250
  #pragma omp parallel for schedule(static) shared(status) \
1251
    magick_number_threads(source,image,(size_t) (stop-start),2)
1252
#endif
1253
0
  for (y=start; y <= stop; y++)
1254
0
  {
1255
0
    PixelInfo
1256
0
      composite,
1257
0
      pixel;
1258
1259
0
    PointInfo
1260
0
      point;
1261
1262
0
    Quantum
1263
0
      *magick_restrict q;
1264
1265
0
    SegmentInfo
1266
0
      inverse_edge;
1267
1268
0
    ssize_t
1269
0
      x;
1270
1271
0
    if (status == MagickFalse)
1272
0
      continue;
1273
0
    inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
1274
0
    if (inverse_edge.x2 < inverse_edge.x1)
1275
0
      continue;
1276
0
    if (inverse_edge.x1 < 0.0)
1277
0
      inverse_edge.x1=0.0;
1278
0
    if (inverse_edge.x2 > ((double) image->columns-1.0))
1279
0
      inverse_edge.x2=(double) image->columns-1.0;
1280
0
    q=GetCacheViewAuthenticPixels(image_view,CastDoubleToSsizeT(
1281
0
      ceil(inverse_edge.x1-0.5)),y,(size_t) CastDoubleToSsizeT(floor(
1282
0
      inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1),1,exception);
1283
0
    if (q == (Quantum *) NULL)
1284
0
      continue;
1285
0
    pixel=zero;
1286
0
    composite=zero;
1287
0
    for (x=CastDoubleToSsizeT(ceil(inverse_edge.x1-0.5));
1288
0
         x <= CastDoubleToSsizeT(floor(inverse_edge.x2+0.5)); x++)
1289
0
    {
1290
0
      point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
1291
0
        inverse_affine.tx;
1292
0
      point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
1293
0
        inverse_affine.ty;
1294
0
      status=InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel,
1295
0
        point.x,point.y,&pixel,exception);
1296
0
      if (status == MagickFalse)
1297
0
        break;
1298
0
      GetPixelInfoPixel(image,q,&composite);
1299
0
      CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha,
1300
0
        &composite);
1301
0
      SetPixelViaPixelInfo(image,&composite,q);
1302
0
      q+=(ptrdiff_t) GetPixelChannels(image);
1303
0
    }
1304
0
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1305
0
      status=MagickFalse;
1306
0
  }
1307
0
  source_view=DestroyCacheView(source_view);
1308
0
  image_view=DestroyCacheView(image_view);
1309
0
  return(status);
1310
0
}
1311

1312
/*
1313
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1314
%                                                                             %
1315
%                                                                             %
1316
%                                                                             %
1317
+   D r a w B o u n d i n g R e c t a n g l e s                               %
1318
%                                                                             %
1319
%                                                                             %
1320
%                                                                             %
1321
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322
%
1323
%  DrawBoundingRectangles() draws the bounding rectangles on the image.  This
1324
%  is only useful for developers debugging the rendering algorithm.
1325
%
1326
%  The format of the DrawBoundingRectangles method is:
1327
%
1328
%      MagickBooleanType DrawBoundingRectangles(Image *image,
1329
%        const DrawInfo *draw_info,PolygonInfo *polygon_info,
1330
%        ExceptionInfo *exception)
1331
%
1332
%  A description of each parameter follows:
1333
%
1334
%    o image: the image.
1335
%
1336
%    o draw_info: the draw info.
1337
%
1338
%    o polygon_info: Specifies a pointer to a PolygonInfo structure.
1339
%
1340
%    o exception: return any errors or warnings in this structure.
1341
%
1342
*/
1343
1344
static MagickBooleanType DrawBoundingRectangles(Image *image,
1345
  const DrawInfo *draw_info,const PolygonInfo *polygon_info,
1346
  ExceptionInfo *exception)
1347
0
{
1348
0
  double
1349
0
    mid;
1350
1351
0
  DrawInfo
1352
0
    *clone_info;
1353
1354
0
  MagickStatusType
1355
0
    status;
1356
1357
0
  PointInfo
1358
0
    end,
1359
0
    resolution,
1360
0
    start;
1361
1362
0
  PrimitiveInfo
1363
0
    primitive_info[6];
1364
1365
0
  ssize_t
1366
0
    i;
1367
1368
0
  SegmentInfo
1369
0
    bounds;
1370
1371
0
  ssize_t
1372
0
    coordinates;
1373
1374
0
  (void) memset(primitive_info,0,sizeof(primitive_info));
1375
0
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1376
0
  status=QueryColorCompliance("#000F",AllCompliance,&clone_info->fill,
1377
0
    exception);
1378
0
  if (status == MagickFalse)
1379
0
    {
1380
0
      clone_info=DestroyDrawInfo(clone_info);
1381
0
      return(MagickFalse);
1382
0
    }
1383
0
  resolution.x=96.0;
1384
0
  resolution.y=96.0;
1385
0
  if (clone_info->density != (char *) NULL)
1386
0
    {
1387
0
      GeometryInfo
1388
0
        geometry_info;
1389
1390
0
      MagickStatusType
1391
0
        flags;
1392
1393
0
      flags=ParseGeometry(clone_info->density,&geometry_info);
1394
0
      if ((flags & RhoValue) != 0)
1395
0
        resolution.x=geometry_info.rho;
1396
0
      resolution.y=resolution.x;
1397
0
      if ((flags & SigmaValue) != 0)
1398
0
        resolution.y=geometry_info.sigma;
1399
0
    }
1400
0
  mid=(resolution.x/96.0)*ExpandAffine(&clone_info->affine)*
1401
0
    clone_info->stroke_width/2.0;
1402
0
  bounds.x1=0.0;
1403
0
  bounds.y1=0.0;
1404
0
  bounds.x2=0.0;
1405
0
  bounds.y2=0.0;
1406
0
  if (polygon_info != (PolygonInfo *) NULL)
1407
0
    {
1408
0
      bounds=polygon_info->edges[0].bounds;
1409
0
      for (i=1; i < (ssize_t) polygon_info->number_edges; i++)
1410
0
      {
1411
0
        if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
1412
0
          bounds.x1=polygon_info->edges[i].bounds.x1;
1413
0
        if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
1414
0
          bounds.y1=polygon_info->edges[i].bounds.y1;
1415
0
        if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
1416
0
          bounds.x2=polygon_info->edges[i].bounds.x2;
1417
0
        if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
1418
0
          bounds.y2=polygon_info->edges[i].bounds.y2;
1419
0
      }
1420
0
      bounds.x1-=mid;
1421
0
      bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
1422
0
        image->columns ? (double) image->columns-1 : bounds.x1;
1423
0
      bounds.y1-=mid;
1424
0
      bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
1425
0
        image->rows ? (double) image->rows-1 : bounds.y1;
1426
0
      bounds.x2+=mid;
1427
0
      bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
1428
0
        image->columns ? (double) image->columns-1 : bounds.x2;
1429
0
      bounds.y2+=mid;
1430
0
      bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
1431
0
        image->rows ? (double) image->rows-1 : bounds.y2;
1432
0
      for (i=0; i < (ssize_t) polygon_info->number_edges; i++)
1433
0
      {
1434
0
        if (polygon_info->edges[i].direction != 0)
1435
0
          status=QueryColorCompliance("#f00",AllCompliance,&clone_info->stroke,
1436
0
            exception);
1437
0
        else
1438
0
          status=QueryColorCompliance("#0f0",AllCompliance,&clone_info->stroke,
1439
0
            exception);
1440
0
        if (status == MagickFalse)
1441
0
          break;
1442
0
        start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
1443
0
        start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
1444
0
        end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
1445
0
        end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
1446
0
        primitive_info[0].primitive=RectanglePrimitive;
1447
0
        status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1448
0
        primitive_info[0].method=ReplaceMethod;
1449
0
        coordinates=(ssize_t) primitive_info[0].coordinates;
1450
0
        primitive_info[coordinates].primitive=UndefinedPrimitive;
1451
0
        status=DrawPrimitive(image,clone_info,primitive_info,exception);
1452
0
        if (status == MagickFalse)
1453
0
          break;
1454
0
      }
1455
0
      if (i < (ssize_t) polygon_info->number_edges)
1456
0
        {
1457
0
          clone_info=DestroyDrawInfo(clone_info);
1458
0
          return(status == 0 ? MagickFalse : MagickTrue);
1459
0
        }
1460
0
    }
1461
0
  status=QueryColorCompliance("#00f",AllCompliance,&clone_info->stroke,
1462
0
    exception);
1463
0
  if (status == MagickFalse)
1464
0
    {
1465
0
      clone_info=DestroyDrawInfo(clone_info);
1466
0
      return(MagickFalse);
1467
0
    }
1468
0
  start.x=(double) (bounds.x1-mid);
1469
0
  start.y=(double) (bounds.y1-mid);
1470
0
  end.x=(double) (bounds.x2+mid);
1471
0
  end.y=(double) (bounds.y2+mid);
1472
0
  primitive_info[0].primitive=RectanglePrimitive;
1473
0
  status&=(MagickStatusType) TraceRectangle(primitive_info,start,end);
1474
0
  primitive_info[0].method=ReplaceMethod;
1475
0
  coordinates=(ssize_t) primitive_info[0].coordinates;
1476
0
  primitive_info[coordinates].primitive=UndefinedPrimitive;
1477
0
  status=DrawPrimitive(image,clone_info,primitive_info,exception);
1478
0
  clone_info=DestroyDrawInfo(clone_info);
1479
0
  return(status == 0 ? MagickFalse : MagickTrue);
1480
0
}
1481

1482
/*
1483
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484
%                                                                             %
1485
%                                                                             %
1486
%                                                                             %
1487
%   D r a w C l i p P a t h                                                   %
1488
%                                                                             %
1489
%                                                                             %
1490
%                                                                             %
1491
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492
%
1493
%  DrawClipPath() draws the clip path on the image mask.
1494
%
1495
%  The format of the DrawClipPath method is:
1496
%
1497
%      MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
1498
%        const char *id,ExceptionInfo *exception)
1499
%
1500
%  A description of each parameter follows:
1501
%
1502
%    o image: the image.
1503
%
1504
%    o draw_info: the draw info.
1505
%
1506
%    o id: the clip path id.
1507
%
1508
%    o exception: return any errors or warnings in this structure.
1509
%
1510
*/
1511
MagickExport MagickBooleanType DrawClipPath(Image *image,
1512
  const DrawInfo *draw_info,const char *id,ExceptionInfo *exception)
1513
4.60k
{
1514
4.60k
  const char
1515
4.60k
    *clip_path;
1516
1517
4.60k
  Image
1518
4.60k
    *clipping_mask;
1519
1520
4.60k
  MagickBooleanType
1521
4.60k
    status;
1522
1523
4.60k
  clip_path=GetImageArtifact(image,id);
1524
4.60k
  if (clip_path == (const char *) NULL)
1525
742
    return(MagickFalse);
1526
3.86k
  clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
1527
3.86k
    exception);
1528
3.86k
  if (clipping_mask == (Image *) NULL)
1529
1.29k
    return(MagickFalse);
1530
2.57k
  status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
1531
2.57k
  clipping_mask=DestroyImage(clipping_mask);
1532
2.57k
  return(status);
1533
3.86k
}
1534

1535
/*
1536
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1537
%                                                                             %
1538
%                                                                             %
1539
%                                                                             %
1540
%   D r a w C l i p p i n g M a s k                                           %
1541
%                                                                             %
1542
%                                                                             %
1543
%                                                                             %
1544
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545
%
1546
%  DrawClippingMask() draws the clip path and returns it as an image clipping
1547
%  mask.
1548
%
1549
%  The format of the DrawClippingMask method is:
1550
%
1551
%      Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1552
%        const char *id,const char *clip_path,ExceptionInfo *exception)
1553
%
1554
%  A description of each parameter follows:
1555
%
1556
%    o image: the image.
1557
%
1558
%    o draw_info: the draw info.
1559
%
1560
%    o id: the clip path id.
1561
%
1562
%    o clip_path: the clip path.
1563
%
1564
%    o exception: return any errors or warnings in this structure.
1565
%
1566
*/
1567
static Image *DrawClippingMask(Image *image,const DrawInfo *draw_info,
1568
  const char *id,const char *clip_path,ExceptionInfo *exception)
1569
7.58k
{
1570
7.58k
  DrawInfo
1571
7.58k
    *clone_info;
1572
1573
7.58k
  Image
1574
7.58k
    *clip_mask,
1575
7.58k
    *separate_mask;
1576
1577
7.58k
  MagickStatusType
1578
7.58k
    status;
1579
1580
  /*
1581
    Draw a clip path.
1582
  */
1583
7.58k
  assert(image != (Image *) NULL);
1584
7.58k
  assert(image->signature == MagickCoreSignature);
1585
7.58k
  assert(draw_info != (const DrawInfo *) NULL);
1586
7.58k
  if (IsEventLogging() != MagickFalse)
1587
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1588
7.58k
  clip_mask=AcquireImage((const ImageInfo *) NULL,exception);
1589
7.58k
  status=SetImageExtent(clip_mask,image->columns,image->rows,exception);
1590
7.58k
  if (status == MagickFalse)
1591
0
    return(DestroyImage(clip_mask));
1592
7.58k
  status=SetImageMask(clip_mask,WritePixelMask,(Image *) NULL,exception);
1593
7.58k
  status=QueryColorCompliance("#0000",AllCompliance,
1594
7.58k
    &clip_mask->background_color,exception);
1595
7.58k
  clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1596
7.58k
  clip_mask->background_color.alpha_trait=BlendPixelTrait;
1597
7.58k
  status=SetImageBackgroundColor(clip_mask,exception);
1598
7.58k
  if (draw_info->debug != MagickFalse)
1599
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
1600
0
      id);
1601
7.58k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1602
7.58k
  (void) CloneString(&clone_info->primitive,clip_path);
1603
7.58k
  status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1604
7.58k
    exception);
1605
7.58k
  if (clone_info->clip_mask != (char *) NULL)
1606
7.58k
    clone_info->clip_mask=DestroyString(clone_info->clip_mask);
1607
7.58k
  status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1608
7.58k
    exception);
1609
7.58k
  clone_info->stroke_width=0.0;
1610
7.58k
  clone_info->alpha=OpaqueAlpha;
1611
7.58k
  clone_info->clip_path=MagickTrue;
1612
7.58k
  status=RenderMVGContent(clip_mask,clone_info,0,exception);
1613
7.58k
  clone_info=DestroyDrawInfo(clone_info);
1614
7.58k
  separate_mask=SeparateImage(clip_mask,AlphaChannel,exception);
1615
7.58k
  if (separate_mask == (Image *) NULL)
1616
0
    status=MagickFalse;
1617
7.58k
  else
1618
7.58k
    {
1619
7.58k
      clip_mask=DestroyImage(clip_mask);
1620
7.58k
      clip_mask=separate_mask;
1621
7.58k
      status&=(MagickStatusType) NegateImage(clip_mask,MagickFalse,exception);
1622
7.58k
    }
1623
7.58k
  if (status == MagickFalse)
1624
2.58k
    clip_mask=DestroyImage(clip_mask);
1625
7.58k
  if (draw_info->debug != MagickFalse)
1626
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
1627
7.58k
  return(clip_mask);
1628
7.58k
}
1629

1630
/*
1631
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632
%                                                                             %
1633
%                                                                             %
1634
%                                                                             %
1635
%   D r a w C o m p o s i t e M a s k                                         %
1636
%                                                                             %
1637
%                                                                             %
1638
%                                                                             %
1639
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1640
%
1641
%  DrawCompositeMask() draws the mask path and returns it as an image mask.
1642
%
1643
%  The format of the DrawCompositeMask method is:
1644
%
1645
%      Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1646
%        const char *id,const char *mask_path,ExceptionInfo *exception)
1647
%
1648
%  A description of each parameter follows:
1649
%
1650
%    o image: the image.
1651
%
1652
%    o draw_info: the draw info.
1653
%
1654
%    o id: the mask path id.
1655
%
1656
%    o mask_path: the mask path.
1657
%
1658
%    o exception: return any errors or warnings in this structure.
1659
%
1660
*/
1661
static Image *DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1662
  const char *id,const char *mask_path,ExceptionInfo *exception)
1663
343k
{
1664
343k
  Image
1665
343k
    *composite_mask,
1666
343k
    *separate_mask;
1667
1668
343k
  DrawInfo
1669
343k
    *clone_info;
1670
1671
343k
  MagickStatusType
1672
343k
    status;
1673
1674
  /*
1675
    Draw a mask path.
1676
  */
1677
343k
  assert(image != (Image *) NULL);
1678
343k
  assert(image->signature == MagickCoreSignature);
1679
343k
  assert(draw_info != (const DrawInfo *) NULL);
1680
343k
  if (IsEventLogging() != MagickFalse)
1681
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1682
343k
  composite_mask=AcquireImage((const ImageInfo *) NULL,exception);
1683
343k
  status=SetImageExtent(composite_mask,image->columns,image->rows,exception);
1684
343k
  if (status == MagickFalse)
1685
0
    return(DestroyImage(composite_mask));
1686
343k
  status=SetImageMask(composite_mask,CompositePixelMask,(Image *) NULL,
1687
343k
    exception);
1688
343k
  status=QueryColorCompliance("#0000",AllCompliance,
1689
343k
    &composite_mask->background_color,exception);
1690
343k
  composite_mask->background_color.alpha=(MagickRealType) TransparentAlpha;
1691
343k
  composite_mask->background_color.alpha_trait=BlendPixelTrait;
1692
343k
  (void) SetImageBackgroundColor(composite_mask,exception);
1693
343k
  if (draw_info->debug != MagickFalse)
1694
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin mask-path %s",
1695
0
      id);
1696
343k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1697
343k
  (void) CloneString(&clone_info->primitive,mask_path);
1698
343k
  status=QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill,
1699
343k
    exception);
1700
343k
  status=QueryColorCompliance("#00000000",AllCompliance,&clone_info->stroke,
1701
343k
    exception);
1702
343k
  clone_info->stroke_width=0.0;
1703
343k
  clone_info->alpha=OpaqueAlpha;
1704
343k
  status=RenderMVGContent(composite_mask,clone_info,0,exception);
1705
343k
  clone_info=DestroyDrawInfo(clone_info);
1706
343k
  separate_mask=SeparateImage(composite_mask,AlphaChannel,exception);
1707
343k
  if (separate_mask != (Image *) NULL)
1708
343k
    {
1709
343k
      composite_mask=DestroyImage(composite_mask);
1710
343k
      composite_mask=separate_mask;
1711
343k
      status=NegateImage(composite_mask,MagickFalse,exception);
1712
343k
      if (status == MagickFalse)
1713
0
        composite_mask=DestroyImage(composite_mask);
1714
343k
    }
1715
343k
  if (status == MagickFalse)
1716
0
    composite_mask=DestroyImage(composite_mask);
1717
343k
  if (draw_info->debug != MagickFalse)
1718
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end mask-path");
1719
343k
  return(composite_mask);
1720
343k
}
1721

1722
/*
1723
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724
%                                                                             %
1725
%                                                                             %
1726
%                                                                             %
1727
+   D r a w D a s h P o l y g o n                                             %
1728
%                                                                             %
1729
%                                                                             %
1730
%                                                                             %
1731
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732
%
1733
%  DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
1734
%  image while respecting the dash offset and dash pattern attributes.
1735
%
1736
%  The format of the DrawDashPolygon method is:
1737
%
1738
%      MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1739
%        const PrimitiveInfo *primitive_info,Image *image,
1740
%        ExceptionInfo *exception)
1741
%
1742
%  A description of each parameter follows:
1743
%
1744
%    o draw_info: the draw info.
1745
%
1746
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
1747
%
1748
%    o image: the image.
1749
%
1750
%    o exception: return any errors or warnings in this structure.
1751
%
1752
*/
1753
static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
1754
  const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception)
1755
1.47k
{
1756
1.47k
  double
1757
1.47k
    dx,
1758
1.47k
    dy,
1759
1.47k
    length,
1760
1.47k
    maximum_length,
1761
1.47k
    offset,
1762
1.47k
    scale,
1763
1.47k
    total_length;
1764
1765
1.47k
  DrawInfo
1766
1.47k
    *clone_info;
1767
1768
1.47k
  MagickStatusType
1769
1.47k
    status;
1770
1771
1.47k
  PrimitiveInfo
1772
1.47k
    *dash_polygon;
1773
1774
1.47k
  ssize_t
1775
1.47k
    i;
1776
1777
1.47k
  size_t
1778
1.47k
    number_vertices;
1779
1780
1.47k
  ssize_t
1781
1.47k
    j,
1782
1.47k
    n;
1783
1784
1.47k
  assert(draw_info != (const DrawInfo *) NULL);
1785
1.47k
  if (draw_info->debug != MagickFalse)
1786
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    begin draw-dash");
1787
123k
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
1788
1.47k
  number_vertices=(size_t) i;
1789
1.47k
  dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
1790
1.47k
    (2UL*number_vertices+32UL),sizeof(*dash_polygon));
1791
1.47k
  if (dash_polygon == (PrimitiveInfo *) NULL)
1792
0
    {
1793
0
      (void) ThrowMagickException(exception,GetMagickModule(),
1794
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1795
0
      return(MagickFalse);
1796
0
    }
1797
1.47k
  (void) memset(dash_polygon,0,(2UL*number_vertices+32UL)*
1798
1.47k
    sizeof(*dash_polygon));
1799
1.47k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1800
1.47k
  clone_info->miterlimit=0;
1801
1.47k
  dash_polygon[0]=primitive_info[0];
1802
1.47k
  dash_polygon[0].closed_subpath=MagickFalse;
1803
1.47k
  scale=ExpandAffine(&draw_info->affine);
1804
1.47k
  length=scale*draw_info->dash_pattern[0];
1805
1.47k
  offset=fabs(draw_info->dash_offset) >= MagickEpsilon ?
1806
1.47k
    scale*draw_info->dash_offset : 0.0;
1807
1.47k
  j=1;
1808
1.47k
  for (n=0; offset > 0.0; j=0)
1809
8
  {
1810
8
    if (draw_info->dash_pattern[n] <= 0.0)
1811
0
      break;
1812
8
    length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
1813
8
    if (offset > length)
1814
4
      {
1815
4
        offset-=length;
1816
4
        n++;
1817
4
        length=scale*draw_info->dash_pattern[n];
1818
4
        continue;
1819
4
      }
1820
4
    if (offset < length)
1821
4
      {
1822
4
        length-=offset;
1823
4
        offset=0.0;
1824
4
        break;
1825
4
      }
1826
0
    offset=0.0;
1827
0
    n++;
1828
0
  }
1829
1.47k
  status=MagickTrue;
1830
1.47k
  maximum_length=0.0;
1831
1.47k
  total_length=0.0;
1832
122k
  for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++)
1833
120k
  {
1834
120k
    dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
1835
120k
    dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
1836
120k
    maximum_length=hypot(dx,dy);
1837
120k
    if (maximum_length > (double) (MaxBezierCoordinates >> 2))
1838
48
      continue;
1839
120k
    if (fabs(length) < MagickEpsilon)
1840
0
      {
1841
0
        if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1842
0
          n++;
1843
0
        if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1844
0
          n=0;
1845
0
        length=scale*draw_info->dash_pattern[n];
1846
0
      }
1847
194k
    for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); )
1848
74.2k
    {
1849
74.2k
      total_length+=length;
1850
74.2k
      if ((n & 0x01) != 0)
1851
36.8k
        {
1852
36.8k
          dash_polygon[0]=primitive_info[0];
1853
36.8k
          dash_polygon[0].closed_subpath=MagickFalse;
1854
36.8k
          dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
1855
36.8k
            total_length*MagickSafeReciprocal(maximum_length));
1856
36.8k
          dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
1857
36.8k
            total_length*MagickSafeReciprocal(maximum_length));
1858
36.8k
          j=1;
1859
36.8k
        }
1860
37.3k
      else
1861
37.3k
        {
1862
37.3k
          if ((j+1) > (ssize_t) number_vertices)
1863
0
            break;
1864
37.3k
          dash_polygon[j]=primitive_info[i-1];
1865
37.3k
          dash_polygon[j].closed_subpath=MagickFalse;
1866
37.3k
          dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
1867
37.3k
            total_length*MagickSafeReciprocal(maximum_length));
1868
37.3k
          dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
1869
37.3k
            total_length*MagickSafeReciprocal(maximum_length));
1870
37.3k
          dash_polygon[j].coordinates=1;
1871
37.3k
          j++;
1872
37.3k
          dash_polygon[0].coordinates=(size_t) j;
1873
37.3k
          dash_polygon[j].primitive=UndefinedPrimitive;
1874
37.3k
          status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1875
37.3k
            dash_polygon,exception);
1876
37.3k
          if (status == MagickFalse)
1877
0
            break;
1878
37.3k
        }
1879
74.2k
      if (fabs(draw_info->dash_pattern[n]) >= MagickEpsilon)
1880
74.2k
        n++;
1881
74.2k
      if (fabs(draw_info->dash_pattern[n]) < MagickEpsilon)
1882
36.8k
        n=0;
1883
74.2k
      length=scale*draw_info->dash_pattern[n];
1884
74.2k
    }
1885
120k
    length-=(maximum_length-total_length);
1886
120k
    if ((n & 0x01) != 0)
1887
12.4k
      continue;
1888
108k
    dash_polygon[j]=primitive_info[i];
1889
108k
    dash_polygon[j].coordinates=1;
1890
108k
    j++;
1891
108k
  }
1892
1.47k
  if ((status != MagickFalse) && (total_length < maximum_length) &&
1893
916
      ((n & 0x01) == 0) && (j > 1))
1894
585
    {
1895
585
      dash_polygon[j]=primitive_info[i-1];
1896
585
      dash_polygon[j].closed_subpath=MagickFalse;
1897
585
      dash_polygon[j].point.x+=MagickEpsilon;
1898
585
      dash_polygon[j].point.y+=MagickEpsilon;
1899
585
      dash_polygon[j].coordinates=1;
1900
585
      j++;
1901
585
      dash_polygon[0].coordinates=(size_t) j;
1902
585
      dash_polygon[j].primitive=UndefinedPrimitive;
1903
585
      status&=(MagickStatusType) DrawStrokePolygon(image,clone_info,
1904
585
        dash_polygon,exception);
1905
585
    }
1906
1.47k
  dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
1907
1.47k
  clone_info=DestroyDrawInfo(clone_info);
1908
1.47k
  if (draw_info->debug != MagickFalse)
1909
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end draw-dash");
1910
1.47k
  return(status != 0 ? MagickTrue : MagickFalse);
1911
1.47k
}
1912

1913
/*
1914
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1915
%                                                                             %
1916
%                                                                             %
1917
%                                                                             %
1918
%     D r a w G r a d i e n t I m a g e                                       %
1919
%                                                                             %
1920
%                                                                             %
1921
%                                                                             %
1922
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923
%
1924
%  DrawGradientImage() draws a linear gradient on the image.
1925
%
1926
%  The format of the DrawGradientImage method is:
1927
%
1928
%      MagickBooleanType DrawGradientImage(Image *image,
1929
%        const DrawInfo *draw_info,ExceptionInfo *exception)
1930
%
1931
%  A description of each parameter follows:
1932
%
1933
%    o image: the image.
1934
%
1935
%    o draw_info: the draw info.
1936
%
1937
%    o exception: return any errors or warnings in this structure.
1938
%
1939
*/
1940
1941
static inline double GetStopColorOffset(const GradientInfo *gradient,
1942
  const ssize_t x,const ssize_t y)
1943
51.1M
{
1944
51.1M
  switch (gradient->type)
1945
51.1M
  {
1946
0
    case UndefinedGradient:
1947
25.2M
    case LinearGradient:
1948
25.2M
    {
1949
25.2M
      double
1950
25.2M
        gamma,
1951
25.2M
        length,
1952
25.2M
        offset,
1953
25.2M
        scale;
1954
1955
25.2M
      PointInfo
1956
25.2M
        p,
1957
25.2M
        q;
1958
1959
25.2M
      const SegmentInfo
1960
25.2M
        *gradient_vector;
1961
1962
25.2M
      gradient_vector=(&gradient->gradient_vector);
1963
25.2M
      p.x=gradient_vector->x2-gradient_vector->x1;
1964
25.2M
      p.y=gradient_vector->y2-gradient_vector->y1;
1965
25.2M
      q.x=(double) x-gradient_vector->x1;
1966
25.2M
      q.y=(double) y-gradient_vector->y1;
1967
25.2M
      length=sqrt(q.x*q.x+q.y*q.y);
1968
25.2M
      gamma=sqrt(p.x*p.x+p.y*p.y)*length;
1969
25.2M
      gamma=MagickSafeReciprocal(gamma);
1970
25.2M
      scale=p.x*q.x+p.y*q.y;
1971
25.2M
      offset=gamma*scale*length;
1972
25.2M
      return(offset);
1973
0
    }
1974
25.8M
    case RadialGradient:
1975
25.8M
    {
1976
25.8M
      PointInfo
1977
25.8M
        v;
1978
1979
25.8M
      if (gradient->spread == RepeatSpread)
1980
0
        {
1981
0
          v.x=(double) x-gradient->center.x;
1982
0
          v.y=(double) y-gradient->center.y;
1983
0
          return(sqrt(v.x*v.x+v.y*v.y));
1984
0
        }
1985
25.8M
      v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
1986
25.8M
        gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
1987
25.8M
        gradient->angle))))*MagickSafeReciprocal(gradient->radii.x);
1988
25.8M
      v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
1989
25.8M
        gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
1990
25.8M
        gradient->angle))))*MagickSafeReciprocal(gradient->radii.y);
1991
25.8M
      return(sqrt(v.x*v.x+v.y*v.y));
1992
25.8M
    }
1993
51.1M
  }
1994
0
  return(0.0);
1995
51.1M
}
1996
1997
static int StopInfoCompare(const void *x,const void *y)
1998
9.06k
{
1999
9.06k
  StopInfo
2000
9.06k
    *stop_1,
2001
9.06k
    *stop_2;
2002
2003
9.06k
  stop_1=(StopInfo *) x;
2004
9.06k
  stop_2=(StopInfo *) y;
2005
9.06k
  if (stop_1->offset > stop_2->offset)
2006
0
    return(1);
2007
9.06k
  if (fabs(stop_1->offset-stop_2->offset) <= MagickEpsilon)
2008
0
    return(0);
2009
9.06k
  return(-1);
2010
9.06k
}
2011
2012
MagickExport MagickBooleanType DrawGradientImage(Image *image,
2013
  const DrawInfo *draw_info,ExceptionInfo *exception)
2014
9.06k
{
2015
9.06k
  CacheView
2016
9.06k
    *image_view;
2017
2018
9.06k
  const GradientInfo
2019
9.06k
    *gradient;
2020
2021
9.06k
  const SegmentInfo
2022
9.06k
    *gradient_vector;
2023
2024
9.06k
  double
2025
9.06k
    length;
2026
2027
9.06k
  MagickBooleanType
2028
9.06k
    status;
2029
2030
9.06k
  PixelInfo
2031
9.06k
    zero;
2032
2033
9.06k
  PointInfo
2034
9.06k
    point;
2035
2036
9.06k
  RectangleInfo
2037
9.06k
    bounding_box;
2038
2039
9.06k
  size_t
2040
9.06k
    height;
2041
2042
9.06k
  ssize_t
2043
9.06k
    y;
2044
2045
  /*
2046
    Draw linear or radial gradient on image.
2047
  */
2048
9.06k
  assert(image != (Image *) NULL);
2049
9.06k
  assert(image->signature == MagickCoreSignature);
2050
9.06k
  assert(draw_info != (const DrawInfo *) NULL);
2051
9.06k
  if (IsEventLogging() != MagickFalse)
2052
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2053
9.06k
  gradient=(&draw_info->gradient);
2054
9.06k
  qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
2055
9.06k
    StopInfoCompare);
2056
9.06k
  gradient_vector=(&gradient->gradient_vector);
2057
9.06k
  point.x=gradient_vector->x2-gradient_vector->x1;
2058
9.06k
  point.y=gradient_vector->y2-gradient_vector->y1;
2059
9.06k
  length=sqrt(point.x*point.x+point.y*point.y);
2060
9.06k
  bounding_box=gradient->bounding_box;
2061
9.06k
  status=MagickTrue;
2062
9.06k
  GetPixelInfo(image,&zero);
2063
9.06k
  image_view=AcquireAuthenticCacheView(image,exception);
2064
9.06k
  height=(size_t) (bounding_box.y+(ssize_t) bounding_box.height);
2065
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2066
  #pragma omp parallel for schedule(static) shared(status) \
2067
    magick_number_threads(image,image,height,1)
2068
#endif
2069
175k
  for (y=bounding_box.y; y < (ssize_t) height; y++)
2070
166k
  {
2071
166k
    double
2072
166k
      alpha,
2073
166k
      offset;
2074
2075
166k
    PixelInfo
2076
166k
      composite,
2077
166k
      pixel;
2078
2079
166k
    Quantum
2080
166k
      *magick_restrict q;
2081
2082
166k
    size_t
2083
166k
      width;
2084
2085
166k
    ssize_t
2086
166k
      i,
2087
166k
      j,
2088
166k
      x;
2089
2090
166k
    if (status == MagickFalse)
2091
0
      continue;
2092
166k
    q=GetCacheViewAuthenticPixels(image_view,bounding_box.x,y,(size_t)
2093
166k
      bounding_box.width,1,exception);
2094
166k
    if (q == (Quantum *) NULL)
2095
0
      {
2096
0
        status=MagickFalse;
2097
0
        continue;
2098
0
      }
2099
166k
    pixel=zero;
2100
166k
    composite=zero;
2101
166k
    offset=GetStopColorOffset(gradient,0,y);
2102
166k
    if (gradient->type != RadialGradient)
2103
97.8k
      offset*=MagickSafeReciprocal(length);
2104
166k
    width=(size_t) (bounding_box.x+(ssize_t) bounding_box.width);
2105
51.1M
    for (x=bounding_box.x; x < (ssize_t) width; x++)
2106
50.9M
    {
2107
50.9M
      GetPixelInfoPixel(image,q,&pixel);
2108
50.9M
      switch (gradient->spread)
2109
50.9M
      {
2110
0
        case UndefinedSpread:
2111
50.9M
        case PadSpread:
2112
50.9M
        {
2113
50.9M
          if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2114
166k
              (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2115
50.9M
            {
2116
50.9M
              offset=GetStopColorOffset(gradient,x,y);
2117
50.9M
              if (gradient->type != RadialGradient)
2118
25.1M
                offset*=MagickSafeReciprocal(length);
2119
50.9M
            }
2120
106M
          for (i=0; i < (ssize_t) gradient->number_stops; i++)
2121
101M
            if (offset < gradient->stops[i].offset)
2122
46.0M
              break;
2123
50.9M
          if ((offset < 0.0) || (i == 0))
2124
14.6k
            composite=gradient->stops[0].color;
2125
50.9M
          else
2126
50.9M
            if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
2127
4.93M
              composite=gradient->stops[gradient->number_stops-1].color;
2128
46.0M
            else
2129
46.0M
              {
2130
46.0M
                j=i;
2131
46.0M
                i--;
2132
46.0M
                alpha=(offset-gradient->stops[i].offset)/
2133
46.0M
                  (gradient->stops[j].offset-gradient->stops[i].offset);
2134
46.0M
                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2135
46.0M
                  &gradient->stops[j].color,alpha,&composite);
2136
46.0M
              }
2137
50.9M
          break;
2138
0
        }
2139
0
        case ReflectSpread:
2140
0
        {
2141
0
          if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2142
0
              (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2143
0
            {
2144
0
              offset=GetStopColorOffset(gradient,x,y);
2145
0
              if (gradient->type != RadialGradient)
2146
0
                offset*=MagickSafeReciprocal(length);
2147
0
            }
2148
0
          if (offset < 0.0)
2149
0
            offset=(-offset);
2150
0
          if ((ssize_t) fmod(offset,2.0) == 0)
2151
0
            offset=fmod(offset,1.0);
2152
0
          else
2153
0
            offset=1.0-fmod(offset,1.0);
2154
0
          for (i=0; i < (ssize_t) gradient->number_stops; i++)
2155
0
            if (offset < gradient->stops[i].offset)
2156
0
              break;
2157
0
          if (i == 0)
2158
0
            composite=gradient->stops[0].color;
2159
0
          else
2160
0
            if (i == (ssize_t) gradient->number_stops)
2161
0
              composite=gradient->stops[gradient->number_stops-1].color;
2162
0
            else
2163
0
              {
2164
0
                j=i;
2165
0
                i--;
2166
0
                alpha=(offset-gradient->stops[i].offset)/
2167
0
                  (gradient->stops[j].offset-gradient->stops[i].offset);
2168
0
                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2169
0
                  &gradient->stops[j].color,alpha,&composite);
2170
0
              }
2171
0
          break;
2172
0
        }
2173
0
        case RepeatSpread:
2174
0
        {
2175
0
          double
2176
0
            repeat;
2177
2178
0
          MagickBooleanType
2179
0
            antialias;
2180
2181
0
          antialias=MagickFalse;
2182
0
          repeat=0.0;
2183
0
          if ((x != CastDoubleToSsizeT(ceil(gradient_vector->x1-0.5))) ||
2184
0
              (y != CastDoubleToSsizeT(ceil(gradient_vector->y1-0.5))))
2185
0
            {
2186
0
              offset=GetStopColorOffset(gradient,x,y);
2187
0
              if (gradient->type == LinearGradient)
2188
0
                {
2189
0
                  repeat=fmod(offset,length);
2190
0
                  if (repeat < 0.0)
2191
0
                    repeat=length-fmod(-repeat,length);
2192
0
                  else
2193
0
                    repeat=fmod(offset,length);
2194
0
                  antialias=(repeat < length) && ((repeat+1.0) > length) ?
2195
0
                    MagickTrue : MagickFalse;
2196
0
                  offset=MagickSafeReciprocal(length)*repeat;
2197
0
                }
2198
0
              else
2199
0
                {
2200
0
                  repeat=fmod(offset,gradient->radius);
2201
0
                  if (repeat < 0.0)
2202
0
                    repeat=gradient->radius-fmod(-repeat,gradient->radius);
2203
0
                  else
2204
0
                    repeat=fmod(offset,gradient->radius);
2205
0
                  antialias=repeat+1.0 > gradient->radius ? MagickTrue :
2206
0
                    MagickFalse;
2207
0
                  offset=repeat*MagickSafeReciprocal(gradient->radius);
2208
0
                }
2209
0
            }
2210
0
          for (i=0; i < (ssize_t) gradient->number_stops; i++)
2211
0
            if (offset < gradient->stops[i].offset)
2212
0
              break;
2213
0
          if (i == 0)
2214
0
            composite=gradient->stops[0].color;
2215
0
          else
2216
0
            if (i == (ssize_t) gradient->number_stops)
2217
0
              composite=gradient->stops[gradient->number_stops-1].color;
2218
0
            else
2219
0
              {
2220
0
                j=i;
2221
0
                i--;
2222
0
                alpha=(offset-gradient->stops[i].offset)/
2223
0
                  (gradient->stops[j].offset-gradient->stops[i].offset);
2224
0
                if (antialias != MagickFalse)
2225
0
                  {
2226
0
                    if (gradient->type == LinearGradient)
2227
0
                      alpha=length-repeat;
2228
0
                    else
2229
0
                      alpha=gradient->radius-repeat;
2230
0
                    i=0;
2231
0
                    j=(ssize_t) gradient->number_stops-1L;
2232
0
                  }
2233
0
                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
2234
0
                  &gradient->stops[j].color,alpha,&composite);
2235
0
              }
2236
0
          break;
2237
0
        }
2238
50.9M
      }
2239
50.9M
      CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
2240
50.9M
        &pixel);
2241
50.9M
      SetPixelViaPixelInfo(image,&pixel,q);
2242
50.9M
      q+=(ptrdiff_t) GetPixelChannels(image);
2243
50.9M
    }
2244
166k
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2245
0
      status=MagickFalse;
2246
166k
  }
2247
9.06k
  image_view=DestroyCacheView(image_view);
2248
9.06k
  return(status);
2249
9.06k
}
2250

2251
/*
2252
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2253
%                                                                             %
2254
%                                                                             %
2255
%                                                                             %
2256
%   D r a w I m a g e                                                         %
2257
%                                                                             %
2258
%                                                                             %
2259
%                                                                             %
2260
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2261
%
2262
%  DrawImage() draws a graphic primitive on your image.  The primitive
2263
%  may be represented as a string or filename.  Precede the filename with an
2264
%  "at" sign (@) and the contents of the file are drawn on the image.  You
2265
%  can affect how text is drawn by setting one or more members of the draw
2266
%  info structure.
2267
%
2268
%  The format of the DrawImage method is:
2269
%
2270
%      MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
2271
%        ExceptionInfo *exception)
2272
%
2273
%  A description of each parameter follows:
2274
%
2275
%    o image: the image.
2276
%
2277
%    o draw_info: the draw info.
2278
%
2279
%    o exception: return any errors or warnings in this structure.
2280
%
2281
*/
2282
2283
static inline MagickBooleanType CheckPrimitiveExtent(MVGInfo *mvg_info,
2284
  const double pad)
2285
1.26M
{
2286
1.26M
  double
2287
1.26M
    proposed_extent;
2288
2289
1.26M
  PrimitiveInfo
2290
1.26M
    *primitive_info;
2291
2292
1.26M
  size_t
2293
1.26M
    extent;
2294
2295
1.26M
  ssize_t
2296
1.26M
    i;
2297
2298
1.26M
  if ((mvg_info == (MVGInfo *) NULL) ||
2299
1.26M
      (mvg_info->primitive_info == (PrimitiveInfo **) NULL) ||
2300
1.26M
      (*mvg_info->primitive_info == (PrimitiveInfo *) NULL) ||
2301
1.26M
      (mvg_info->extent == (size_t *) NULL))
2302
0
    return(MagickFalse);
2303
1.26M
  proposed_extent=mvg_info->offset+pad+PrimitiveExtentPad+1.0;
2304
1.26M
  if ((proposed_extent <= 0.0) || (proposed_extent > (double) MAGICK_SIZE_MAX))
2305
16
    return(MagickFalse);
2306
1.26M
  extent=CastDoubleToSizeT(ceil(proposed_extent));
2307
1.26M
  if (extent <= *mvg_info->extent)
2308
928k
    return(MagickTrue);
2309
339k
  if (extent > (GetMaxMemoryRequest()/sizeof(PrimitiveInfo)))
2310
4.67k
    return(MagickFalse);
2311
334k
  primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(
2312
334k
    *mvg_info->primitive_info,extent+1,sizeof(PrimitiveInfo));
2313
334k
  if (primitive_info == (PrimitiveInfo *) NULL)
2314
0
    {
2315
      /*
2316
        Create a stack to unwind; report failure.
2317
      */
2318
0
      extent=(size_t) PrimitiveExtentPad;
2319
0
      primitive_info=(PrimitiveInfo *) AcquireCriticalMemory(extent*
2320
0
        sizeof(*primitive_info));
2321
0
      (void) memset(primitive_info,0,extent*sizeof(*primitive_info));
2322
0
      *mvg_info->primitive_info=primitive_info;
2323
0
      *mvg_info->extent=extent;
2324
0
      mvg_info->offset=0;
2325
0
      ThrowMagickException(mvg_info->exception,GetMagickModule(),
2326
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2327
0
      return(MagickFalse);
2328
0
    }
2329
334k
  primitive_info[extent].primitive=UndefinedPrimitive;
2330
334k
  primitive_info[extent].text=(char *) NULL;
2331
  /*
2332
    Commit updated buffer.
2333
  */
2334
1.69G
  for (i=(ssize_t) *mvg_info->extent; i < (ssize_t) extent; i++)
2335
1.69G
  {
2336
1.69G
    primitive_info[i].primitive=UndefinedPrimitive;
2337
1.69G
    primitive_info[i].text=(char *) NULL;
2338
1.69G
  }
2339
334k
  *mvg_info->primitive_info=primitive_info;
2340
334k
  *mvg_info->extent=extent;
2341
334k
  return(MagickTrue);
2342
334k
}
2343
2344
static inline double GetDrawValue(const char *magick_restrict string,
2345
  char **magick_restrict sentinel)
2346
5.50M
{
2347
5.50M
  char
2348
5.50M
    **magick_restrict q;
2349
2350
5.50M
  double
2351
5.50M
    value;
2352
2353
5.50M
  q=sentinel;
2354
5.50M
  value=InterpretLocaleValue(string,q);
2355
5.50M
  sentinel=q;
2356
5.50M
  return(value);
2357
5.50M
}
2358
2359
static int MVGMacroCompare(const void *target,const void *source)
2360
2.28M
{
2361
2.28M
  const char
2362
2.28M
    *p,
2363
2.28M
    *q;
2364
2365
2.28M
  p=(const char *) target;
2366
2.28M
  q=(const char *) source;
2367
2.28M
  return(strcmp(p,q));
2368
2.28M
}
2369
2370
static SplayTreeInfo *GetMVGMacros(const char *primitive,
2371
  ExceptionInfo *exception)
2372
369k
{
2373
369k
  char
2374
369k
    *macro,
2375
369k
    *token;
2376
2377
369k
  const char
2378
369k
    *q;
2379
2380
369k
  size_t
2381
369k
    extent;
2382
2383
369k
  SplayTreeInfo
2384
369k
    *macros;
2385
2386
  /*
2387
    Scan graphic primitives for definitions and classes.
2388
  */
2389
369k
  if (primitive == (const char *) NULL)
2390
0
    return((SplayTreeInfo *) NULL);
2391
369k
  macros=NewSplayTree(MVGMacroCompare,RelinquishMagickMemory,
2392
369k
    RelinquishMagickMemory);
2393
369k
  macro=AcquireString(primitive);
2394
369k
  token=AcquireString(primitive);
2395
369k
  extent=strlen(token)+MagickPathExtent;
2396
29.8M
  for (q=primitive; *q != '\0'; )
2397
29.4M
  {
2398
29.4M
    if (GetNextToken(q,&q,extent,token) < 1)
2399
0
      break;
2400
29.4M
    if (*token == '\0')
2401
269
      break;
2402
29.4M
    if (LocaleCompare("push",token) == 0)
2403
436k
      {
2404
436k
        const char
2405
436k
          *end,
2406
436k
          *start;
2407
2408
436k
        (void) GetNextToken(q,&q,extent,token);
2409
436k
        if (*q == '"')
2410
386k
          {
2411
386k
            char
2412
386k
              name[MagickPathExtent];
2413
2414
386k
            const char
2415
386k
              *p;
2416
2417
386k
            ssize_t
2418
386k
              n;
2419
2420
            /*
2421
              Named macro (e.g. push graphic-context "wheel").
2422
            */
2423
386k
            (void) GetNextToken(q,&q,extent,token);
2424
386k
            start=q;
2425
386k
            end=q;
2426
386k
            (void) CopyMagickString(name,token,MagickPathExtent);
2427
386k
            n=1;
2428
32.1M
            for (p=q; *p != '\0'; )
2429
32.1M
            {
2430
32.1M
              if (GetNextToken(p,&p,extent,token) < 1)
2431
0
                break;
2432
32.1M
              if (*token == '\0')
2433
277
                break;
2434
32.1M
              if (LocaleCompare(token,"pop") == 0)
2435
847k
                {
2436
847k
                  end=p-strlen(token)-1;
2437
847k
                  n--;
2438
847k
                }
2439
32.1M
              if (LocaleCompare(token,"push") == 0)
2440
486k
                {
2441
486k
                  if (n++ > MagickMaxRecursionDepth)
2442
0
                    {
2443
0
                      (void) ThrowMagickException(exception,GetMagickModule(),
2444
0
                        DrawError,"VectorGraphicsNestedTooDeeply","`%s'",token);
2445
0
                      break;
2446
0
                    }
2447
486k
                }
2448
32.1M
              if ((n == 0) && (end >= start))
2449
377k
                {
2450
377k
                  size_t
2451
377k
                    length=(size_t) (end-start);
2452
2453
                  /*
2454
                    Extract macro.
2455
                  */
2456
377k
                  (void) GetNextToken(p,&p,extent,token);
2457
377k
                  if (length > 0)
2458
377k
                    {
2459
377k
                      (void) CopyMagickString(macro,start,length);
2460
377k
                      (void) AddValueToSplayTree(macros,ConstantString(name),
2461
377k
                        ConstantString(macro));
2462
377k
                    }
2463
377k
                  break;
2464
377k
                }
2465
32.1M
            }
2466
386k
          }
2467
436k
      }
2468
29.4M
  }
2469
369k
  token=DestroyString(token);
2470
369k
  macro=DestroyString(macro);
2471
369k
  return(macros);
2472
369k
}
2473
2474
static inline MagickBooleanType IsValidListChar(int c)
2475
1.38M
{
2476
1.38M
  if ((c >= '0') && (c <= '9'))
2477
1.06M
    return(MagickTrue);
2478
320k
  switch (c)
2479
320k
  {
2480
748
    case '.':
2481
3.26k
    case '+':
2482
184k
    case '-':
2483
305k
    case ',':
2484
305k
    case ' ':
2485
305k
    case '\t':
2486
305k
    case '\r':
2487
305k
    case '\n':
2488
305k
      break;
2489
15.5k
    default:
2490
15.5k
      return(MagickFalse);
2491
320k
  }
2492
305k
  return(MagickTrue);
2493
320k
}
2494
2495
static inline MagickBooleanType IsValidPoint(const char *point)
2496
1.71M
{
2497
1.71M
  char
2498
1.71M
    *p;
2499
2500
1.71M
  double
2501
1.71M
    value;
2502
2503
1.71M
  value=GetDrawValue(point,&p);
2504
1.71M
  return((fabs(value) < MagickEpsilon) && (p == point) ? MagickFalse :
2505
1.71M
    MagickTrue);
2506
1.71M
}
2507
2508
static inline MagickBooleanType TracePoint(PrimitiveInfo *primitive_info,
2509
  const PointInfo point)
2510
55.7M
{
2511
55.7M
  primitive_info->point=point;
2512
55.7M
  primitive_info->coordinates=1;
2513
55.7M
  primitive_info->closed_subpath=MagickFalse;
2514
55.7M
  primitive_info->text=(char *) NULL;
2515
55.7M
  return(MagickTrue);
2516
55.7M
}
2517
2518
static MagickBooleanType RenderMVGContent(Image *image,
2519
  const DrawInfo *draw_info,const size_t depth,ExceptionInfo *exception)
2520
377k
{
2521
390k
#define RenderImageTag  "Render/Image"
2522
2523
377k
  AffineMatrix
2524
377k
    affine,
2525
377k
    current;
2526
2527
377k
  char
2528
377k
    keyword[MagickPathExtent],
2529
377k
    geometry[MagickPathExtent],
2530
377k
    *next_token,
2531
377k
    pattern[MagickPathExtent],
2532
377k
    *primitive,
2533
377k
    *token;
2534
2535
377k
  const char
2536
377k
    *p,
2537
377k
    *q;
2538
2539
377k
  double
2540
377k
    angle,
2541
377k
    coordinates,
2542
377k
    cursor,
2543
377k
    factor,
2544
377k
    primitive_extent;
2545
2546
377k
  DrawInfo
2547
377k
    *clone_info,
2548
377k
    **graphic_context;
2549
2550
377k
  MagickBooleanType
2551
377k
    proceed;
2552
2553
377k
  MagickStatusType
2554
377k
    status;
2555
2556
377k
  MVGInfo
2557
377k
    mvg_info;
2558
2559
377k
  PointInfo
2560
377k
    point;
2561
2562
377k
  PrimitiveInfo
2563
377k
    *primitive_info;
2564
2565
377k
  PrimitiveType
2566
377k
    primitive_type;
2567
2568
377k
  SegmentInfo
2569
377k
    bounds;
2570
2571
377k
  size_t
2572
377k
    extent,
2573
377k
    number_points,
2574
377k
    number_stops;
2575
2576
377k
  SplayTreeInfo
2577
377k
    *macros;
2578
2579
377k
  ssize_t
2580
377k
    classDepth = 0,
2581
377k
    defsDepth,
2582
377k
    i,
2583
377k
    j,
2584
377k
    k,
2585
377k
    n,
2586
377k
    symbolDepth,
2587
377k
    x;
2588
2589
377k
  StopInfo
2590
377k
    *stops;
2591
2592
377k
  TypeMetric
2593
377k
    metrics;
2594
2595
377k
  assert(image != (Image *) NULL);
2596
377k
  assert(image->signature == MagickCoreSignature);
2597
377k
  assert(draw_info != (DrawInfo *) NULL);
2598
377k
  assert(draw_info->signature == MagickCoreSignature);
2599
377k
  if (IsEventLogging() != MagickFalse)
2600
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2601
377k
  if (depth > MagickMaxRecursionDepth)
2602
0
    ThrowBinaryException(DrawError,"VectorGraphicsNestedTooDeeply",
2603
377k
      image->filename);
2604
377k
  if ((draw_info->primitive == (char *) NULL) ||
2605
377k
      (*draw_info->primitive == '\0'))
2606
1.28k
    return(MagickFalse);
2607
376k
  if (draw_info->debug != MagickFalse)
2608
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
2609
376k
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2610
7.42k
    return(MagickFalse);
2611
369k
  if ((image->alpha_trait & BlendPixelTrait) == 0)
2612
10.2k
    {
2613
10.2k
      status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2614
10.2k
      if (status == MagickFalse)
2615
0
        return(MagickFalse);
2616
10.2k
    }
2617
369k
  if ((*draw_info->primitive == '@') && (strlen(draw_info->primitive) > 1) &&
2618
218
      (*(draw_info->primitive+1) != '-') && (depth == 0))
2619
216
    primitive=FileToString(draw_info->primitive,~0UL,exception);
2620
369k
  else
2621
369k
    primitive=AcquireString(draw_info->primitive);
2622
369k
  if (primitive == (char *) NULL)
2623
216
    return(MagickFalse);
2624
369k
  primitive_extent=(double) strlen(primitive);
2625
369k
  (void) SetImageArtifact(image,"mvg:vector-graphics",primitive);
2626
369k
  n=0;
2627
369k
  number_stops=0;
2628
369k
  stops=(StopInfo *) NULL;
2629
  /*
2630
    Allocate primitive info memory.
2631
  */
2632
369k
  graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
2633
369k
  if (graphic_context == (DrawInfo **) NULL)
2634
0
    {
2635
0
      primitive=DestroyString(primitive);
2636
0
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2637
0
        image->filename);
2638
0
    }
2639
369k
  number_points=(size_t) PrimitiveExtentPad;
2640
369k
  primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
2641
369k
    (number_points+1),sizeof(*primitive_info));
2642
369k
  if (primitive_info == (PrimitiveInfo *) NULL)
2643
0
    {
2644
0
      primitive=DestroyString(primitive);
2645
0
      for ( ; n >= 0; n--)
2646
0
        graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
2647
0
      graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
2648
0
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2649
0
        image->filename);
2650
0
    }
2651
369k
  (void) memset(primitive_info,0,(size_t) (number_points+1)*
2652
369k
    sizeof(*primitive_info));
2653
369k
  (void) memset(&mvg_info,0,sizeof(mvg_info));
2654
369k
  mvg_info.primitive_info=(&primitive_info);
2655
369k
  mvg_info.extent=(&number_points);
2656
369k
  mvg_info.exception=exception;
2657
369k
  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2658
369k
  graphic_context[n]->viewbox=image->page;
2659
369k
  if ((image->page.width == 0) || (image->page.height == 0))
2660
369k
    {
2661
369k
      graphic_context[n]->viewbox.width=image->columns;
2662
369k
      graphic_context[n]->viewbox.height=image->rows;
2663
369k
    }
2664
369k
  token=AcquireString(primitive);
2665
369k
  extent=strlen(token)+MagickPathExtent;
2666
369k
  defsDepth=0;
2667
369k
  symbolDepth=0;
2668
369k
  cursor=0.0;
2669
369k
  macros=GetMVGMacros(primitive,exception);
2670
369k
  status=MagickTrue;
2671
8.48M
  for (q=primitive; *q != '\0'; )
2672
8.46M
  {
2673
    /*
2674
      Interpret graphic primitive.
2675
    */
2676
8.46M
    if (GetNextToken(q,&q,MagickPathExtent,keyword) < 1)
2677
0
      break;
2678
8.46M
    if (*keyword == '\0')
2679
175
      break;
2680
8.46M
    if (*keyword == '#')
2681
454k
      {
2682
        /*
2683
          Comment.
2684
        */
2685
18.4M
        while ((*q != '\n') && (*q != '\0'))
2686
18.0M
          q++;
2687
454k
        continue;
2688
454k
      }
2689
8.01M
    p=q-strlen(keyword)-1;
2690
8.01M
    primitive_type=UndefinedPrimitive;
2691
8.01M
    current=graphic_context[n]->affine;
2692
8.01M
    GetAffineMatrix(&affine);
2693
8.01M
    *token='\0';
2694
8.01M
    switch (*keyword)
2695
8.01M
    {
2696
6.11M
      case ';':
2697
6.11M
        break;
2698
13.5k
      case 'a':
2699
20.6k
      case 'A':
2700
20.6k
      {
2701
20.6k
        if (LocaleCompare("affine",keyword) == 0)
2702
9.96k
          {
2703
9.96k
            (void) GetNextToken(q,&q,extent,token);
2704
9.96k
            affine.sx=GetDrawValue(token,&next_token);
2705
9.96k
            if (token == next_token)
2706
5.94k
              ThrowPointExpectedException(token,exception);
2707
5.94k
            (void) GetNextToken(q,&q,extent,token);
2708
5.94k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2709
5.87k
              ThrowPointExpectedException(token,exception);
2710
5.87k
            if (*token == ',')
2711
272
              (void) GetNextToken(q,&q,extent,token);
2712
5.87k
            affine.ry=GetDrawValue(token,&next_token);
2713
5.87k
            if (token == next_token)
2714
5.87k
              ThrowPointExpectedException(token,exception);
2715
5.87k
            (void) GetNextToken(q,&q,extent,token);
2716
5.87k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2717
5.73k
              ThrowPointExpectedException(token,exception);
2718
5.73k
            if (*token == ',')
2719
1.92k
              (void) GetNextToken(q,&q,extent,token);
2720
5.73k
            affine.rx=GetDrawValue(token,&next_token);
2721
5.73k
            if (token == next_token)
2722
5.57k
              ThrowPointExpectedException(token,exception);
2723
5.57k
            (void) GetNextToken(q,&q,extent,token);
2724
5.57k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2725
5.50k
              ThrowPointExpectedException(token,exception);
2726
5.50k
            if (*token == ',')
2727
3.64k
              (void) GetNextToken(q,&q,extent,token);
2728
5.50k
            affine.sy=GetDrawValue(token,&next_token);
2729
5.50k
            if (token == next_token)
2730
5.50k
              ThrowPointExpectedException(token,exception);
2731
5.50k
            (void) GetNextToken(q,&q,extent,token);
2732
5.50k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2733
5.47k
              ThrowPointExpectedException(token,exception);
2734
5.47k
            if (*token == ',')
2735
242
              (void) GetNextToken(q,&q,extent,token);
2736
5.47k
            affine.tx=GetDrawValue(token,&next_token);
2737
5.47k
            if (token == next_token)
2738
5.21k
              ThrowPointExpectedException(token,exception);
2739
5.21k
            (void) GetNextToken(q,&q,extent,token);
2740
5.21k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
2741
4.65k
              ThrowPointExpectedException(token,exception);
2742
4.65k
            if (*token == ',')
2743
4.36k
              (void) GetNextToken(q,&q,extent,token);
2744
4.65k
            affine.ty=GetDrawValue(token,&next_token);
2745
4.65k
            if (token == next_token)
2746
4.65k
              ThrowPointExpectedException(token,exception);
2747
4.65k
            break;
2748
4.65k
          }
2749
10.6k
        if (LocaleCompare("alpha",keyword) == 0)
2750
3.15k
          {
2751
3.15k
            primitive_type=AlphaPrimitive;
2752
3.15k
            break;
2753
3.15k
          }
2754
7.53k
        if (LocaleCompare("arc",keyword) == 0)
2755
7.23k
          {
2756
7.23k
            primitive_type=ArcPrimitive;
2757
7.23k
            break;
2758
7.23k
          }
2759
303
        status=MagickFalse;
2760
303
        break;
2761
7.53k
      }
2762
510
      case 'b':
2763
723
      case 'B':
2764
723
      {
2765
723
        if (LocaleCompare("bezier",keyword) == 0)
2766
342
          {
2767
342
            primitive_type=BezierPrimitive;
2768
342
            break;
2769
342
          }
2770
381
        if (LocaleCompare("border-color",keyword) == 0)
2771
144
          {
2772
144
            (void) GetNextToken(q,&q,extent,token);
2773
144
            status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
2774
144
              &graphic_context[n]->border_color,exception);
2775
144
            break;
2776
144
          }
2777
237
        status=MagickFalse;
2778
237
        break;
2779
381
      }
2780
128k
      case 'c':
2781
129k
      case 'C':
2782
129k
      {
2783
129k
        if (LocaleCompare("class",keyword) == 0)
2784
1.07k
          {
2785
1.07k
            const char
2786
1.07k
              *mvg_class;
2787
2788
1.07k
            (void) GetNextToken(q,&q,extent,token);
2789
1.07k
            if ((*token == '\0') || (*token == ';'))
2790
576
              {
2791
576
                status=MagickFalse;
2792
576
                break;
2793
576
              }
2794
            /*
2795
              Identify recursion.
2796
            */
2797
1.63k
            for (i=0; i <= n; i++)
2798
1.23k
              if (LocaleCompare(token,graphic_context[i]->id) == 0)
2799
96
                break;
2800
498
            if (i <= n)
2801
96
              break;
2802
402
            if (classDepth++ > MagickMaxRecursionDepth)
2803
0
              {
2804
0
                (void) ThrowMagickException(exception,GetMagickModule(),
2805
0
                  DrawError,"VectorGraphicsNestedTooDeeply","`%s'",token);
2806
0
                status=MagickFalse;
2807
0
                break;
2808
0
              }
2809
402
            mvg_class=(const char *) GetValueFromSplayTree(macros,token);
2810
402
            if ((graphic_context[n]->render != MagickFalse) &&
2811
138
                (mvg_class != (const char *) NULL) && (p > primitive))
2812
96
              {
2813
96
                char
2814
96
                  *elements;
2815
2816
96
                ssize_t
2817
96
                  offset;
2818
2819
                /*
2820
                  Inject class elements in stream.
2821
                */
2822
96
                (void) CloneString(&graphic_context[n]->id,token);
2823
96
                offset=(ssize_t) (p-primitive);
2824
96
                elements=AcquireString(primitive);
2825
96
                elements[offset]='\0';
2826
96
                (void) ConcatenateString(&elements,mvg_class);
2827
96
                (void) ConcatenateString(&elements,"\n");
2828
96
                (void) ConcatenateString(&elements,q);
2829
96
                primitive=DestroyString(primitive);
2830
96
                primitive=elements;
2831
96
                q=primitive+offset;
2832
96
              }
2833
402
            break;
2834
402
          }
2835
128k
        if (LocaleCompare("clip-path",keyword) == 0)
2836
119k
          {
2837
119k
            const char
2838
119k
              *clip_path;
2839
2840
            /*
2841
              Take a node from within the MVG document, and duplicate it here.
2842
            */
2843
119k
            (void) GetNextToken(q,&q,extent,token);
2844
119k
            if (*token == '\0')
2845
143
              {
2846
143
                status=MagickFalse;
2847
143
                break;
2848
143
              }
2849
119k
            (void) CloneString(&graphic_context[n]->clip_mask,token);
2850
119k
            clip_path=(const char *) GetValueFromSplayTree(macros,token);
2851
119k
            if (clip_path != (const char *) NULL)
2852
3.71k
              {
2853
3.71k
                if (graphic_context[n]->clipping_mask != (Image *) NULL)
2854
1.49k
                  graphic_context[n]->clipping_mask=
2855
1.49k
                    DestroyImage(graphic_context[n]->clipping_mask);
2856
3.71k
                graphic_context[n]->clipping_mask=DrawClippingMask(image,
2857
3.71k
                  graphic_context[n],token,clip_path,exception);
2858
3.71k
                if (graphic_context[n]->compliance != SVGCompliance)
2859
3.71k
                  {
2860
3.71k
                    clip_path=(const char *) GetValueFromSplayTree(macros,
2861
3.71k
                      graphic_context[n]->clip_mask);
2862
3.71k
                    if (clip_path != (const char *) NULL)
2863
3.71k
                      (void) SetImageArtifact(image,
2864
3.71k
                        graphic_context[n]->clip_mask,clip_path);
2865
3.71k
                    status&=(MagickStatusType) DrawClipPath(image,
2866
3.71k
                      graphic_context[n],graphic_context[n]->clip_mask,
2867
3.71k
                      exception);
2868
3.71k
                  }
2869
3.71k
              }
2870
119k
            break;
2871
119k
          }
2872
8.77k
        if (LocaleCompare("clip-rule",keyword) == 0)
2873
384
          {
2874
384
            ssize_t
2875
384
              fill_rule;
2876
2877
384
            (void) GetNextToken(q,&q,extent,token);
2878
384
            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
2879
384
              token);
2880
384
            if (fill_rule == -1)
2881
128
              {
2882
128
                status=MagickFalse;
2883
128
                break;
2884
128
              }
2885
256
            graphic_context[n]->fill_rule=(FillRule) fill_rule;
2886
256
            break;
2887
384
          }
2888
8.39k
        if (LocaleCompare("clip-units",keyword) == 0)
2889
434
          {
2890
434
            ssize_t
2891
434
              clip_units;
2892
2893
434
            (void) GetNextToken(q,&q,extent,token);
2894
434
            clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
2895
434
              token);
2896
434
            if (clip_units == -1)
2897
258
              {
2898
258
                status=MagickFalse;
2899
258
                break;
2900
258
              }
2901
176
            graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
2902
176
            if (clip_units == ObjectBoundingBox)
2903
0
              {
2904
0
                GetAffineMatrix(&current);
2905
0
                affine.sx=draw_info->bounds.x2;
2906
0
                affine.sy=draw_info->bounds.y2;
2907
0
                affine.tx=draw_info->bounds.x1;
2908
0
                affine.ty=draw_info->bounds.y1;
2909
0
                break;
2910
0
              }
2911
176
            break;
2912
176
          }
2913
7.95k
        if (LocaleCompare("circle",keyword) == 0)
2914
558
          {
2915
558
            primitive_type=CirclePrimitive;
2916
558
            break;
2917
558
          }
2918
7.39k
        if (LocaleCompare("color",keyword) == 0)
2919
5.42k
          {
2920
5.42k
            primitive_type=ColorPrimitive;
2921
5.42k
            break;
2922
5.42k
          }
2923
1.96k
        if (LocaleCompare("compliance",keyword) == 0)
2924
480
          {
2925
            /*
2926
              MVG compliance associates a clipping mask with an image; SVG
2927
              compliance associates a clipping mask with a graphics context.
2928
            */
2929
480
            (void) GetNextToken(q,&q,extent,token);
2930
480
            graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
2931
480
              MagickComplianceOptions,MagickFalse,token);
2932
480
            break;
2933
480
          }
2934
1.48k
        if (LocaleCompare("currentColor",keyword) == 0)
2935
0
          {
2936
0
            (void) GetNextToken(q,&q,extent,token);
2937
0
            break;
2938
0
          }
2939
1.48k
        status=MagickFalse;
2940
1.48k
        break;
2941
1.48k
      }
2942
182
      case 'd':
2943
5.58k
      case 'D':
2944
5.58k
      {
2945
5.58k
        if (LocaleCompare("decorate",keyword) == 0)
2946
167
          {
2947
167
            ssize_t
2948
167
              decorate;
2949
2950
167
            (void) GetNextToken(q,&q,extent,token);
2951
167
            decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
2952
167
              token);
2953
167
            if (decorate == -1)
2954
167
              {
2955
167
                status=MagickFalse;
2956
167
                break;
2957
167
              }
2958
0
            graphic_context[n]->decorate=(DecorationType) decorate;
2959
0
            break;
2960
167
          }
2961
5.41k
        if (LocaleCompare("density",keyword) == 0)
2962
5.40k
          {
2963
5.40k
            (void) GetNextToken(q,&q,extent,token);
2964
5.40k
            (void) CloneString(&graphic_context[n]->density,token);
2965
5.40k
            break;
2966
5.40k
          }
2967
16
        if (LocaleCompare("direction",keyword) == 0)
2968
0
          {
2969
0
            ssize_t
2970
0
              direction;
2971
2972
0
            (void) GetNextToken(q,&q,extent,token);
2973
0
            direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
2974
0
              token);
2975
0
            if (direction == -1)
2976
0
              status=MagickFalse;
2977
0
            else
2978
0
              graphic_context[n]->direction=(DirectionType) direction;
2979
0
            break;
2980
0
          }
2981
16
        status=MagickFalse;
2982
16
        break;
2983
16
      }
2984
886
      case 'e':
2985
1.16k
      case 'E':
2986
1.16k
      {
2987
1.16k
        if (LocaleCompare("ellipse",keyword) == 0)
2988
468
          {
2989
468
            primitive_type=EllipsePrimitive;
2990
468
            break;
2991
468
          }
2992
698
        if (LocaleCompare("encoding",keyword) == 0)
2993
177
          {
2994
177
            (void) GetNextToken(q,&q,extent,token);
2995
177
            (void) CloneString(&graphic_context[n]->encoding,token);
2996
177
            break;
2997
177
          }
2998
521
        status=MagickFalse;
2999
521
        break;
3000
698
      }
3001
104k
      case 'f':
3002
104k
      case 'F':
3003
104k
      {
3004
104k
        if (LocaleCompare("fill",keyword) == 0)
3005
13.8k
          {
3006
13.8k
            const char
3007
13.8k
              *mvg_class;
3008
3009
13.8k
            (void) GetNextToken(q,&q,extent,token);
3010
13.8k
            if (graphic_context[n]->clip_path != MagickFalse)
3011
1.96k
              break;
3012
11.9k
            mvg_class=(const char *) GetValueFromSplayTree(macros,token);
3013
11.9k
            if (mvg_class != (const char *) NULL)
3014
281
              {
3015
281
                (void) DrawPatternPath(image,draw_info,mvg_class,
3016
281
                  &graphic_context[n]->fill_pattern,exception);
3017
281
                break;
3018
281
              }
3019
11.6k
            (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3020
11.6k
            if (GetImageArtifact(image,pattern) != (const char *) NULL)
3021
9.44k
              {
3022
9.44k
                (void) DrawPatternPath(image,draw_info,token,
3023
9.44k
                  &graphic_context[n]->fill_pattern,exception);
3024
9.44k
                break;
3025
9.44k
              }
3026
2.18k
            status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3027
2.18k
              &graphic_context[n]->fill,exception);
3028
2.18k
            if (graphic_context[n]->fill_alpha != (double) OpaqueAlpha)
3029
747
              graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3030
2.18k
            break;
3031
11.6k
          }
3032
90.7k
        if (LocaleCompare("fill-opacity",keyword) == 0)
3033
760
          {
3034
760
            double
3035
760
              opacity;
3036
3037
760
            (void) GetNextToken(q,&q,extent,token);
3038
760
            if (graphic_context[n]->clip_path != MagickFalse)
3039
36
              break;
3040
724
            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3041
724
            opacity=MagickMin(MagickMax(factor*
3042
724
              GetDrawValue(token,&next_token),0.0),1.0);
3043
724
            if (token == next_token)
3044
596
              ThrowPointExpectedException(token,exception);
3045
596
            if (graphic_context[n]->compliance == SVGCompliance)
3046
0
              graphic_context[n]->fill_alpha*=opacity;
3047
596
            else
3048
596
              graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3049
596
            if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3050
385
              graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3051
211
            else
3052
211
              graphic_context[n]->fill.alpha=(MagickRealType)
3053
211
                ClampToQuantum((double) QuantumRange*opacity);
3054
596
            graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3055
596
            break;
3056
724
          }
3057
90.0k
        if (LocaleCompare("fill-rule",keyword) == 0)
3058
192
          {
3059
192
            ssize_t
3060
192
              fill_rule;
3061
3062
192
            (void) GetNextToken(q,&q,extent,token);
3063
192
            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
3064
192
              token);
3065
192
            if (fill_rule == -1)
3066
128
              {
3067
128
                status=MagickFalse;
3068
128
                break;
3069
128
              }
3070
64
            graphic_context[n]->fill_rule=(FillRule) fill_rule;
3071
64
            break;
3072
192
          }
3073
89.8k
        if (LocaleCompare("font",keyword) == 0)
3074
13.3k
          {
3075
13.3k
            (void) GetNextToken(q,&q,extent,token);
3076
13.3k
            (void) CloneString(&graphic_context[n]->font,token);
3077
13.3k
            if (LocaleCompare("none",token) == 0)
3078
24
              graphic_context[n]->font=(char *) RelinquishMagickMemory(
3079
24
                graphic_context[n]->font);
3080
13.3k
            break;
3081
13.3k
          }
3082
76.5k
        if (LocaleCompare("font-family",keyword) == 0)
3083
2.49k
          {
3084
2.49k
            (void) GetNextToken(q,&q,extent,token);
3085
2.49k
            (void) CloneString(&graphic_context[n]->family,token);
3086
2.49k
            break;
3087
2.49k
          }
3088
74.0k
        if (LocaleCompare("font-size",keyword) == 0)
3089
67.9k
          {
3090
67.9k
            (void) GetNextToken(q,&q,extent,token);
3091
67.9k
            graphic_context[n]->pointsize=GetDrawValue(token,&next_token);
3092
67.9k
            if (token == next_token)
3093
67.8k
              ThrowPointExpectedException(token,exception);
3094
67.8k
            break;
3095
67.9k
          }
3096
6.10k
        if (LocaleCompare("font-stretch",keyword) == 0)
3097
496
          {
3098
496
            ssize_t
3099
496
              stretch;
3100
3101
496
            (void) GetNextToken(q,&q,extent,token);
3102
496
            stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
3103
496
            if (stretch == -1)
3104
280
              {
3105
280
                status=MagickFalse;
3106
280
                break;
3107
280
              }
3108
216
            graphic_context[n]->stretch=(StretchType) stretch;
3109
216
            break;
3110
496
          }
3111
5.61k
        if (LocaleCompare("font-style",keyword) == 0)
3112
127
          {
3113
127
            ssize_t
3114
127
              style;
3115
3116
127
            (void) GetNextToken(q,&q,extent,token);
3117
127
            style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
3118
127
            if (style == -1)
3119
64
              {
3120
64
                status=MagickFalse;
3121
64
                break;
3122
64
              }
3123
63
            graphic_context[n]->style=(StyleType) style;
3124
63
            break;
3125
127
          }
3126
5.48k
        if (LocaleCompare("font-weight",keyword) == 0)
3127
4.72k
          {
3128
4.72k
            ssize_t
3129
4.72k
              weight;
3130
3131
4.72k
            (void) GetNextToken(q,&q,extent,token);
3132
4.72k
            weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
3133
4.72k
            if (weight == -1)
3134
4.53k
              weight=(ssize_t) StringToUnsignedLong(token);
3135
4.72k
            graphic_context[n]->weight=(size_t) weight;
3136
4.72k
            break;
3137
4.72k
          }
3138
766
        status=MagickFalse;
3139
766
        break;
3140
5.48k
      }
3141
1.88k
      case 'g':
3142
2.01k
      case 'G':
3143
2.01k
      {
3144
2.01k
        if (LocaleCompare("gradient-units",keyword) == 0)
3145
812
          {
3146
812
            (void) GetNextToken(q,&q,extent,token);
3147
812
            break;
3148
812
          }
3149
1.19k
        if (LocaleCompare("gravity",keyword) == 0)
3150
323
          {
3151
323
            ssize_t
3152
323
              gravity;
3153
3154
323
            (void) GetNextToken(q,&q,extent,token);
3155
323
            gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
3156
323
            if (gravity == -1)
3157
218
              {
3158
218
                status=MagickFalse;
3159
218
                break;
3160
218
              }
3161
105
            graphic_context[n]->gravity=(GravityType) gravity;
3162
105
            break;
3163
323
          }
3164
876
        status=MagickFalse;
3165
876
        break;
3166
1.19k
      }
3167
283k
      case 'i':
3168
295k
      case 'I':
3169
295k
      {
3170
295k
        if (LocaleCompare("image",keyword) == 0)
3171
293k
          {
3172
293k
            ssize_t
3173
293k
              compose;
3174
3175
293k
            primitive_type=ImagePrimitive;
3176
293k
            (void) GetNextToken(q,&q,extent,token);
3177
293k
            compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
3178
293k
            if (compose == -1)
3179
667
              {
3180
667
                status=MagickFalse;
3181
667
                break;
3182
667
              }
3183
293k
            graphic_context[n]->compose=(CompositeOperator) compose;
3184
293k
            break;
3185
293k
          }
3186
1.81k
        if (LocaleCompare("interline-spacing",keyword) == 0)
3187
515
          {
3188
515
            (void) GetNextToken(q,&q,extent,token);
3189
515
            graphic_context[n]->interline_spacing=GetDrawValue(token,
3190
515
              &next_token);
3191
515
            if (token == next_token)
3192
335
              ThrowPointExpectedException(token,exception);
3193
335
            break;
3194
515
          }
3195
1.30k
        if (LocaleCompare("interword-spacing",keyword) == 0)
3196
1.02k
          {
3197
1.02k
            (void) GetNextToken(q,&q,extent,token);
3198
1.02k
            graphic_context[n]->interword_spacing=GetDrawValue(token,
3199
1.02k
              &next_token);
3200
1.02k
            if (token == next_token)
3201
890
              ThrowPointExpectedException(token,exception);
3202
890
            break;
3203
1.02k
          }
3204
280
        status=MagickFalse;
3205
280
        break;
3206
1.30k
      }
3207
466
      case 'k':
3208
717
      case 'K':
3209
717
      {
3210
717
        if (LocaleCompare("kerning",keyword) == 0)
3211
280
          {
3212
280
            (void) GetNextToken(q,&q,extent,token);
3213
280
            graphic_context[n]->kerning=GetDrawValue(token,&next_token);
3214
280
            if (token == next_token)
3215
192
              ThrowPointExpectedException(token,exception);
3216
88
            break;
3217
280
          }
3218
437
        status=MagickFalse;
3219
437
        break;
3220
717
      }
3221
14.0k
      case 'l':
3222
14.4k
      case 'L':
3223
14.4k
      {
3224
14.4k
        if (LocaleCompare("letter-spacing",keyword) == 0)
3225
94
          {
3226
94
            (void) GetNextToken(q,&q,extent,token);
3227
94
            if (IsValidPoint(token) == MagickFalse)
3228
24
              break;
3229
70
            clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
3230
70
            clone_info->text=AcquireString(" ");
3231
70
            status&=(MagickStatusType) GetTypeMetrics(image,clone_info,&metrics,
3232
70
              exception);
3233
70
            graphic_context[n]->kerning=metrics.width*
3234
70
              GetDrawValue(token,&next_token);
3235
70
            clone_info=DestroyDrawInfo(clone_info);
3236
70
            if (token == next_token)
3237
70
              ThrowPointExpectedException(token,exception);
3238
70
            break;
3239
70
          }
3240
14.3k
        if (LocaleCompare("line",keyword) == 0)
3241
14.3k
          {
3242
14.3k
            primitive_type=LinePrimitive;
3243
14.3k
            break;
3244
14.3k
          }
3245
48
        status=MagickFalse;
3246
48
        break;
3247
14.3k
      }
3248
430k
      case 'm':
3249
430k
      case 'M':
3250
430k
      {
3251
430k
        if (LocaleCompare("mask",keyword) == 0)
3252
430k
          {
3253
430k
            const char
3254
430k
              *mask_path;
3255
3256
            /*
3257
              Take a node from within the MVG document, and duplicate it here.
3258
            */
3259
430k
            (void) GetNextToken(q,&q,extent,token);
3260
430k
            mask_path=(const char *) GetValueFromSplayTree(macros,token);
3261
430k
            if (mask_path != (const char *) NULL)
3262
343k
              {
3263
343k
                if (graphic_context[n]->composite_mask != (Image *) NULL)
3264
173k
                  graphic_context[n]->composite_mask=
3265
173k
                    DestroyImage(graphic_context[n]->composite_mask);
3266
343k
                graphic_context[n]->composite_mask=DrawCompositeMask(image,
3267
343k
                  graphic_context[n],token,mask_path,exception);
3268
343k
                if (graphic_context[n]->compliance != SVGCompliance)
3269
343k
                  status=SetImageMask(image,CompositePixelMask,
3270
343k
                    graphic_context[n]->composite_mask,exception);
3271
343k
              }
3272
430k
            break;
3273
430k
          }
3274
89
        status=MagickFalse;
3275
89
        break;
3276
430k
      }
3277
18.7k
      case 'o':
3278
18.9k
      case 'O':
3279
18.9k
      {
3280
18.9k
        if (LocaleCompare("offset",keyword) == 0)
3281
15.5k
          {
3282
15.5k
            (void) GetNextToken(q,&q,extent,token);
3283
15.5k
            break;
3284
15.5k
          }
3285
3.37k
        if (LocaleCompare("opacity",keyword) == 0)
3286
3.09k
          {
3287
3.09k
            double
3288
3.09k
              opacity;
3289
3290
3.09k
            (void) GetNextToken(q,&q,extent,token);
3291
3.09k
            if (graphic_context[n]->clip_path != MagickFalse)
3292
272
              break;
3293
2.82k
            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3294
2.82k
            opacity=MagickMin(MagickMax(factor*
3295
2.82k
              GetDrawValue(token,&next_token),0.0),1.0);
3296
2.82k
            if (token == next_token)
3297
1.59k
              ThrowPointExpectedException(token,exception);
3298
1.59k
            if (graphic_context[n]->compliance == SVGCompliance)
3299
0
              {
3300
0
                graphic_context[n]->fill_alpha*=opacity;
3301
0
                graphic_context[n]->stroke_alpha*=opacity;
3302
0
              }
3303
1.59k
            else
3304
1.59k
              {
3305
1.59k
                graphic_context[n]->fill_alpha=(double) QuantumRange*opacity;
3306
1.59k
                graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3307
1.59k
              }
3308
1.59k
            if (graphic_context[n]->fill.alpha != (double) TransparentAlpha)
3309
1.38k
              {
3310
1.38k
                graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
3311
1.38k
                graphic_context[n]->stroke.alpha=
3312
1.38k
                  graphic_context[n]->stroke_alpha;
3313
1.38k
              }
3314
206
            else
3315
206
              {
3316
206
                graphic_context[n]->fill.alpha=(MagickRealType)
3317
206
                  ClampToQuantum((double) QuantumRange*opacity);
3318
206
                graphic_context[n]->stroke.alpha=(MagickRealType)
3319
206
                  ClampToQuantum((double) QuantumRange*opacity);
3320
206
              }
3321
1.59k
            graphic_context[n]->fill.alpha_trait=BlendPixelTrait;
3322
1.59k
            graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3323
1.59k
            break;
3324
2.82k
          }
3325
278
        status=MagickFalse;
3326
278
        break;
3327
3.37k
      }
3328
59.7k
      case 'p':
3329
63.6k
      case 'P':
3330
63.6k
      {
3331
63.6k
        if (LocaleCompare("path",keyword) == 0)
3332
5.99k
          {
3333
5.99k
            primitive_type=PathPrimitive;
3334
5.99k
            break;
3335
5.99k
          }
3336
57.6k
        if (LocaleCompare("point",keyword) == 0)
3337
6.71k
          {
3338
6.71k
            primitive_type=PointPrimitive;
3339
6.71k
            break;
3340
6.71k
          }
3341
50.9k
        if (LocaleCompare("polyline",keyword) == 0)
3342
342
          {
3343
342
            primitive_type=PolylinePrimitive;
3344
342
            break;
3345
342
          }
3346
50.5k
        if (LocaleCompare("polygon",keyword) == 0)
3347
205
          {
3348
205
            primitive_type=PolygonPrimitive;
3349
205
            break;
3350
205
          }
3351
50.3k
        if (LocaleCompare("pop",keyword) == 0)
3352
13.8k
          {
3353
13.8k
            if (GetNextToken(q,&q,extent,token) < 1)
3354
0
              break;
3355
13.8k
            if (LocaleCompare("class",token) == 0)
3356
672
              break;
3357
13.1k
            if (LocaleCompare("clip-path",token) == 0)
3358
1.09k
              break;
3359
12.0k
            if (LocaleCompare("defs",token) == 0)
3360
78
              {
3361
78
                defsDepth--;
3362
78
                graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3363
78
                  MagickTrue;
3364
78
                break;
3365
78
              }
3366
11.9k
            if (LocaleCompare("gradient",token) == 0)
3367
811
              break;
3368
11.1k
            if (LocaleCompare("graphic-context",token) == 0)
3369
4.70k
              {
3370
4.70k
                if (n <= 0)
3371
3
                  {
3372
3
                    (void) ThrowMagickException(exception,GetMagickModule(),
3373
3
                      DrawError,"UnbalancedGraphicContextPushPop","`%s'",token);
3374
3
                    status=MagickFalse;
3375
3
                    n=0;
3376
3
                    break;
3377
3
                  }
3378
4.70k
                if ((graphic_context[n]->clip_mask != (char *) NULL) &&
3379
288
                    (graphic_context[n]->compliance != SVGCompliance))
3380
288
                  if (LocaleCompare(graphic_context[n]->clip_mask,
3381
288
                      graphic_context[n-1]->clip_mask) != 0)
3382
73
                    status=SetImageMask(image,WritePixelMask,(Image *) NULL,
3383
73
                      exception);
3384
4.70k
                graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
3385
4.70k
                n--;
3386
4.70k
                break;
3387
4.70k
              }
3388
6.47k
            if (LocaleCompare("mask",token) == 0)
3389
140
              break;
3390
6.33k
            if (LocaleCompare("pattern",token) == 0)
3391
3.84k
              break;
3392
2.48k
            if (LocaleCompare("symbol",token) == 0)
3393
967
              {
3394
967
                symbolDepth--;
3395
967
                graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3396
967
                  MagickTrue;
3397
967
                break;
3398
967
              }
3399
1.52k
            status=MagickFalse;
3400
1.52k
            break;
3401
2.48k
          }
3402
36.5k
        if (LocaleCompare("push",keyword) == 0)
3403
34.7k
          {
3404
34.7k
            if (GetNextToken(q,&q,extent,token) < 1)
3405
0
              break;
3406
34.7k
            if (LocaleCompare("class",token) == 0)
3407
325
              {
3408
                /*
3409
                  Class context.
3410
                */
3411
26.0k
                for (p=q; *q != '\0'; )
3412
26.0k
                {
3413
26.0k
                  if (GetNextToken(q,&q,extent,token) < 1)
3414
0
                    break;
3415
26.0k
                  if (LocaleCompare(token,"pop") != 0)
3416
25.4k
                    continue;
3417
562
                  (void) GetNextToken(q,(const char **) NULL,extent,token);
3418
562
                  if (LocaleCompare(token,"class") != 0)
3419
296
                    continue;
3420
266
                  break;
3421
562
                }
3422
325
                (void) GetNextToken(q,&q,extent,token);
3423
325
                break;
3424
325
              }
3425
34.3k
            if (LocaleCompare("clip-path",token) == 0)
3426
494
              {
3427
494
                (void) GetNextToken(q,&q,extent,token);
3428
9.75k
                for (p=q; *q != '\0'; )
3429
9.45k
                {
3430
9.45k
                  if (GetNextToken(q,&q,extent,token) < 1)
3431
0
                    break;
3432
9.45k
                  if (LocaleCompare(token,"pop") != 0)
3433
8.97k
                    continue;
3434
484
                  (void) GetNextToken(q,(const char **) NULL,extent,token);
3435
484
                  if (LocaleCompare(token,"clip-path") != 0)
3436
295
                    continue;
3437
189
                  break;
3438
484
                }
3439
494
                if ((q == (char *) NULL) || (p == (char *) NULL) || ((q-4) < p))
3440
220
                  {
3441
220
                    status=MagickFalse;
3442
220
                    break;
3443
220
                  }
3444
274
                (void) GetNextToken(q,&q,extent,token);
3445
274
                break;
3446
494
              }
3447
33.9k
            if (LocaleCompare("defs",token) == 0)
3448
1.94k
              {
3449
1.94k
                defsDepth++;
3450
1.94k
                graphic_context[n]->render=defsDepth > 0 ? MagickFalse :
3451
1.94k
                  MagickTrue;
3452
1.94k
                break;
3453
1.94k
              }
3454
31.9k
            if (LocaleCompare("gradient",token) == 0)
3455
17.1k
              {
3456
17.1k
                char
3457
17.1k
                  key[2*MagickPathExtent],
3458
17.1k
                  name[MagickPathExtent],
3459
17.1k
                  type[MagickPathExtent];
3460
3461
17.1k
                SegmentInfo
3462
17.1k
                  segment;
3463
3464
17.1k
                (void) GetNextToken(q,&q,extent,token);
3465
17.1k
                (void) CopyMagickString(name,token,MagickPathExtent);
3466
17.1k
                (void) GetNextToken(q,&q,extent,token);
3467
17.1k
                (void) CopyMagickString(type,token,MagickPathExtent);
3468
17.1k
                (void) GetNextToken(q,&q,extent,token);
3469
17.1k
                segment.x1=GetDrawValue(token,&next_token);
3470
17.1k
                if (token == next_token)
3471
17.0k
                  ThrowPointExpectedException(token,exception);
3472
17.0k
                (void) GetNextToken(q,&q,extent,token);
3473
17.0k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3474
16.8k
                  ThrowPointExpectedException(token,exception);
3475
16.8k
                if (*token == ',')
3476
269
                  (void) GetNextToken(q,&q,extent,token);
3477
16.8k
                segment.y1=GetDrawValue(token,&next_token);
3478
16.8k
                if (token == next_token)
3479
16.8k
                  ThrowPointExpectedException(token,exception);
3480
16.8k
                (void) GetNextToken(q,&q,extent,token);
3481
16.8k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3482
16.6k
                  ThrowPointExpectedException(token,exception);
3483
16.6k
                if (*token == ',')
3484
160
                  (void) GetNextToken(q,&q,extent,token);
3485
16.6k
                segment.x2=GetDrawValue(token,&next_token);
3486
16.6k
                if (token == next_token)
3487
16.6k
                  ThrowPointExpectedException(token,exception);
3488
16.6k
                (void) GetNextToken(q,&q,extent,token);
3489
16.6k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3490
16.2k
                  ThrowPointExpectedException(token,exception);
3491
16.2k
                if (*token == ',')
3492
287
                  (void) GetNextToken(q,&q,extent,token);
3493
16.2k
                segment.y2=GetDrawValue(token,&next_token);
3494
16.2k
                if (token == next_token)
3495
16.2k
                  ThrowPointExpectedException(token,exception);
3496
16.2k
                if (LocaleCompare(type,"radial") == 0)
3497
266
                  {
3498
266
                    (void) GetNextToken(q,&q,extent,token);
3499
266
                    if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3500
266
                      ThrowPointExpectedException(token,exception);
3501
0
                    if (*token == ',')
3502
0
                      (void) GetNextToken(q,&q,extent,token);
3503
0
                  }
3504
629k
                for (p=q; *q != '\0'; )
3505
628k
                {
3506
628k
                  if (GetNextToken(q,&q,extent,token) < 1)
3507
0
                    break;
3508
628k
                  if (LocaleCompare(token,"pop") != 0)
3509
610k
                    continue;
3510
18.7k
                  (void) GetNextToken(q,(const char **) NULL,extent,token);
3511
18.7k
                  if (LocaleCompare(token,"gradient") != 0)
3512
3.55k
                    continue;
3513
15.2k
                  break;
3514
18.7k
                }
3515
16.0k
                if ((q == (char *) NULL) || (*q == '\0') ||
3516
15.2k
                    (p == (char *) NULL) || ((q-4) < p) ||
3517
15.2k
                    ((size_t) (q-p+4+1) > extent))
3518
792
                  {
3519
792
                    status=MagickFalse;
3520
792
                    break;
3521
792
                  }
3522
15.2k
                (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3523
15.2k
                bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3524
15.2k
                  graphic_context[n]->affine.ry*segment.y1+
3525
15.2k
                  graphic_context[n]->affine.tx;
3526
15.2k
                bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3527
15.2k
                  graphic_context[n]->affine.sy*segment.y1+
3528
15.2k
                  graphic_context[n]->affine.ty;
3529
15.2k
                bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3530
15.2k
                  graphic_context[n]->affine.ry*segment.y2+
3531
15.2k
                  graphic_context[n]->affine.tx;
3532
15.2k
                bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3533
15.2k
                  graphic_context[n]->affine.sy*segment.y2+
3534
15.2k
                  graphic_context[n]->affine.ty;
3535
15.2k
                (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3536
15.2k
                (void) SetImageArtifact(image,key,token);
3537
15.2k
                (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name);
3538
15.2k
                (void) SetImageArtifact(image,key,type);
3539
15.2k
                (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3540
15.2k
                  name);
3541
15.2k
                (void) FormatLocaleString(geometry,MagickPathExtent,
3542
15.2k
                  "%gx%g%+.15g%+.15g",
3543
15.2k
                  MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
3544
15.2k
                  MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
3545
15.2k
                  bounds.x1,bounds.y1);
3546
15.2k
                (void) SetImageArtifact(image,key,geometry);
3547
15.2k
                (void) GetNextToken(q,&q,extent,token);
3548
15.2k
                break;
3549
16.0k
              }
3550
14.8k
            if (LocaleCompare("graphic-context",token) == 0)
3551
9.80k
              {
3552
9.80k
                n++;
3553
9.80k
                graphic_context=(DrawInfo **) ResizeQuantumMemory(
3554
9.80k
                  graphic_context,(size_t) (n+1),sizeof(*graphic_context));
3555
9.80k
                if (graphic_context == (DrawInfo **) NULL)
3556
0
                  {
3557
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
3558
0
                      ResourceLimitError,"MemoryAllocationFailed","`%s'",
3559
0
                      image->filename);
3560
0
                    status=MagickFalse;
3561
0
                    break;
3562
0
                  }
3563
9.80k
                graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
3564
9.80k
                  graphic_context[n-1]);
3565
9.80k
                if (*q == '"')
3566
1.36k
                  {
3567
1.36k
                    (void) GetNextToken(q,&q,extent,token);
3568
1.36k
                    (void) CloneString(&graphic_context[n]->id,token);
3569
1.36k
                  }
3570
9.80k
                if (n > MagickMaxRecursionDepth)
3571
0
                  {
3572
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
3573
0
                      DrawError,"VectorGraphicsNestedTooDeeply","`%s'",
3574
0
                      image->filename);
3575
0
                    status=MagickFalse;
3576
0
                  }
3577
9.80k
                break;
3578
9.80k
              }
3579
5.00k
            if (LocaleCompare("mask",token) == 0)
3580
3.08k
              {
3581
3.08k
                (void) GetNextToken(q,&q,extent,token);
3582
3.08k
                break;
3583
3.08k
              }
3584
1.92k
            if (LocaleCompare("pattern",token) == 0)
3585
1.80k
              {
3586
1.80k
                char
3587
1.80k
                  key[2*MagickPathExtent],
3588
1.80k
                  name[MagickPathExtent];
3589
3590
1.80k
                RectangleInfo
3591
1.80k
                  region;
3592
3593
1.80k
                (void) GetNextToken(q,&q,extent,token);
3594
1.80k
                (void) CopyMagickString(name,token,MagickPathExtent);
3595
1.80k
                (void) GetNextToken(q,&q,extent,token);
3596
1.80k
                region.x=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3597
1.80k
                  &next_token)-0.5));
3598
1.80k
                if (token == next_token)
3599
1.61k
                  ThrowPointExpectedException(token,exception);
3600
1.61k
                (void) GetNextToken(q,&q,extent,token);
3601
1.61k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3602
1.39k
                  ThrowPointExpectedException(token,exception);
3603
1.39k
                if (*token == ',')
3604
197
                  (void) GetNextToken(q,&q,extent,token);
3605
1.39k
                region.y=CastDoubleToSsizeT(ceil(GetDrawValue(token,
3606
1.39k
                  &next_token)-0.5));
3607
1.39k
                if (token == next_token)
3608
1.39k
                  ThrowPointExpectedException(token,exception);
3609
1.39k
                (void) GetNextToken(q,&q,extent,token);
3610
1.39k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3611
934
                  ThrowPointExpectedException(token,exception);
3612
934
                if (*token == ',')
3613
22
                  (void) GetNextToken(q,&q,extent,token);
3614
934
                region.width=CastDoubleToSizeT(floor(GetDrawValue(token,
3615
934
                  &next_token)+0.5));
3616
934
                if (token == next_token)
3617
934
                  ThrowPointExpectedException(token,exception);
3618
934
                (void) GetNextToken(q,&q,extent,token);
3619
934
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3620
737
                  ThrowPointExpectedException(token,exception);
3621
737
                if (*token == ',')
3622
731
                  (void) GetNextToken(q,&q,extent,token);
3623
737
                region.height=CastDoubleToSizeT(GetDrawValue(token,
3624
737
                  &next_token)+0.5);
3625
737
                if (token == next_token)
3626
737
                  ThrowPointExpectedException(token,exception);
3627
11.5k
                for (p=q; *q != '\0'; )
3628
11.2k
                {
3629
11.2k
                  if (GetNextToken(q,&q,extent,token) < 1)
3630
0
                    break;
3631
11.2k
                  if (LocaleCompare(token,"pop") != 0)
3632
10.2k
                    continue;
3633
999
                  (void) GetNextToken(q,(const char **) NULL,extent,token);
3634
999
                  if (LocaleCompare(token,"pattern") != 0)
3635
524
                    continue;
3636
475
                  break;
3637
999
                }
3638
737
                if ((q == (char *) NULL) || (p == (char *) NULL) ||
3639
737
                    ((q-4) < p) || ((size_t) (q-p+4+1) > extent))
3640
0
                  {
3641
0
                    status=MagickFalse;
3642
0
                    break;
3643
0
                  }
3644
737
                (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
3645
737
                (void) FormatLocaleString(key,MagickPathExtent,"%s",name);
3646
737
                (void) SetImageArtifact(image,key,token);
3647
737
                (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry",
3648
737
                  name);
3649
737
                (void) FormatLocaleString(geometry,MagickPathExtent,
3650
737
                  "%.17gx%.17g%+.20g%+.20g",(double) region.width,(double)
3651
737
                  region.height,(double) region.x,(double) region.y);
3652
737
                (void) SetImageArtifact(image,key,geometry);
3653
737
                (void) GetNextToken(q,&q,extent,token);
3654
737
                break;
3655
737
              }
3656
126
            if (LocaleCompare("symbol",token) == 0)
3657
54
              {
3658
54
                symbolDepth++;
3659
54
                graphic_context[n]->render=symbolDepth > 0 ? MagickFalse :
3660
54
                  MagickTrue;
3661
54
                break;
3662
54
              }
3663
72
            status=MagickFalse;
3664
72
            break;
3665
126
          }
3666
1.81k
        status=MagickFalse;
3667
1.81k
        break;
3668
36.5k
      }
3669
953
      case 'r':
3670
1.34k
      case 'R':
3671
1.34k
      {
3672
1.34k
        if (LocaleCompare("rectangle",keyword) == 0)
3673
132
          {
3674
132
            primitive_type=RectanglePrimitive;
3675
132
            break;
3676
132
          }
3677
1.21k
        if (LocaleCompare("rotate",keyword) == 0)
3678
358
          {
3679
358
            (void) GetNextToken(q,&q,extent,token);
3680
358
            angle=GetDrawValue(token,&next_token);
3681
358
            if (token == next_token)
3682
192
              ThrowPointExpectedException(token,exception);
3683
166
            affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
3684
166
            affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
3685
166
            affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
3686
166
            affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
3687
166
            break;
3688
358
          }
3689
855
        if (LocaleCompare("roundRectangle",keyword) == 0)
3690
537
          {
3691
537
            primitive_type=RoundRectanglePrimitive;
3692
537
            break;
3693
537
          }
3694
318
        status=MagickFalse;
3695
318
        break;
3696
855
      }
3697
532k
      case 's':
3698
534k
      case 'S':
3699
534k
      {
3700
534k
        if (LocaleCompare("scale",keyword) == 0)
3701
4.63k
          {
3702
4.63k
            (void) GetNextToken(q,&q,extent,token);
3703
4.63k
            affine.sx=GetDrawValue(token,&next_token);
3704
4.63k
            if (token == next_token)
3705
4.63k
              ThrowPointExpectedException(token,exception);
3706
4.63k
            (void) GetNextToken(q,&q,extent,token);
3707
4.63k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3708
4.50k
              ThrowPointExpectedException(token,exception);
3709
4.50k
            if (*token == ',')
3710
225
              (void) GetNextToken(q,&q,extent,token);
3711
4.50k
            affine.sy=GetDrawValue(token,&next_token);
3712
4.50k
            if (token == next_token)
3713
4.31k
              ThrowPointExpectedException(token,exception);
3714
4.31k
            break;
3715
4.50k
          }
3716
529k
        if (LocaleCompare("skewX",keyword) == 0)
3717
812
          {
3718
812
            (void) GetNextToken(q,&q,extent,token);
3719
812
            angle=GetDrawValue(token,&next_token);
3720
812
            if (token == next_token)
3721
680
              ThrowPointExpectedException(token,exception);
3722
680
            affine.ry=sin(DegreesToRadians(angle));
3723
680
            break;
3724
812
          }
3725
528k
        if (LocaleCompare("skewY",keyword) == 0)
3726
17
          {
3727
17
            (void) GetNextToken(q,&q,extent,token);
3728
17
            angle=GetDrawValue(token,&next_token);
3729
17
            if (token == next_token)
3730
17
              ThrowPointExpectedException(token,exception);
3731
17
            affine.rx=(-tan(DegreesToRadians(angle)/2.0));
3732
17
            break;
3733
17
          }
3734
528k
        if (LocaleCompare("stop-color",keyword) == 0)
3735
3.73k
          {
3736
3.73k
            PixelInfo
3737
3.73k
              stop_color;
3738
3739
3.73k
            number_stops++;
3740
3.73k
            if (number_stops == 1)
3741
1.93k
              stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops));
3742
1.80k
            else
3743
1.80k
              if (number_stops > 2)
3744
1
                stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops,
3745
1
                  sizeof(*stops));
3746
3.73k
            if (stops == (StopInfo *) NULL)
3747
0
              {
3748
0
                (void) ThrowMagickException(exception,GetMagickModule(),
3749
0
                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
3750
0
                  image->filename);
3751
0
                status=MagickFalse;
3752
0
                break;
3753
0
              }
3754
3.73k
            (void) GetNextToken(q,&q,extent,token);
3755
3.73k
            status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3756
3.73k
              &stop_color,exception);
3757
3.73k
            stops[number_stops-1].color=stop_color;
3758
3.73k
            (void) GetNextToken(q,&q,extent,token);
3759
3.73k
            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3760
3.73k
            stops[number_stops-1].offset=factor*GetDrawValue(token,
3761
3.73k
              &next_token);
3762
3.73k
            if (token == next_token)
3763
3.60k
              ThrowPointExpectedException(token,exception);
3764
3.60k
            break;
3765
3.73k
          }
3766
525k
        if (LocaleCompare("stroke",keyword) == 0)
3767
44.5k
          {
3768
44.5k
            const char
3769
44.5k
              *mvg_class;
3770
3771
44.5k
            (void) GetNextToken(q,&q,extent,token);
3772
44.5k
            if (graphic_context[n]->clip_path != MagickFalse)
3773
1.67k
              break;
3774
42.8k
            mvg_class=(const char *) GetValueFromSplayTree(macros,token);
3775
42.8k
            if (mvg_class != (const char *) NULL)
3776
38
              {
3777
38
                (void) DrawPatternPath(image,draw_info,mvg_class,
3778
38
                  &graphic_context[n]->stroke_pattern,exception);
3779
38
                break;
3780
38
              }
3781
42.8k
            (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
3782
42.8k
            if (GetImageArtifact(image,pattern) != (const char *) NULL)
3783
6.64k
              {
3784
6.64k
                (void) DrawPatternPath(image,draw_info,token,
3785
6.64k
                  &graphic_context[n]->stroke_pattern,exception);
3786
6.64k
                break;
3787
6.64k
              }
3788
36.1k
            status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3789
36.1k
              &graphic_context[n]->stroke,exception);
3790
36.1k
            if (graphic_context[n]->stroke_alpha != (double) OpaqueAlpha)
3791
5.82k
              graphic_context[n]->stroke.alpha=
3792
5.82k
                graphic_context[n]->stroke_alpha;
3793
36.1k
            break;
3794
42.8k
          }
3795
480k
        if (LocaleCompare("stroke-antialias",keyword) == 0)
3796
548
          {
3797
548
            (void) GetNextToken(q,&q,extent,token);
3798
548
            graphic_context[n]->stroke_antialias=StringToLong(token) != 0 ?
3799
548
              MagickTrue : MagickFalse;
3800
548
            break;
3801
548
          }
3802
479k
        if (LocaleCompare("stroke-dasharray",keyword) == 0)
3803
471k
          {
3804
471k
            if (graphic_context[n]->dash_pattern != (double *) NULL)
3805
1.67k
              graphic_context[n]->dash_pattern=(double *)
3806
1.67k
                RelinquishMagickMemory(graphic_context[n]->dash_pattern);
3807
471k
            if (IsValidPoint(q) != MagickFalse)
3808
14.0k
              {
3809
14.0k
                const char
3810
14.0k
                  *r;
3811
3812
14.0k
                r=q;
3813
14.0k
                (void) GetNextToken(r,&r,extent,token);
3814
14.0k
                if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3815
14.0k
                  ThrowPointExpectedException(token,exception);
3816
14.0k
                if (*token == ',')
3817
0
                  (void) GetNextToken(r,&r,extent,token);
3818
42.1k
                for (x=0; IsValidPoint(token) != MagickFalse; x++)
3819
28.1k
                {
3820
28.1k
                  (void) GetNextToken(r,&r,extent,token);
3821
28.1k
                  if (*token == ',')
3822
312
                    (void) GetNextToken(r,&r,extent,token);
3823
28.1k
                }
3824
14.0k
                graphic_context[n]->dash_pattern=(double *)
3825
14.0k
                  AcquireQuantumMemory((size_t) (2*x+2),
3826
14.0k
                  sizeof(*graphic_context[n]->dash_pattern));
3827
14.0k
                if (graphic_context[n]->dash_pattern == (double *) NULL)
3828
0
                  {
3829
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
3830
0
                      ResourceLimitError,"MemoryAllocationFailed","`%s'",
3831
0
                      image->filename);
3832
0
                    status=MagickFalse;
3833
0
                    break;
3834
0
                  }
3835
14.0k
                (void) memset(graphic_context[n]->dash_pattern,0,(size_t)
3836
14.0k
                  (2*x+2)*sizeof(*graphic_context[n]->dash_pattern));
3837
42.1k
                for (j=0; j < x; j++)
3838
28.1k
                {
3839
28.1k
                  (void) GetNextToken(q,&q,extent,token);
3840
28.1k
                  if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
3841
28.1k
                    ThrowPointExpectedException(token,exception);
3842
28.1k
                  if (*token == ',')
3843
312
                    (void) GetNextToken(q,&q,extent,token);
3844
28.1k
                  graphic_context[n]->dash_pattern[j]=GetDrawValue(token,
3845
28.1k
                    &next_token);
3846
28.1k
                  if (token == next_token)
3847
28.1k
                    ThrowPointExpectedException(token,exception);
3848
28.1k
                  if (graphic_context[n]->dash_pattern[j] <= 0.0)
3849
8
                    status=MagickFalse;
3850
28.1k
                }
3851
14.0k
                if (((x & 0x01) != 0) && (j == x))
3852
15.2k
                  for ( ; j < (2*x); j++)
3853
10.1k
                    graphic_context[n]->dash_pattern[j]=
3854
10.1k
                      graphic_context[n]->dash_pattern[j-x];
3855
14.0k
                graphic_context[n]->dash_pattern[j]=0.0;
3856
14.0k
                break;
3857
14.0k
              }
3858
457k
            (void) GetNextToken(q,&q,extent,token);
3859
457k
            break;
3860
471k
          }
3861
8.87k
        if (LocaleCompare("stroke-dashoffset",keyword) == 0)
3862
165
          {
3863
165
            (void) GetNextToken(q,&q,extent,token);
3864
165
            graphic_context[n]->dash_offset=GetDrawValue(token,&next_token);
3865
165
            if (token == next_token)
3866
128
              ThrowPointExpectedException(token,exception);
3867
37
            break;
3868
165
          }
3869
8.71k
        if (LocaleCompare("stroke-linecap",keyword) == 0)
3870
430
          {
3871
430
            ssize_t
3872
430
              linecap;
3873
3874
430
            (void) GetNextToken(q,&q,extent,token);
3875
430
            linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token);
3876
430
            if (linecap == -1)
3877
163
              {
3878
163
                status=MagickFalse;
3879
163
                break;
3880
163
              }
3881
267
            graphic_context[n]->linecap=(LineCap) linecap;
3882
267
            break;
3883
430
          }
3884
8.28k
        if (LocaleCompare("stroke-linejoin",keyword) == 0)
3885
1.34k
          {
3886
1.34k
            ssize_t
3887
1.34k
              linejoin;
3888
3889
1.34k
            (void) GetNextToken(q,&q,extent,token);
3890
1.34k
            linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse,
3891
1.34k
              token);
3892
1.34k
            if (linejoin == -1)
3893
0
              {
3894
0
                status=MagickFalse;
3895
0
                break;
3896
0
              }
3897
1.34k
            graphic_context[n]->linejoin=(LineJoin) linejoin;
3898
1.34k
            break;
3899
1.34k
          }
3900
6.93k
        if (LocaleCompare("stroke-miterlimit",keyword) == 0)
3901
911
          {
3902
911
            (void) GetNextToken(q,&q,extent,token);
3903
911
            graphic_context[n]->miterlimit=StringToUnsignedLong(token);
3904
911
            break;
3905
911
          }
3906
6.02k
        if (LocaleCompare("stroke-opacity",keyword) == 0)
3907
2.03k
          {
3908
2.03k
            double
3909
2.03k
              opacity;
3910
3911
2.03k
            (void) GetNextToken(q,&q,extent,token);
3912
2.03k
            if (graphic_context[n]->clip_path != MagickFalse)
3913
160
              break;
3914
1.87k
            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3915
1.87k
            opacity=MagickMin(MagickMax(factor*GetDrawValue(token,&next_token),
3916
1.87k
              0.0),1.0);
3917
1.87k
            if (token == next_token)
3918
1.73k
              ThrowPointExpectedException(token,exception);
3919
1.73k
            if (graphic_context[n]->compliance == SVGCompliance)
3920
0
              graphic_context[n]->stroke_alpha*=opacity;
3921
1.73k
            else
3922
1.73k
              graphic_context[n]->stroke_alpha=(double) QuantumRange*opacity;
3923
1.73k
            if (graphic_context[n]->stroke.alpha != (double) TransparentAlpha)
3924
1.22k
              graphic_context[n]->stroke.alpha=graphic_context[n]->stroke_alpha;
3925
505
            else
3926
505
              graphic_context[n]->stroke.alpha=(MagickRealType)
3927
505
                ClampToQuantum((double) QuantumRange*opacity);
3928
1.73k
            graphic_context[n]->stroke.alpha_trait=BlendPixelTrait;
3929
1.73k
            break;
3930
1.87k
          }
3931
3.99k
        if (LocaleCompare("stroke-width",keyword) == 0)
3932
1.06k
          {
3933
1.06k
            (void) GetNextToken(q,&q,extent,token);
3934
1.06k
            if (graphic_context[n]->clip_path != MagickFalse)
3935
63
              break;
3936
1.00k
            graphic_context[n]->stroke_width=GetDrawValue(token,&next_token);
3937
1.00k
            if ((token == next_token) ||
3938
806
                (graphic_context[n]->stroke_width < 0.0))
3939
761
              ThrowPointExpectedException(token,exception);
3940
761
            break;
3941
1.00k
          }
3942
2.92k
        status=MagickFalse;
3943
2.92k
        break;
3944
3.99k
      }
3945
137k
      case 't':
3946
138k
      case 'T':
3947
138k
      {
3948
138k
        if (LocaleCompare("text",keyword) == 0)
3949
80.5k
          {
3950
80.5k
            primitive_type=TextPrimitive;
3951
80.5k
            cursor=0.0;
3952
80.5k
            break;
3953
80.5k
          }
3954
57.5k
        if (LocaleCompare("text-align",keyword) == 0)
3955
0
          {
3956
0
            ssize_t
3957
0
              align;
3958
3959
0
            (void) GetNextToken(q,&q,extent,token);
3960
0
            align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3961
0
            if (align == -1)
3962
0
              {
3963
0
                status=MagickFalse;
3964
0
                break;
3965
0
              }
3966
0
            graphic_context[n]->align=(AlignType) align;
3967
0
            break;
3968
0
          }
3969
57.5k
        if (LocaleCompare("text-anchor",keyword) == 0)
3970
0
          {
3971
0
            ssize_t
3972
0
              align;
3973
3974
0
            (void) GetNextToken(q,&q,extent,token);
3975
0
            align=ParseCommandOption(MagickAlignOptions,MagickFalse,token);
3976
0
            if (align == -1)
3977
0
              {
3978
0
                status=MagickFalse;
3979
0
                break;
3980
0
              }
3981
0
            graphic_context[n]->align=(AlignType) align;
3982
0
            break;
3983
0
          }
3984
57.5k
        if (LocaleCompare("text-antialias",keyword) == 0)
3985
56.0k
          {
3986
56.0k
            (void) GetNextToken(q,&q,extent,token);
3987
56.0k
            graphic_context[n]->text_antialias=StringToLong(token) != 0 ?
3988
56.0k
              MagickTrue : MagickFalse;
3989
56.0k
            break;
3990
56.0k
          }
3991
1.55k
        if (LocaleCompare("text-undercolor",keyword) == 0)
3992
144
          {
3993
144
            (void) GetNextToken(q,&q,extent,token);
3994
144
            status&=(MagickStatusType) QueryColorCompliance(token,AllCompliance,
3995
144
              &graphic_context[n]->undercolor,exception);
3996
144
            break;
3997
144
          }
3998
1.41k
        if (LocaleCompare("translate",keyword) == 0)
3999
764
          {
4000
764
            (void) GetNextToken(q,&q,extent,token);
4001
764
            affine.tx=GetDrawValue(token,&next_token);
4002
764
            if (token == next_token)
4003
540
              ThrowPointExpectedException(token,exception);
4004
540
            (void) GetNextToken(q,&q,extent,token);
4005
540
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4006
377
              ThrowPointExpectedException(token,exception);
4007
377
            if (*token == ',')
4008
377
              (void) GetNextToken(q,&q,extent,token);
4009
377
            affine.ty=GetDrawValue(token,&next_token);
4010
377
            if (token == next_token)
4011
377
              ThrowPointExpectedException(token,exception);
4012
377
            cursor=0.0;
4013
377
            break;
4014
377
          }
4015
647
        status=MagickFalse;
4016
647
        break;
4017
1.41k
      }
4018
860
      case 'u':
4019
1.02k
      case 'U':
4020
1.02k
      {
4021
1.02k
        if (LocaleCompare("use",keyword) == 0)
4022
502
          {
4023
502
            const char
4024
502
              *use;
4025
4026
            /*
4027
              Get a macro from the MVG document, and "use" it here.
4028
            */
4029
502
            (void) GetNextToken(q,&q,extent,token);
4030
502
            use=(const char *) GetValueFromSplayTree(macros,token);
4031
502
            if (use != (const char *) NULL)
4032
36
              {
4033
36
                clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4034
36
                (void) CloneString(&clone_info->primitive,use);
4035
36
                status=RenderMVGContent(image,clone_info,depth+1,exception);
4036
36
                clone_info=DestroyDrawInfo(clone_info);
4037
36
              }
4038
502
            break;
4039
502
          }
4040
519
        status=MagickFalse;
4041
519
        break;
4042
1.02k
      }
4043
128k
      case 'v':
4044
128k
      case 'V':
4045
128k
      {
4046
128k
        if (LocaleCompare("viewbox",keyword) == 0)
4047
128k
          {
4048
128k
            (void) GetNextToken(q,&q,extent,token);
4049
128k
            graphic_context[n]->viewbox.x=CastDoubleToSsizeT(ceil(
4050
128k
              GetDrawValue(token,&next_token)-0.5));
4051
128k
            if (token == next_token)
4052
127k
              ThrowPointExpectedException(token,exception);
4053
127k
            (void) GetNextToken(q,&q,extent,token);
4054
127k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4055
126k
              ThrowPointExpectedException(token,exception);
4056
126k
            if (*token == ',')
4057
706
              (void) GetNextToken(q,&q,extent,token);
4058
126k
            graphic_context[n]->viewbox.y=CastDoubleToSsizeT(
4059
126k
              ceil(GetDrawValue(token,&next_token)-0.5));
4060
126k
            if (token == next_token)
4061
126k
              ThrowPointExpectedException(token,exception);
4062
126k
            (void) GetNextToken(q,&q,extent,token);
4063
126k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4064
126k
              ThrowPointExpectedException(token,exception);
4065
126k
            if (*token == ',')
4066
128
              (void) GetNextToken(q,&q,extent,token);
4067
126k
            graphic_context[n]->viewbox.width=CastDoubleToSizeT(floor(
4068
126k
              GetDrawValue(token,&next_token)+0.5));
4069
126k
            if (token == next_token)
4070
126k
              ThrowPointExpectedException(token,exception);
4071
126k
            (void) GetNextToken(q,&q,extent,token);
4072
126k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4073
126k
              ThrowPointExpectedException(token,exception);
4074
126k
            if (*token == ',')
4075
2.37k
              (void) GetNextToken(q,&q,extent,token);
4076
126k
            graphic_context[n]->viewbox.height=CastDoubleToSizeT(floor(
4077
126k
              GetDrawValue(token,&next_token)+0.5));
4078
126k
            if (token == next_token)
4079
126k
              ThrowPointExpectedException(token,exception);
4080
126k
            break;
4081
126k
          }
4082
697
        status=MagickFalse;
4083
697
        break;
4084
128k
      }
4085
67
      case 'w':
4086
67
      case 'W':
4087
67
      {
4088
67
        if (LocaleCompare("word-spacing",keyword) == 0)
4089
66
          {
4090
66
            (void) GetNextToken(q,&q,extent,token);
4091
66
            graphic_context[n]->interword_spacing=GetDrawValue(token,
4092
66
              &next_token);
4093
66
            if (token == next_token)
4094
60
              ThrowPointExpectedException(token,exception);
4095
60
            break;
4096
66
          }
4097
1
        status=MagickFalse;
4098
1
        break;
4099
67
      }
4100
6.17k
      default:
4101
6.17k
      {
4102
6.17k
        status=MagickFalse;
4103
6.17k
        break;
4104
67
      }
4105
8.01M
    }
4106
8.01M
    if (status == MagickFalse)
4107
40.8k
      break;
4108
7.97M
    if ((fabs(affine.sx-1.0) >= MagickEpsilon) ||
4109
7.96M
        (fabs(affine.rx) >= MagickEpsilon) || (fabs(affine.ry) >= MagickEpsilon) ||
4110
7.96M
        (fabs(affine.sy-1.0) >= MagickEpsilon) ||
4111
7.96M
        (fabs(affine.tx) >= MagickEpsilon) || (fabs(affine.ty) >= MagickEpsilon))
4112
10.1k
      {
4113
10.1k
        graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
4114
10.1k
        graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
4115
10.1k
        graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
4116
10.1k
        graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
4117
10.1k
        graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+
4118
10.1k
          current.tx;
4119
10.1k
        graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+
4120
10.1k
          current.ty;
4121
10.1k
      }
4122
7.97M
    if (primitive_type == UndefinedPrimitive)
4123
7.55M
      {
4124
7.55M
        if (*q == '\0')
4125
10.4k
          {
4126
10.4k
            if (number_stops > 1)
4127
901
              {
4128
901
                GradientType
4129
901
                  type;
4130
4131
901
                type=LinearGradient;
4132
901
                if (draw_info->gradient.type == RadialGradient)
4133
0
                  type=RadialGradient;
4134
901
                (void) GradientImage(image,type,PadSpread,stops,number_stops,
4135
901
                  exception);
4136
901
              }
4137
10.4k
           if (number_stops > 0)
4138
901
             stops=(StopInfo *) RelinquishMagickMemory(stops);
4139
10.4k
          }
4140
7.55M
        if ((draw_info->debug != MagickFalse) && (q > p))
4141
0
          (void) LogMagickEvent(DrawEvent,GetMagickModule(),"  %.*s",(int)
4142
0
            (q-p-1),p);
4143
7.55M
        continue;
4144
7.55M
      }
4145
    /*
4146
      Parse the primitive attributes.
4147
    */
4148
3.38M
    for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4149
2.96M
      if (primitive_info[i].text != (char *) NULL)
4150
62.6k
        primitive_info[i].text=DestroyString(primitive_info[i].text);
4151
419k
    i=0;
4152
419k
    mvg_info.offset=i;
4153
419k
    j=0;
4154
419k
    primitive_info[0].primitive=primitive_type;
4155
419k
    primitive_info[0].point.x=0.0;
4156
419k
    primitive_info[0].point.y=0.0;
4157
419k
    primitive_info[0].coordinates=0;
4158
419k
    primitive_info[0].method=FloodfillMethod;
4159
419k
    primitive_info[0].closed_subpath=MagickFalse;
4160
1.15M
    for (x=0; *q != '\0'; x++)
4161
1.15M
    {
4162
      /*
4163
        Define points.
4164
      */
4165
1.15M
      if (IsValidPoint(q) == MagickFalse)
4166
418k
        break;
4167
736k
      (void) GetNextToken(q,&q,extent,token);
4168
736k
      point.x=GetDrawValue(token,&next_token);
4169
736k
      if (token == next_token)
4170
736k
        ThrowPointExpectedException(token,exception);
4171
736k
      (void) GetNextToken(q,&q,extent,token);
4172
736k
      if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
4173
736k
        ThrowPointExpectedException(token,exception);
4174
736k
      if (*token == ',')
4175
71.1k
        (void) GetNextToken(q,&q,extent,token);
4176
736k
      point.y=GetDrawValue(token,&next_token);
4177
736k
      if (token == next_token)
4178
736k
        ThrowPointExpectedException(token,exception);
4179
736k
      (void) GetNextToken(q,(const char **) NULL,extent,token);
4180
736k
      if (*token == ',')
4181
6.75k
        (void) GetNextToken(q,&q,extent,token);
4182
736k
      primitive_info[i].primitive=primitive_type;
4183
736k
      primitive_info[i].point=point;
4184
736k
      primitive_info[i].coordinates=0;
4185
736k
      primitive_info[i].method=FloodfillMethod;
4186
736k
      primitive_info[i].closed_subpath=MagickFalse;
4187
736k
      i++;
4188
736k
      mvg_info.offset=i;
4189
736k
      if (i < (ssize_t) number_points)
4190
736k
        continue;
4191
0
      status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4192
0
        number_points);
4193
0
      primitive_info=(*mvg_info.primitive_info);
4194
0
      if (status == MagickFalse)
4195
0
        break;
4196
0
    }
4197
419k
    if (status == MagickFalse)
4198
382
      break;
4199
418k
    if (primitive_info[j].text != (char *) NULL)
4200
0
      primitive_info[j].text=DestroyString(primitive_info[j].text);
4201
418k
    primitive_info[j].primitive=primitive_type;
4202
418k
    primitive_info[j].coordinates=(size_t) x;
4203
418k
    primitive_info[j].method=FloodfillMethod;
4204
418k
    primitive_info[j].closed_subpath=MagickFalse;
4205
    /*
4206
      Circumscribe primitive within a circle.
4207
    */
4208
418k
    bounds.x1=primitive_info[j].point.x;
4209
418k
    bounds.y1=primitive_info[j].point.y;
4210
418k
    bounds.x2=primitive_info[j].point.x;
4211
418k
    bounds.y2=primitive_info[j].point.y;
4212
743k
    for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++)
4213
324k
    {
4214
324k
      point=primitive_info[j+k].point;
4215
324k
      if (point.x < bounds.x1)
4216
69.9k
        bounds.x1=point.x;
4217
324k
      if (point.y < bounds.y1)
4218
215k
        bounds.y1=point.y;
4219
324k
      if (point.x > bounds.x2)
4220
236k
        bounds.x2=point.x;
4221
324k
      if (point.y > bounds.y2)
4222
87.9k
        bounds.y2=point.y;
4223
324k
    }
4224
    /*
4225
      Speculate how many points our primitive might consume.
4226
    */
4227
418k
    coordinates=(double) primitive_info[j].coordinates;
4228
418k
    switch (primitive_type)
4229
418k
    {
4230
132
      case RectanglePrimitive:
4231
132
      {
4232
132
        coordinates*=5.0;
4233
132
        break;
4234
0
      }
4235
537
      case RoundRectanglePrimitive:
4236
537
      {
4237
537
        double
4238
537
          alpha,
4239
537
          beta,
4240
537
          radius;
4241
4242
537
        alpha=bounds.x2-bounds.x1;
4243
537
        beta=bounds.y2-bounds.y1;
4244
537
        radius=hypot(alpha,beta);
4245
537
        coordinates*=5.0;
4246
537
        coordinates+=2.0*((size_t) ceil((double) MagickPI*radius))+6.0*
4247
537
          BezierQuantum+360.0;
4248
537
        break;
4249
0
      }
4250
341
      case BezierPrimitive:
4251
341
      {
4252
341
        coordinates=(BezierQuantum*(double) primitive_info[j].coordinates);
4253
341
        break;
4254
0
      }
4255
5.99k
      case PathPrimitive:
4256
5.99k
      {
4257
5.99k
        char
4258
5.99k
          *s,
4259
5.99k
          *t;
4260
4261
5.99k
        (void) GetNextToken(q,&q,extent,token);
4262
5.99k
        coordinates=1.0;
4263
5.99k
        t=token;
4264
1.46M
        for (s=token; *s != '\0'; s=t)
4265
1.46M
        {
4266
1.46M
          double
4267
1.46M
            value;
4268
4269
1.46M
          value=GetDrawValue(s,&t);
4270
1.46M
          (void) value;
4271
1.46M
          if (s == t)
4272
1.11M
            {
4273
1.11M
              t++;
4274
1.11M
              continue;
4275
1.11M
            }
4276
348k
          coordinates++;
4277
348k
        }
4278
2.04M
        for (s=token; *s != '\0'; s++)
4279
2.03M
          if (strspn(s,"AaCcQqSsTt") != 0)
4280
128k
            coordinates+=(20.0*BezierQuantum)+360.0;
4281
5.99k
        break;
4282
0
      }
4283
411k
      default:
4284
411k
        break;
4285
418k
    }
4286
418k
    if (status == MagickFalse)
4287
0
      break;
4288
418k
    if (((size_t) (i+coordinates)) >= number_points)
4289
5.92k
      {
4290
        /*
4291
          Resize based on speculative points required by primitive.
4292
        */
4293
5.92k
        number_points+=(size_t) coordinates+1;
4294
5.92k
        if (number_points < (size_t) coordinates)
4295
0
          {
4296
0
            (void) ThrowMagickException(exception,GetMagickModule(),
4297
0
              ResourceLimitError,"MemoryAllocationFailed","`%s'",
4298
0
              image->filename);
4299
0
            status=MagickFalse;
4300
0
            break;
4301
0
          }
4302
5.92k
        mvg_info.offset=i;
4303
5.92k
        status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4304
5.92k
          number_points);
4305
5.92k
        primitive_info=(*mvg_info.primitive_info);
4306
5.92k
        if (status == MagickFalse)
4307
184
          break;
4308
5.92k
      }
4309
418k
    status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,
4310
418k
      PrimitiveExtentPad);
4311
418k
    primitive_info=(*mvg_info.primitive_info);
4312
418k
    if (status == MagickFalse)
4313
0
      break;
4314
418k
    mvg_info.offset=j;
4315
418k
    switch (primitive_type)
4316
418k
    {
4317
6.68k
      case PointPrimitive:
4318
6.68k
      default:
4319
6.68k
      {
4320
6.68k
        if (primitive_info[j].coordinates != 1)
4321
17
          {
4322
17
            status=MagickFalse;
4323
17
            break;
4324
17
          }
4325
6.66k
        status&=(MagickStatusType) TracePoint(primitive_info+j,
4326
6.66k
          primitive_info[j].point);
4327
6.66k
        primitive_info=(*mvg_info.primitive_info);
4328
6.66k
        if (status == MagickFalse)
4329
0
          break;
4330
6.66k
        i=j+(ssize_t) primitive_info[j].coordinates;
4331
6.66k
        break;
4332
6.66k
      }
4333
14.3k
      case LinePrimitive:
4334
14.3k
      {
4335
14.3k
        if (primitive_info[j].coordinates != 2)
4336
355
          {
4337
355
            status=MagickFalse;
4338
355
            break;
4339
355
          }
4340
13.9k
        status&=(MagickStatusType) TraceLine(primitive_info+j,
4341
13.9k
          primitive_info[j].point,primitive_info[j+1].point);
4342
13.9k
        primitive_info=(*mvg_info.primitive_info);
4343
13.9k
        if (status == MagickFalse)
4344
0
          break;
4345
13.9k
        i=j+(ssize_t) primitive_info[j].coordinates;
4346
13.9k
        break;
4347
13.9k
      }
4348
132
      case RectanglePrimitive:
4349
132
      {
4350
132
        if (primitive_info[j].coordinates != 2)
4351
0
          {
4352
0
            status=MagickFalse;
4353
0
            break;
4354
0
          }
4355
132
        status&=(MagickStatusType) TraceRectangle(primitive_info+j,
4356
132
          primitive_info[j].point,primitive_info[j+1].point);
4357
132
        primitive_info=(*mvg_info.primitive_info);
4358
132
        if (status == MagickFalse)
4359
0
          break;
4360
132
        i=j+(ssize_t) primitive_info[j].coordinates;
4361
132
        break;
4362
132
      }
4363
353
      case RoundRectanglePrimitive:
4364
353
      {
4365
353
        if (primitive_info[j].coordinates != 3)
4366
160
          {
4367
160
            status=MagickFalse;
4368
160
            break;
4369
160
          }
4370
193
        if ((primitive_info[j+2].point.x < 0.0) ||
4371
0
            (primitive_info[j+2].point.y < 0.0))
4372
193
          {
4373
193
            status=MagickFalse;
4374
193
            break;
4375
193
          }
4376
0
        if ((primitive_info[j+1].point.x-primitive_info[j].point.x) < 0.0)
4377
0
          {
4378
0
            status=MagickFalse;
4379
0
            break;
4380
0
          }
4381
0
        if ((primitive_info[j+1].point.y-primitive_info[j].point.y) < 0.0)
4382
0
          {
4383
0
            status=MagickFalse;
4384
0
            break;
4385
0
          }
4386
0
        status&=(MagickStatusType) TraceRoundRectangle(&mvg_info,
4387
0
          primitive_info[j].point,primitive_info[j+1].point,
4388
0
          primitive_info[j+2].point);
4389
0
        primitive_info=(*mvg_info.primitive_info);
4390
0
        if (status == MagickFalse)
4391
0
          break;
4392
0
        i=j+(ssize_t) primitive_info[j].coordinates;
4393
0
        break;
4394
0
      }
4395
7.13k
      case ArcPrimitive:
4396
7.13k
      {
4397
7.13k
        if (primitive_info[j].coordinates != 3)
4398
132
          {
4399
132
            status=MagickFalse;
4400
132
            break;
4401
132
          }
4402
7.00k
        status&=(MagickStatusType) TraceArc(&mvg_info,primitive_info[j].point,
4403
7.00k
          primitive_info[j+1].point,primitive_info[j+2].point);
4404
7.00k
        primitive_info=(*mvg_info.primitive_info);
4405
7.00k
        if (status == MagickFalse)
4406
144
          break;
4407
6.86k
        i=j+(ssize_t) primitive_info[j].coordinates;
4408
6.86k
        break;
4409
7.00k
      }
4410
468
      case EllipsePrimitive:
4411
468
      {
4412
468
        if (primitive_info[j].coordinates != 3)
4413
128
          {
4414
128
            status=MagickFalse;
4415
128
            break;
4416
128
          }
4417
340
        if ((primitive_info[j+1].point.x < 0.0) ||
4418
180
            (primitive_info[j+1].point.y < 0.0))
4419
212
          {
4420
212
            status=MagickFalse;
4421
212
            break;
4422
212
          }
4423
128
        status&=(MagickStatusType) TraceEllipse(&mvg_info,
4424
128
          primitive_info[j].point,primitive_info[j+1].point,
4425
128
          primitive_info[j+2].point);
4426
128
        primitive_info=(*mvg_info.primitive_info);
4427
128
        if (status == MagickFalse)
4428
0
          break;
4429
128
        i=j+(ssize_t) primitive_info[j].coordinates;
4430
128
        break;
4431
128
      }
4432
558
      case CirclePrimitive:
4433
558
      {
4434
558
        if (primitive_info[j].coordinates != 2)
4435
38
          {
4436
38
            status=MagickFalse;
4437
38
            break;
4438
38
          }
4439
520
        status&=(MagickStatusType) TraceCircle(&mvg_info,
4440
520
          primitive_info[j].point,primitive_info[j+1].point);
4441
520
        primitive_info=(*mvg_info.primitive_info);
4442
520
        if (status == MagickFalse)
4443
72
          break;
4444
448
        i=j+(ssize_t) primitive_info[j].coordinates;
4445
448
        break;
4446
520
      }
4447
342
      case PolylinePrimitive:
4448
342
      {
4449
342
        if (primitive_info[j].coordinates < 1)
4450
158
          {
4451
158
            status=MagickFalse;
4452
158
            break;
4453
158
          }
4454
184
        break;
4455
342
      }
4456
205
      case PolygonPrimitive:
4457
205
      {
4458
205
        if (primitive_info[j].coordinates < 3)
4459
51
          {
4460
51
            status=MagickFalse;
4461
51
            break;
4462
51
          }
4463
154
        primitive_info[i]=primitive_info[j];
4464
154
        primitive_info[i].coordinates=0;
4465
154
        primitive_info[j].coordinates++;
4466
154
        primitive_info[j].closed_subpath=MagickTrue;
4467
154
        i++;
4468
154
        break;
4469
205
      }
4470
341
      case BezierPrimitive:
4471
341
      {
4472
341
        if (primitive_info[j].coordinates < 3)
4473
67
          {
4474
67
            status=MagickFalse;
4475
67
            break;
4476
67
          }
4477
274
        status&=(MagickStatusType) TraceBezier(&mvg_info,
4478
274
          primitive_info[j].coordinates);
4479
274
        primitive_info=(*mvg_info.primitive_info);
4480
274
        if (status == MagickFalse)
4481
0
          break;
4482
274
        i=j+(ssize_t) primitive_info[j].coordinates;
4483
274
        break;
4484
274
      }
4485
5.99k
      case PathPrimitive:
4486
5.99k
      {
4487
5.99k
        coordinates=(double) TracePath(&mvg_info,token,exception);
4488
5.99k
        primitive_info=(*mvg_info.primitive_info);
4489
5.99k
        if (status == MagickFalse)
4490
0
          break;
4491
5.99k
        if (coordinates < 0.0)
4492
5.39k
          {
4493
5.39k
            status=MagickFalse;
4494
5.39k
            break;
4495
5.39k
          }
4496
602
        i=(ssize_t) (j+coordinates);
4497
602
        break;
4498
5.99k
      }
4499
3.14k
      case AlphaPrimitive:
4500
8.37k
      case ColorPrimitive:
4501
8.37k
      {
4502
8.37k
        ssize_t
4503
8.37k
          method;
4504
4505
8.37k
        if (primitive_info[j].coordinates != 1)
4506
165
          {
4507
165
            status=MagickFalse;
4508
165
            break;
4509
165
          }
4510
8.21k
        (void) GetNextToken(q,&q,extent,token);
4511
8.21k
        method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
4512
8.21k
        if (method == -1)
4513
231
          {
4514
231
            status=MagickFalse;
4515
231
            break;
4516
231
          }
4517
7.98k
        primitive_info[j].method=(PaintMethod) method;
4518
7.98k
        break;
4519
8.21k
      }
4520
80.5k
      case TextPrimitive:
4521
80.5k
      {
4522
80.5k
        if (primitive_info[j].coordinates != 1)
4523
128
          {
4524
128
            status=MagickFalse;
4525
128
            break;
4526
128
          }
4527
80.4k
        if (*token != ',')
4528
80.2k
          (void) GetNextToken(q,&q,extent,token);
4529
80.4k
        (void) CloneString(&primitive_info[j].text,token);
4530
        /*
4531
          Compute text cursor offset.
4532
        */
4533
80.4k
        clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
4534
80.4k
        if ((fabs(mvg_info.point.x-primitive_info->point.x) < MagickEpsilon) &&
4535
8.46k
            (fabs(mvg_info.point.y-primitive_info->point.y) < MagickEpsilon))
4536
3.51k
          {
4537
3.51k
            mvg_info.point=primitive_info->point;
4538
3.51k
            primitive_info->point.x+=cursor;
4539
3.51k
          }
4540
76.8k
        else
4541
76.8k
          {
4542
76.8k
            mvg_info.point=primitive_info->point;
4543
76.8k
            cursor=0.0;
4544
76.8k
          }
4545
80.4k
        clone_info->render=MagickFalse;
4546
80.4k
        clone_info->text=AcquireString(token);
4547
80.4k
        status&=(MagickStatusType) GetTypeMetrics(image,clone_info,
4548
80.4k
          &metrics,exception);
4549
80.4k
        clone_info=DestroyDrawInfo(clone_info);
4550
80.4k
        cursor+=metrics.width;
4551
80.4k
        if (graphic_context[n]->compliance != SVGCompliance)
4552
80.4k
          cursor=0.0;
4553
80.4k
        break;
4554
80.5k
      }
4555
293k
      case ImagePrimitive:
4556
293k
      {
4557
293k
        if (primitive_info[j].coordinates != 2)
4558
52
          {
4559
52
            status=MagickFalse;
4560
52
            break;
4561
52
          }
4562
293k
        (void) GetNextToken(q,&q,extent,token);
4563
293k
        (void) CloneString(&primitive_info[j].text,token);
4564
293k
        break;
4565
293k
      }
4566
418k
    }
4567
418k
    mvg_info.offset=i;
4568
418k
    if (status == 0)
4569
23.2k
      break;
4570
395k
    primitive_info[i].primitive=UndefinedPrimitive;
4571
395k
    if ((draw_info->debug != MagickFalse) && (q > p))
4572
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"  %.*s",(int) (q-p),p);
4573
    /*
4574
      Sanity check.
4575
    */
4576
395k
    status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,ExpandAffine(
4577
395k
      &graphic_context[n]->affine));
4578
395k
    primitive_info=(*mvg_info.primitive_info);
4579
395k
    if (status == 0)
4580
4.27k
      break;
4581
391k
    status&=(MagickStatusType) CheckPrimitiveExtent(&mvg_info,(double)
4582
391k
      graphic_context[n]->stroke_width);
4583
391k
    primitive_info=(*mvg_info.primitive_info);
4584
391k
    if (status == 0)
4585
15
      break;
4586
391k
    if (i == 0)
4587
805
      continue;
4588
    /*
4589
      Transform points.
4590
    */
4591
48.6M
    for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4592
48.5M
    {
4593
48.5M
      point=primitive_info[i].point;
4594
48.5M
      primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
4595
48.5M
        graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
4596
48.5M
      primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
4597
48.5M
        graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
4598
48.5M
      point=primitive_info[i].point;
4599
48.5M
      if (point.x < graphic_context[n]->bounds.x1)
4600
35.0k
        graphic_context[n]->bounds.x1=point.x;
4601
48.5M
      if (point.y < graphic_context[n]->bounds.y1)
4602
366k
        graphic_context[n]->bounds.y1=point.y;
4603
48.5M
      if (point.x > graphic_context[n]->bounds.x2)
4604
774k
        graphic_context[n]->bounds.x2=point.x;
4605
48.5M
      if (point.y > graphic_context[n]->bounds.y2)
4606
10.3M
        graphic_context[n]->bounds.y2=point.y;
4607
48.5M
      if (primitive_info[i].primitive == ImagePrimitive)
4608
289k
        break;
4609
48.2M
      if (i >= (ssize_t) number_points)
4610
48.2M
        ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
4611
48.2M
    }
4612
390k
    if (graphic_context[n]->render != MagickFalse)
4613
385k
      {
4614
385k
        if ((n != 0) && (graphic_context[n]->compliance != SVGCompliance) &&
4615
65.9k
            (graphic_context[n]->clip_mask != (char *) NULL) &&
4616
1.16k
            (LocaleCompare(graphic_context[n]->clip_mask,
4617
1.16k
             graphic_context[n-1]->clip_mask) != 0))
4618
890
          {
4619
890
            const char
4620
890
              *clip_path;
4621
4622
890
            clip_path=(const char *) GetValueFromSplayTree(macros,
4623
890
              graphic_context[n]->clip_mask);
4624
890
            if (clip_path != (const char *) NULL)
4625
147
              (void) SetImageArtifact(image,graphic_context[n]->clip_mask,
4626
147
                clip_path);
4627
890
            status&=(MagickStatusType) DrawClipPath(image,graphic_context[n],
4628
890
              graphic_context[n]->clip_mask,exception);
4629
890
          }
4630
385k
        status&=(MagickStatusType) DrawPrimitive(image,graphic_context[n],
4631
385k
          primitive_info,exception);
4632
385k
      }
4633
390k
    proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
4634
390k
      primitive_extent);
4635
390k
    if (proceed == MagickFalse)
4636
0
      break;
4637
390k
    if (status == 0)
4638
285k
      break;
4639
390k
  }
4640
369k
  if (draw_info->debug != MagickFalse)
4641
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
4642
  /*
4643
    Relinquish resources.
4644
  */
4645
369k
  macros=DestroySplayTree(macros);
4646
369k
  token=DestroyString(token);
4647
369k
  if (primitive_info != (PrimitiveInfo *) NULL)
4648
369k
    {
4649
49.1M
      for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
4650
48.8M
        if (primitive_info[i].text != (char *) NULL)
4651
310k
          primitive_info[i].text=DestroyString(primitive_info[i].text);
4652
369k
      primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4653
369k
    }
4654
369k
  primitive=DestroyString(primitive);
4655
369k
  if (stops != (StopInfo *) NULL)
4656
1.02k
    stops=(StopInfo *) RelinquishMagickMemory(stops);
4657
743k
  for ( ; n >= 0; n--)
4658
374k
    graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
4659
369k
  graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
4660
369k
  if ((status == MagickFalse) && (exception->severity < ErrorException))
4661
9.59k
    ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
4662
369k
      keyword);
4663
359k
  return(status != 0 ? MagickTrue : MagickFalse);
4664
369k
}
4665
4666
MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
4667
  ExceptionInfo *exception)
4668
10.2k
{
4669
10.2k
  return(RenderMVGContent(image,draw_info,0,exception));
4670
10.2k
}
4671

4672
/*
4673
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4674
%                                                                             %
4675
%                                                                             %
4676
%                                                                             %
4677
%   D r a w P a t t e r n P a t h                                             %
4678
%                                                                             %
4679
%                                                                             %
4680
%                                                                             %
4681
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4682
%
4683
%  DrawPatternPath() draws a pattern.
4684
%
4685
%  The format of the DrawPatternPath method is:
4686
%
4687
%      MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
4688
%        const char *name,Image **pattern,ExceptionInfo *exception)
4689
%
4690
%  A description of each parameter follows:
4691
%
4692
%    o image: the image.
4693
%
4694
%    o draw_info: the draw info.
4695
%
4696
%    o name: the pattern name.
4697
%
4698
%    o image: the image.
4699
%
4700
%    o exception: return any errors or warnings in this structure.
4701
%
4702
*/
4703
MagickExport MagickBooleanType DrawPatternPath(Image *image,
4704
  const DrawInfo *draw_info,const char *name,Image **pattern,
4705
  ExceptionInfo *exception)
4706
16.4k
{
4707
16.4k
  char
4708
16.4k
    property[MagickPathExtent];
4709
4710
16.4k
  const char
4711
16.4k
    *geometry,
4712
16.4k
    *path,
4713
16.4k
    *type;
4714
4715
16.4k
  DrawInfo
4716
16.4k
    *clone_info;
4717
4718
16.4k
  ImageInfo
4719
16.4k
    *image_info;
4720
4721
16.4k
  MagickBooleanType
4722
16.4k
    status;
4723
4724
16.4k
  assert(image != (Image *) NULL);
4725
16.4k
  assert(image->signature == MagickCoreSignature);
4726
16.4k
  assert(draw_info != (const DrawInfo *) NULL);
4727
16.4k
  assert(name != (const char *) NULL);
4728
16.4k
  if (IsEventLogging() != MagickFalse)
4729
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4730
16.4k
  (void) FormatLocaleString(property,MagickPathExtent,"%s",name);
4731
16.4k
  path=GetImageArtifact(image,property);
4732
16.4k
  if (path == (const char *) NULL)
4733
319
    return(MagickFalse);
4734
16.0k
  (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name);
4735
16.0k
  geometry=GetImageArtifact(image,property);
4736
16.0k
  if (geometry == (const char *) NULL)
4737
0
    return(MagickFalse);
4738
16.0k
  if ((*pattern) != (Image *) NULL)
4739
5.58k
    *pattern=DestroyImage(*pattern);
4740
16.0k
  image_info=AcquireImageInfo();
4741
16.0k
  image_info->size=AcquireString(geometry);
4742
16.0k
  *pattern=AcquireImage(image_info,exception);
4743
16.0k
  image_info=DestroyImageInfo(image_info);
4744
16.0k
  (void) QueryColorCompliance("#00000000",AllCompliance,
4745
16.0k
    &(*pattern)->background_color,exception);
4746
16.0k
  (void) SetImageBackgroundColor(*pattern,exception);
4747
16.0k
  if (draw_info->debug != MagickFalse)
4748
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),
4749
0
      "begin pattern-path %s %s",name,geometry);
4750
16.0k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
4751
16.0k
  if (clone_info->fill_pattern != (Image *) NULL)
4752
342
    clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
4753
16.0k
  if (clone_info->stroke_pattern != (Image *) NULL)
4754
4.23k
    clone_info->stroke_pattern=DestroyImage(clone_info->stroke_pattern);
4755
16.0k
  (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name);
4756
16.0k
  type=GetImageArtifact(image,property);
4757
16.0k
  if (type != (const char *) NULL)
4758
16.0k
    clone_info->gradient.type=(GradientType) ParseCommandOption(
4759
16.0k
      MagickGradientOptions,MagickFalse,type);
4760
16.0k
  (void) CloneString(&clone_info->primitive,path);
4761
16.0k
  status=RenderMVGContent(*pattern,clone_info,0,exception);
4762
16.0k
  clone_info=DestroyDrawInfo(clone_info);
4763
16.0k
  if (draw_info->debug != MagickFalse)
4764
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
4765
16.0k
  return(status);
4766
16.0k
}
4767

4768
/*
4769
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4770
%                                                                             %
4771
%                                                                             %
4772
%                                                                             %
4773
+   D r a w P o l y g o n P r i m i t i v e                                   %
4774
%                                                                             %
4775
%                                                                             %
4776
%                                                                             %
4777
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4778
%
4779
%  DrawPolygonPrimitive() draws a polygon on the image.
4780
%
4781
%  The format of the DrawPolygonPrimitive method is:
4782
%
4783
%      MagickBooleanType DrawPolygonPrimitive(Image *image,
4784
%        const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
4785
%        ExceptionInfo *exception)
4786
%
4787
%  A description of each parameter follows:
4788
%
4789
%    o image: the image.
4790
%
4791
%    o draw_info: the draw info.
4792
%
4793
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
4794
%
4795
%    o exception: return any errors or warnings in this structure.
4796
%
4797
*/
4798
4799
static PolygonInfo **DestroyPolygonTLS(PolygonInfo **polygon_info)
4800
61.0k
{
4801
61.0k
  ssize_t
4802
61.0k
    i;
4803
4804
61.0k
  assert(polygon_info != (PolygonInfo **) NULL);
4805
122k
  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
4806
61.0k
    if (polygon_info[i] != (PolygonInfo *) NULL)
4807
61.0k
      polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
4808
61.0k
  polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info);
4809
61.0k
  return(polygon_info);
4810
61.0k
}
4811
4812
static PolygonInfo **AcquirePolygonTLS(const PrimitiveInfo *primitive_info,
4813
  ExceptionInfo *exception)
4814
61.0k
{
4815
61.0k
  PathInfo
4816
61.0k
    *magick_restrict path_info;
4817
4818
61.0k
  PolygonInfo
4819
61.0k
    **polygon_info;
4820
4821
61.0k
  size_t
4822
61.0k
    number_threads;
4823
4824
61.0k
  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4825
61.0k
  polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
4826
61.0k
    sizeof(*polygon_info));
4827
61.0k
  if (polygon_info == (PolygonInfo **) NULL)
4828
0
    {
4829
0
      (void) ThrowMagickException(exception,GetMagickModule(),
4830
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4831
0
      return((PolygonInfo **) NULL);
4832
0
    }
4833
61.0k
  (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
4834
61.0k
  path_info=ConvertPrimitiveToPath(primitive_info,exception);
4835
61.0k
  if (path_info == (PathInfo *) NULL)
4836
20
    return(DestroyPolygonTLS(polygon_info));
4837
61.0k
  polygon_info[0]=ConvertPathToPolygon(path_info,exception);
4838
61.0k
  if (polygon_info[0] == (PolygonInfo *) NULL)
4839
0
    {
4840
0
      (void) ThrowMagickException(exception,GetMagickModule(),
4841
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4842
0
      return(DestroyPolygonTLS(polygon_info));
4843
0
    }
4844
61.0k
  path_info=(PathInfo *) RelinquishMagickMemory(path_info);
4845
61.0k
  return(polygon_info);
4846
61.0k
}
4847
4848
static MagickBooleanType ClonePolygonEdgesTLS(PolygonInfo **polygon_info,
4849
  const size_t number_threads,ExceptionInfo *exception)
4850
19.5k
{
4851
19.5k
  ssize_t
4852
19.5k
    i;
4853
4854
19.5k
  for (i=1; i < (ssize_t) number_threads; i++)
4855
0
  {
4856
0
    EdgeInfo
4857
0
      *edge_info;
4858
4859
0
    ssize_t
4860
0
      j;
4861
4862
0
    polygon_info[i]=(PolygonInfo *) AcquireMagickMemory(
4863
0
      sizeof(*polygon_info[i]));
4864
0
    if (polygon_info[i] == (PolygonInfo *) NULL)
4865
0
      {
4866
0
        (void) ThrowMagickException(exception,GetMagickModule(),
4867
0
          ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4868
0
        return(MagickFalse);
4869
0
      }
4870
0
    polygon_info[i]->number_edges=0;
4871
0
    edge_info=polygon_info[0]->edges;
4872
0
    polygon_info[i]->edges=(EdgeInfo *) AcquireQuantumMemory(
4873
0
      polygon_info[0]->number_edges,sizeof(*edge_info));
4874
0
    if (polygon_info[i]->edges == (EdgeInfo *) NULL)
4875
0
      {
4876
0
        (void) ThrowMagickException(exception,GetMagickModule(),
4877
0
          ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4878
0
        return(MagickFalse);
4879
0
      }
4880
0
    (void) memcpy(polygon_info[i]->edges,edge_info,
4881
0
      polygon_info[0]->number_edges*sizeof(*edge_info));
4882
0
    for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4883
0
      polygon_info[i]->edges[j].points=(PointInfo *) NULL;
4884
0
    polygon_info[i]->number_edges=polygon_info[0]->number_edges;
4885
0
    for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
4886
0
    {
4887
0
      edge_info=polygon_info[0]->edges+j;
4888
0
      polygon_info[i]->edges[j].points=(PointInfo *) AcquireQuantumMemory(
4889
0
        edge_info->number_points,sizeof(*edge_info));
4890
0
      if (polygon_info[i]->edges[j].points == (PointInfo *) NULL)
4891
0
        {
4892
0
          (void) ThrowMagickException(exception,GetMagickModule(),
4893
0
            ResourceLimitError,"MemoryAllocationFailed","`%s'","");
4894
0
          return(MagickFalse);
4895
0
        }
4896
0
      (void) memcpy(polygon_info[i]->edges[j].points,edge_info->points,
4897
0
        edge_info->number_points*sizeof(*edge_info->points));
4898
0
    }
4899
0
  }
4900
19.5k
  return(MagickTrue);
4901
19.5k
}
4902
4903
static size_t DestroyEdge(PolygonInfo *polygon_info,const ssize_t edge)
4904
63.7k
{
4905
63.7k
  assert(edge < (ssize_t) polygon_info->number_edges);
4906
63.7k
  polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
4907
63.7k
    polygon_info->edges[edge].points);
4908
63.7k
  polygon_info->number_edges--;
4909
63.7k
  if (edge < (ssize_t) polygon_info->number_edges)
4910
49.7k
    (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
4911
49.7k
      (polygon_info->number_edges-(size_t) edge)*sizeof(*polygon_info->edges));
4912
63.7k
  return(polygon_info->number_edges);
4913
63.7k
}
4914
4915
static double GetFillAlpha(PolygonInfo *polygon_info,const double mid,
4916
  const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x,
4917
  const ssize_t y,double *stroke_alpha)
4918
701k
{
4919
701k
  double
4920
701k
    alpha,
4921
701k
    beta,
4922
701k
    distance,
4923
701k
    subpath_alpha;
4924
4925
701k
  const PointInfo
4926
701k
    *q;
4927
4928
701k
  EdgeInfo
4929
701k
    *p;
4930
4931
701k
  PointInfo
4932
701k
    delta;
4933
4934
701k
  ssize_t
4935
701k
    i,
4936
701k
    j,
4937
701k
    winding_number;
4938
4939
  /*
4940
    Compute fill & stroke opacity for this (x,y) point.
4941
  */
4942
701k
  *stroke_alpha=0.0;
4943
701k
  subpath_alpha=0.0;
4944
701k
  p=polygon_info->edges;
4945
2.99M
  for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
4946
2.39M
  {
4947
2.39M
    if ((double) y <= (p->bounds.y1-mid-0.5))
4948
100k
      break;
4949
2.28M
    if ((double) y > (p->bounds.y2+mid+0.5))
4950
63.7k
      {
4951
63.7k
        p--;
4952
63.7k
        (void) DestroyEdge(polygon_info,j--);
4953
63.7k
        continue;
4954
63.7k
      }
4955
2.22M
    if (((double) x <= (p->bounds.x1-mid-0.5)) ||
4956
1.61M
        ((double) x > (p->bounds.x2+mid+0.5)))
4957
707k
      continue;
4958
1.51M
    i=(ssize_t) MagickMax((double) p->highwater,1.0);
4959
7.10M
    for ( ; i < (ssize_t) p->number_points; i++)
4960
6.51M
    {
4961
6.51M
      if ((double) y <= (p->points[i-1].y-mid-0.5))
4962
926k
        break;
4963
5.58M
      if ((double) y > (p->points[i].y+mid+0.5))
4964
1.30M
        continue;
4965
4.28M
      if (p->scanline != (double) y)
4966
316k
        {
4967
316k
          p->scanline=(double) y;
4968
316k
          p->highwater=(size_t) i;
4969
316k
        }
4970
      /*
4971
        Compute distance between a point and an edge.
4972
      */
4973
4.28M
      q=p->points+i-1;
4974
4.28M
      delta.x=(q+1)->x-q->x;
4975
4.28M
      delta.y=(q+1)->y-q->y;
4976
4.28M
      beta=delta.x*(x-q->x)+delta.y*(y-q->y);  /* segLen*point-cos(theta) */
4977
4.28M
      if (beta <= 0.0)
4978
2.41M
        {
4979
          /*
4980
            Cosine <= 0, point is closest to q.
4981
          */
4982
2.41M
          delta.x=(double) x-q->x;
4983
2.41M
          delta.y=(double) y-q->y;
4984
2.41M
          distance=delta.x*delta.x+delta.y*delta.y;
4985
2.41M
        }
4986
1.86M
      else
4987
1.86M
        {
4988
1.86M
          alpha=delta.x*delta.x+delta.y*delta.y;  /* segLen*segLen */
4989
1.86M
          if (beta >= alpha)
4990
768k
            {
4991
              /*
4992
                Point is closest to q+1.
4993
              */
4994
768k
              delta.x=(double) x-(q+1)->x;
4995
768k
              delta.y=(double) y-(q+1)->y;
4996
768k
              distance=delta.x*delta.x+delta.y*delta.y;
4997
768k
            }
4998
1.09M
          else
4999
1.09M
            {
5000
              /*
5001
                Point is closest to point between q & q+1.
5002
              */
5003
1.09M
              alpha=MagickSafeReciprocal(alpha);
5004
1.09M
              beta=delta.x*(y-q->y)-delta.y*(x-q->x);
5005
1.09M
              distance=alpha*beta*beta;
5006
1.09M
            }
5007
1.86M
        }
5008
      /*
5009
        Compute stroke & subpath opacity.
5010
      */
5011
4.28M
      beta=0.0;
5012
4.28M
      if (p->ghostline == MagickFalse)
5013
4.07M
        {
5014
4.07M
          alpha=mid+0.5;
5015
4.07M
          if ((*stroke_alpha < 1.0) &&
5016
3.74M
              (distance <= ((alpha+0.25)*(alpha+0.25))))
5017
233k
            {
5018
233k
              alpha=mid-0.5;
5019
233k
              if (distance <= ((alpha+0.25)*(alpha+0.25)))
5020
38.0k
                *stroke_alpha=1.0;
5021
195k
              else
5022
195k
                {
5023
195k
                  beta=1.0;
5024
195k
                  if (fabs(distance-1.0) >= MagickEpsilon)
5025
193k
                    beta=sqrt((double) distance);
5026
195k
                  alpha=beta-mid-0.5;
5027
195k
                  if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25)))
5028
118k
                    *stroke_alpha=(alpha-0.25)*(alpha-0.25);
5029
195k
                }
5030
233k
            }
5031
4.07M
        }
5032
4.28M
      if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0))
5033
3.86M
        continue;
5034
418k
      if (distance <= 0.0)
5035
13.7k
        {
5036
13.7k
          subpath_alpha=1.0;
5037
13.7k
          continue;
5038
13.7k
        }
5039
404k
      if (distance > 1.0)
5040
0
        continue;
5041
404k
      if (fabs(beta) < MagickEpsilon)
5042
213k
        {
5043
213k
          beta=1.0;
5044
213k
          if (fabs(distance-1.0) >= MagickEpsilon)
5045
210k
            beta=sqrt(distance);
5046
213k
        }
5047
404k
      alpha=beta-1.0;
5048
404k
      if (subpath_alpha < (alpha*alpha))
5049
215k
        subpath_alpha=alpha*alpha;
5050
404k
    }
5051
1.51M
  }
5052
  /*
5053
    Compute fill opacity.
5054
  */
5055
701k
  if (fill == MagickFalse)
5056
0
    return(0.0);
5057
701k
  if (subpath_alpha >= 1.0)
5058
13.7k
    return(1.0);
5059
  /*
5060
    Determine winding number.
5061
  */
5062
687k
  winding_number=0;
5063
687k
  p=polygon_info->edges;
5064
2.83M
  for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++)
5065
2.25M
  {
5066
2.25M
    if ((double) y <= p->bounds.y1)
5067
108k
      break;
5068
2.14M
    if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1))
5069
616k
      continue;
5070
1.53M
    if ((double) x > p->bounds.x2)
5071
90.9k
      {
5072
90.9k
        winding_number+=p->direction != 0 ? 1 : -1;
5073
90.9k
        continue;
5074
90.9k
      }
5075
1.44M
    i=(ssize_t) MagickMax((double) p->highwater,1.0);
5076
2.66M
    for ( ; i < (ssize_t) (p->number_points-1); i++)
5077
2.12M
      if ((double) y <= p->points[i].y)
5078
894k
        break;
5079
1.44M
    q=p->points+i-1;
5080
1.44M
    if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
5081
838k
      winding_number+=p->direction != 0 ? 1 : -1;
5082
1.44M
  }
5083
687k
  if (fill_rule != NonZeroRule)
5084
479k
    {
5085
479k
      if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
5086
50.8k
        return(1.0);
5087
479k
    }
5088
207k
  else
5089
207k
    if (MagickAbsoluteValue(winding_number) != 0)
5090
118k
      return(1.0);
5091
517k
  return(subpath_alpha);
5092
687k
}
5093
5094
static MagickBooleanType DrawPolygonPrimitive(Image *image,
5095
  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5096
  ExceptionInfo *exception)
5097
61.2k
{
5098
61.2k
  typedef struct _ExtentInfo
5099
61.2k
  {
5100
61.2k
    ssize_t
5101
61.2k
      x1,
5102
61.2k
      y1,
5103
61.2k
      x2,
5104
61.2k
      y2;
5105
61.2k
  } ExtentInfo;
5106
5107
61.2k
  CacheView
5108
61.2k
    *image_view;
5109
5110
61.2k
  const char
5111
61.2k
    *artifact;
5112
5113
61.2k
  double
5114
61.2k
    mid;
5115
5116
61.2k
  EdgeInfo
5117
61.2k
    *p;
5118
5119
61.2k
  ExtentInfo
5120
61.2k
    poly_extent;
5121
5122
61.2k
  MagickBooleanType
5123
61.2k
    fill,
5124
61.2k
    status;
5125
5126
61.2k
  PolygonInfo
5127
61.2k
    **magick_restrict polygon_info;
5128
5129
61.2k
  SegmentInfo
5130
61.2k
    bounds;
5131
5132
61.2k
  size_t
5133
61.2k
    number_threads;
5134
5135
61.2k
  ssize_t
5136
61.2k
    i,
5137
61.2k
    y;
5138
5139
61.2k
  assert(image != (Image *) NULL);
5140
61.2k
  assert(image->signature == MagickCoreSignature);
5141
61.2k
  assert(draw_info != (DrawInfo *) NULL);
5142
61.2k
  assert(draw_info->signature == MagickCoreSignature);
5143
61.2k
  assert(primitive_info != (PrimitiveInfo *) NULL);
5144
61.2k
  if (IsEventLogging() != MagickFalse)
5145
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5146
61.2k
  if (primitive_info->coordinates <= 1)
5147
199
    return(MagickTrue);
5148
  /*
5149
    Compute bounding box.
5150
  */
5151
61.0k
  polygon_info=AcquirePolygonTLS(primitive_info,exception);
5152
61.0k
  if (polygon_info == (PolygonInfo **) NULL)
5153
20
    return(MagickFalse);
5154
61.0k
  if (draw_info->debug != MagickFalse)
5155
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    begin draw-polygon");
5156
61.0k
  fill=(primitive_info->method == FillToBorderMethod) ||
5157
61.0k
    (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
5158
61.0k
  mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5159
61.0k
  bounds=polygon_info[0]->edges[0].bounds;
5160
61.0k
  artifact=GetImageArtifact(image,"draw:render-bounding-rectangles");
5161
61.0k
  if (IsStringTrue(artifact) != MagickFalse)
5162
0
    (void) DrawBoundingRectangles(image,draw_info,polygon_info[0],exception);
5163
256k
  for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++)
5164
195k
  {
5165
195k
    p=polygon_info[0]->edges+i;
5166
195k
    if (p->bounds.x1 < bounds.x1)
5167
11.0k
      bounds.x1=p->bounds.x1;
5168
195k
    if (p->bounds.y1 < bounds.y1)
5169
0
      bounds.y1=p->bounds.y1;
5170
195k
    if (p->bounds.x2 > bounds.x2)
5171
54.4k
      bounds.x2=p->bounds.x2;
5172
195k
    if (p->bounds.y2 > bounds.y2)
5173
37.6k
      bounds.y2=p->bounds.y2;
5174
195k
  }
5175
61.0k
  bounds.x1-=(mid+1.0);
5176
61.0k
  bounds.y1-=(mid+1.0);
5177
61.0k
  bounds.x2+=(mid+1.0);
5178
61.0k
  bounds.y2+=(mid+1.0);
5179
61.0k
  if ((bounds.x1 >= (double) image->columns) ||
5180
44.3k
      (bounds.y1 >= (double) image->rows) ||
5181
34.1k
      (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0))
5182
41.5k
    {
5183
41.5k
      polygon_info=DestroyPolygonTLS(polygon_info);
5184
41.5k
      return(MagickTrue);  /* virtual polygon */
5185
41.5k
    }
5186
19.5k
  bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) image->columns-1.0 ?
5187
6.25k
    (double) image->columns-1.0 : bounds.x1;
5188
19.5k
  bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) image->rows-1.0 ?
5189
689
    (double) image->rows-1.0 : bounds.y1;
5190
19.5k
  bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) image->columns-1.0 ?
5191
16.5k
    (double) image->columns-1.0 : bounds.x2;
5192
19.5k
  bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) image->rows-1.0 ?
5193
13.5k
    (double) image->rows-1.0 : bounds.y2;
5194
19.5k
  poly_extent.x1=CastDoubleToSsizeT(ceil(bounds.x1-0.5));
5195
19.5k
  poly_extent.y1=CastDoubleToSsizeT(ceil(bounds.y1-0.5));
5196
19.5k
  poly_extent.x2=CastDoubleToSsizeT(floor(bounds.x2+0.5));
5197
19.5k
  poly_extent.y2=CastDoubleToSsizeT(floor(bounds.y2+0.5));
5198
19.5k
  number_threads=(size_t) GetMagickNumberThreads(image,image,(size_t)
5199
19.5k
    (poly_extent.y2-poly_extent.y1+1),1);
5200
19.5k
  status=ClonePolygonEdgesTLS(polygon_info,number_threads,exception);
5201
19.5k
  if (status == MagickFalse)
5202
0
    {
5203
0
      polygon_info=DestroyPolygonTLS(polygon_info);
5204
0
      return(status);
5205
0
    }
5206
19.5k
  image_view=AcquireAuthenticCacheView(image,exception);
5207
19.5k
  if ((primitive_info->coordinates == 1) ||
5208
19.5k
      (polygon_info[0]->number_edges == 0))
5209
0
    {
5210
      /*
5211
        Draw point.
5212
      */
5213
#if defined(MAGICKCORE_OPENMP_SUPPORT)
5214
      #pragma omp parallel for schedule(static) shared(status) \
5215
        num_threads((int) number_threads)
5216
#endif
5217
0
      for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5218
0
      {
5219
0
        PixelInfo
5220
0
          pixel;
5221
5222
0
        ssize_t
5223
0
          x;
5224
5225
0
        Quantum
5226
0
          *magick_restrict q;
5227
5228
0
        if (status == MagickFalse)
5229
0
          continue;
5230
0
        x=poly_extent.x1;
5231
0
        q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (poly_extent.x2-
5232
0
          x+1),1,exception);
5233
0
        if (q == (Quantum *) NULL)
5234
0
          {
5235
0
            status=MagickFalse;
5236
0
            continue;
5237
0
          }
5238
0
        GetPixelInfo(image,&pixel);
5239
0
        for ( ; x <= poly_extent.x2; x++)
5240
0
        {
5241
0
          if ((x == CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5))) &&
5242
0
              (y == CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5))))
5243
0
            {
5244
0
              GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&pixel,
5245
0
                exception);
5246
0
              SetPixelViaPixelInfo(image,&pixel,q);
5247
0
            }
5248
0
          q+=(ptrdiff_t) GetPixelChannels(image);
5249
0
        }
5250
0
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5251
0
          status=MagickFalse;
5252
0
      }
5253
0
      image_view=DestroyCacheView(image_view);
5254
0
      polygon_info=DestroyPolygonTLS(polygon_info);
5255
0
      if (draw_info->debug != MagickFalse)
5256
0
        (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5257
0
          "    end draw-polygon");
5258
0
      return(status);
5259
0
    }
5260
  /*
5261
    Draw polygon or line.
5262
  */
5263
#if defined(MAGICKCORE_OPENMP_SUPPORT)
5264
  #pragma omp parallel for schedule(static) shared(status) \
5265
    num_threads((int) number_threads)
5266
#endif
5267
190k
  for (y=poly_extent.y1; y <= poly_extent.y2; y++)
5268
170k
  {
5269
170k
    const int
5270
170k
      id = GetOpenMPThreadId();
5271
5272
170k
    Quantum
5273
170k
      *magick_restrict q;
5274
5275
170k
    ssize_t
5276
170k
      x;
5277
5278
170k
    if (status == MagickFalse)
5279
0
      continue;
5280
170k
    q=GetCacheViewAuthenticPixels(image_view,poly_extent.x1,y,(size_t)
5281
170k
      (poly_extent.x2-poly_extent.x1+1),1,exception);
5282
170k
    if (q == (Quantum *) NULL)
5283
0
      {
5284
0
        status=MagickFalse;
5285
0
        continue;
5286
0
      }
5287
871k
    for (x=poly_extent.x1; x <= poly_extent.x2; x++)
5288
701k
    {
5289
701k
      double
5290
701k
        fill_alpha,
5291
701k
        stroke_alpha;
5292
5293
701k
      PixelInfo
5294
701k
        fill_color,
5295
701k
        stroke_color;
5296
5297
      /*
5298
        Fill and/or stroke.
5299
      */
5300
701k
      fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule,
5301
701k
        x,y,&stroke_alpha);
5302
701k
      if (draw_info->stroke_antialias == MagickFalse)
5303
34
        {
5304
34
          fill_alpha=fill_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5305
34
          stroke_alpha=stroke_alpha >= AntialiasThreshold ? 1.0 : 0.0;
5306
34
        }
5307
701k
      GetFillColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&fill_color,
5308
701k
        exception);
5309
701k
      CompositePixelOver(image,&fill_color,fill_alpha*fill_color.alpha,q,
5310
701k
        (double) GetPixelAlpha(image,q),q);
5311
701k
      GetStrokeColor(draw_info,x-poly_extent.x1,y-poly_extent.y1,&stroke_color,
5312
701k
        exception);
5313
701k
      CompositePixelOver(image,&stroke_color,stroke_alpha*stroke_color.alpha,q,
5314
701k
        (double) GetPixelAlpha(image,q),q);
5315
701k
      q+=(ptrdiff_t) GetPixelChannels(image);
5316
701k
    }
5317
170k
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5318
0
      status=MagickFalse;
5319
170k
  }
5320
19.5k
  image_view=DestroyCacheView(image_view);
5321
19.5k
  polygon_info=DestroyPolygonTLS(polygon_info);
5322
19.5k
  if (draw_info->debug != MagickFalse)
5323
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end draw-polygon");
5324
19.5k
  return(status);
5325
19.5k
}
5326

5327
/*
5328
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5329
%                                                                             %
5330
%                                                                             %
5331
%                                                                             %
5332
%   D r a w P r i m i t i v e                                                 %
5333
%                                                                             %
5334
%                                                                             %
5335
%                                                                             %
5336
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5337
%
5338
%  DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
5339
%
5340
%  The format of the DrawPrimitive method is:
5341
%
5342
%      MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
5343
%        PrimitiveInfo *primitive_info,ExceptionInfo *exception)
5344
%
5345
%  A description of each parameter follows:
5346
%
5347
%    o image: the image.
5348
%
5349
%    o draw_info: the draw info.
5350
%
5351
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5352
%
5353
%    o exception: return any errors or warnings in this structure.
5354
%
5355
*/
5356
static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
5357
0
{
5358
0
  const char
5359
0
    *methods[] =
5360
0
    {
5361
0
      "point",
5362
0
      "replace",
5363
0
      "floodfill",
5364
0
      "filltoborder",
5365
0
      "reset",
5366
0
      "?"
5367
0
    };
5368
5369
0
  PointInfo
5370
0
    p,
5371
0
    point,
5372
0
    q;
5373
5374
0
  ssize_t
5375
0
    i,
5376
0
    x;
5377
5378
0
  ssize_t
5379
0
    coordinates,
5380
0
    y;
5381
5382
0
  x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5383
0
  y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5384
0
  switch (primitive_info->primitive)
5385
0
  {
5386
0
    case AlphaPrimitive:
5387
0
    {
5388
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5389
0
        "AlphaPrimitive %.17g,%.17g %s",(double) x,(double) y,
5390
0
        methods[primitive_info->method]);
5391
0
      return;
5392
0
    }
5393
0
    case ColorPrimitive:
5394
0
    {
5395
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5396
0
        "ColorPrimitive %.17g,%.17g %s",(double) x,(double) y,
5397
0
        methods[primitive_info->method]);
5398
0
      return;
5399
0
    }
5400
0
    case ImagePrimitive:
5401
0
    {
5402
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5403
0
        "ImagePrimitive %.17g,%.17g",(double) x,(double) y);
5404
0
      return;
5405
0
    }
5406
0
    case PointPrimitive:
5407
0
    {
5408
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5409
0
        "PointPrimitive %.17g,%.17g %s",(double) x,(double) y,
5410
0
        methods[primitive_info->method]);
5411
0
      return;
5412
0
    }
5413
0
    case TextPrimitive:
5414
0
    {
5415
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5416
0
        "TextPrimitive %.17g,%.17g",(double) x,(double) y);
5417
0
      return;
5418
0
    }
5419
0
    default:
5420
0
      break;
5421
0
  }
5422
0
  coordinates=0;
5423
0
  p=primitive_info[0].point;
5424
0
  q.x=(-1.0);
5425
0
  q.y=(-1.0);
5426
0
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5427
0
  {
5428
0
    point=primitive_info[i].point;
5429
0
    if (coordinates <= 0)
5430
0
      {
5431
0
        coordinates=(ssize_t) primitive_info[i].coordinates;
5432
0
        (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5433
0
          "    begin open (%.17g)",(double) coordinates);
5434
0
        p=point;
5435
0
      }
5436
0
    point=primitive_info[i].point;
5437
0
    if ((fabs(q.x-point.x) >= MagickEpsilon) ||
5438
0
        (fabs(q.y-point.y) >= MagickEpsilon))
5439
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5440
0
        "      %.17g: %.18g,%.18g",(double) coordinates,point.x,point.y);
5441
0
    else
5442
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5443
0
        "      %.17g: %g %g (duplicate)",(double) coordinates,point.x,point.y);
5444
0
    q=point;
5445
0
    coordinates--;
5446
0
    if (coordinates > 0)
5447
0
      continue;
5448
0
    if ((fabs(p.x-point.x) >= MagickEpsilon) ||
5449
0
        (fabs(p.y-point.y) >= MagickEpsilon))
5450
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end last (%.17g)",
5451
0
        (double) coordinates);
5452
0
    else
5453
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"    end open (%.17g)",
5454
0
        (double) coordinates);
5455
0
  }
5456
0
}
5457
5458
MagickExport MagickBooleanType DrawPrimitive(Image *image,
5459
  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5460
  ExceptionInfo *exception)
5461
385k
{
5462
385k
  CacheView
5463
385k
    *image_view;
5464
5465
385k
  MagickStatusType
5466
385k
    status;
5467
5468
385k
  ssize_t
5469
385k
    i,
5470
385k
    x;
5471
5472
385k
  ssize_t
5473
385k
    y;
5474
5475
385k
  if (draw_info->debug != MagickFalse)
5476
0
    {
5477
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5478
0
        "  begin draw-primitive");
5479
0
      (void) LogMagickEvent(DrawEvent,GetMagickModule(),
5480
0
        "    affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
5481
0
        draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
5482
0
        draw_info->affine.tx,draw_info->affine.ty);
5483
0
    }
5484
385k
  status=MagickTrue;
5485
385k
  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5486
0
      ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) ||
5487
0
       (IsPixelInfoGray(&draw_info->stroke) == MagickFalse)))
5488
0
    status&=(MagickStatusType) SetImageColorspace(image,sRGBColorspace,
5489
0
      exception);
5490
385k
  if (draw_info->compliance == SVGCompliance)
5491
0
    {
5492
0
      status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5493
0
        draw_info->clipping_mask,exception);
5494
0
      status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5495
0
        draw_info->composite_mask,exception);
5496
0
    }
5497
385k
  x=CastDoubleToSsizeT(ceil(primitive_info->point.x-0.5));
5498
385k
  y=CastDoubleToSsizeT(ceil(primitive_info->point.y-0.5));
5499
385k
  image_view=AcquireAuthenticCacheView(image,exception);
5500
385k
  switch (primitive_info->primitive)
5501
385k
  {
5502
2.98k
    case AlphaPrimitive:
5503
2.98k
    {
5504
2.98k
      if ((image->alpha_trait & BlendPixelTrait) == 0)
5505
0
        status&=(MagickStatusType) SetImageAlphaChannel(image,
5506
0
          OpaqueAlphaChannel,exception);
5507
2.98k
      switch (primitive_info->method)
5508
2.98k
      {
5509
1.50k
        case PointMethod:
5510
1.50k
        default:
5511
1.50k
        {
5512
1.50k
          PixelInfo
5513
1.50k
            pixel;
5514
5515
1.50k
          Quantum
5516
1.50k
            *q;
5517
5518
1.50k
          q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5519
1.50k
          if (q == (Quantum *) NULL)
5520
1.50k
            break;
5521
4
          GetFillColor(draw_info,x,y,&pixel,exception);
5522
4
          SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5523
4
          status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5524
4
            exception);
5525
4
          break;
5526
1.50k
        }
5527
59
        case ReplaceMethod:
5528
59
        {
5529
59
          PixelInfo
5530
59
            pixel,
5531
59
            target;
5532
5533
59
          status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5534
59
            x,y,&target,exception);
5535
59
          GetPixelInfo(image,&pixel);
5536
963
          for (y=0; y < (ssize_t) image->rows; y++)
5537
919
          {
5538
919
            Quantum
5539
919
              *magick_restrict q;
5540
5541
919
            q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5542
919
              exception);
5543
919
            if (q == (Quantum *) NULL)
5544
0
              break;
5545
74.1k
            for (x=0; x < (ssize_t) image->columns; x++)
5546
73.2k
            {
5547
73.2k
              GetPixelInfoPixel(image,q,&pixel);
5548
73.2k
              if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5549
1.50k
                {
5550
1.50k
                  q+=(ptrdiff_t) GetPixelChannels(image);
5551
1.50k
                  continue;
5552
1.50k
                }
5553
71.7k
              GetFillColor(draw_info,x,y,&pixel,exception);
5554
71.7k
              SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5555
71.7k
              q+=(ptrdiff_t) GetPixelChannels(image);
5556
71.7k
            }
5557
919
            status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5558
919
              exception);
5559
919
            if (status == MagickFalse)
5560
15
              break;
5561
919
          }
5562
59
          break;
5563
1.50k
        }
5564
841
        case FloodfillMethod:
5565
1.33k
        case FillToBorderMethod:
5566
1.33k
        {
5567
1.33k
          ChannelType
5568
1.33k
            channel_mask;
5569
5570
1.33k
          PixelInfo
5571
1.33k
            target;
5572
5573
1.33k
          status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5574
1.33k
            TileVirtualPixelMethod,x,y,&target,exception);
5575
1.33k
          if (primitive_info->method == FillToBorderMethod)
5576
498
            {
5577
498
              target.red=(double) draw_info->border_color.red;
5578
498
              target.green=(double) draw_info->border_color.green;
5579
498
              target.blue=(double) draw_info->border_color.blue;
5580
498
            }
5581
1.33k
          channel_mask=SetImageChannelMask(image,AlphaChannel);
5582
1.33k
          status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5583
1.33k
            &target,x,y,primitive_info->method == FloodfillMethod ?
5584
841
            MagickFalse : MagickTrue,exception);
5585
1.33k
          (void) SetImageChannelMask(image,channel_mask);
5586
1.33k
          break;
5587
841
        }
5588
80
        case ResetMethod:
5589
80
        {
5590
80
          PixelInfo
5591
80
            pixel;
5592
5593
2.88k
          for (y=0; y < (ssize_t) image->rows; y++)
5594
2.80k
          {
5595
2.80k
            Quantum
5596
2.80k
              *magick_restrict q;
5597
5598
2.80k
            q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5599
2.80k
              exception);
5600
2.80k
            if (q == (Quantum *) NULL)
5601
0
              break;
5602
5.60k
            for (x=0; x < (ssize_t) image->columns; x++)
5603
2.80k
            {
5604
2.80k
              GetFillColor(draw_info,x,y,&pixel,exception);
5605
2.80k
              SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
5606
2.80k
              q+=(ptrdiff_t) GetPixelChannels(image);
5607
2.80k
            }
5608
2.80k
            status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5609
2.80k
              exception);
5610
2.80k
            if (status == MagickFalse)
5611
0
              break;
5612
2.80k
          }
5613
80
          break;
5614
841
        }
5615
2.98k
      }
5616
2.98k
      break;
5617
2.98k
    }
5618
4.99k
    case ColorPrimitive:
5619
4.99k
    {
5620
4.99k
      switch (primitive_info->method)
5621
4.99k
      {
5622
1.10k
        case PointMethod:
5623
1.10k
        default:
5624
1.10k
        {
5625
1.10k
          PixelInfo
5626
1.10k
            pixel;
5627
5628
1.10k
          Quantum
5629
1.10k
            *q;
5630
5631
1.10k
          q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5632
1.10k
          if (q == (Quantum *) NULL)
5633
464
            break;
5634
638
          GetPixelInfo(image,&pixel);
5635
638
          GetFillColor(draw_info,x,y,&pixel,exception);
5636
638
          SetPixelViaPixelInfo(image,&pixel,q);
5637
638
          status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5638
638
            exception);
5639
638
          break;
5640
1.10k
        }
5641
2.99k
        case ReplaceMethod:
5642
2.99k
        {
5643
2.99k
          PixelInfo
5644
2.99k
            pixel,
5645
2.99k
            target;
5646
5647
2.99k
          status&=(MagickStatusType) GetOneCacheViewVirtualPixelInfo(image_view,
5648
2.99k
            x,y,&target,exception);
5649
22.5k
          for (y=0; y < (ssize_t) image->rows; y++)
5650
20.0k
          {
5651
20.0k
            Quantum
5652
20.0k
              *magick_restrict q;
5653
5654
20.0k
            q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5655
20.0k
              exception);
5656
20.0k
            if (q == (Quantum *) NULL)
5657
0
              break;
5658
168k
            for (x=0; x < (ssize_t) image->columns; x++)
5659
148k
            {
5660
148k
              GetPixelInfoPixel(image,q,&pixel);
5661
148k
              if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)
5662
14.9k
                {
5663
14.9k
                  q+=(ptrdiff_t) GetPixelChannels(image);
5664
14.9k
                  continue;
5665
14.9k
                }
5666
133k
              GetFillColor(draw_info,x,y,&pixel,exception);
5667
133k
              SetPixelViaPixelInfo(image,&pixel,q);
5668
133k
              q+=(ptrdiff_t) GetPixelChannels(image);
5669
133k
            }
5670
20.0k
            status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5671
20.0k
              exception);
5672
20.0k
            if (status == MagickFalse)
5673
483
              break;
5674
20.0k
          }
5675
2.99k
          break;
5676
1.10k
        }
5677
690
        case FloodfillMethod:
5678
700
        case FillToBorderMethod:
5679
700
        {
5680
700
          PixelInfo
5681
700
            target;
5682
5683
700
          status&=(MagickStatusType) GetOneVirtualPixelInfo(image,
5684
700
            TileVirtualPixelMethod,x,y,&target,exception);
5685
700
          if (primitive_info->method == FillToBorderMethod)
5686
10
            {
5687
10
              target.red=(double) draw_info->border_color.red;
5688
10
              target.green=(double) draw_info->border_color.green;
5689
10
              target.blue=(double) draw_info->border_color.blue;
5690
10
            }
5691
700
          status&=(MagickStatusType) FloodfillPaintImage(image,draw_info,
5692
700
            &target,x,y,primitive_info->method == FloodfillMethod ?
5693
690
            MagickFalse : MagickTrue,exception);
5694
700
          break;
5695
690
        }
5696
196
        case ResetMethod:
5697
196
        {
5698
196
          PixelInfo
5699
196
            pixel;
5700
5701
196
          GetPixelInfo(image,&pixel);
5702
1.35k
          for (y=0; y < (ssize_t) image->rows; y++)
5703
1.15k
          {
5704
1.15k
            Quantum
5705
1.15k
              *magick_restrict q;
5706
5707
1.15k
            q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5708
1.15k
              exception);
5709
1.15k
            if (q == (Quantum *) NULL)
5710
0
              break;
5711
32.2k
            for (x=0; x < (ssize_t) image->columns; x++)
5712
31.1k
            {
5713
31.1k
              GetFillColor(draw_info,x,y,&pixel,exception);
5714
31.1k
              SetPixelViaPixelInfo(image,&pixel,q);
5715
31.1k
              q+=(ptrdiff_t) GetPixelChannels(image);
5716
31.1k
            }
5717
1.15k
            status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5718
1.15k
              exception);
5719
1.15k
            if (status == MagickFalse)
5720
0
              break;
5721
1.15k
          }
5722
196
          break;
5723
690
        }
5724
4.99k
      }
5725
4.99k
      break;
5726
4.99k
    }
5727
284k
    case ImagePrimitive:
5728
284k
    {
5729
284k
      AffineMatrix
5730
284k
        affine;
5731
5732
284k
      char
5733
284k
        composite_geometry[MagickPathExtent];
5734
5735
284k
      Image
5736
284k
        *composite_image,
5737
284k
        *composite_images;
5738
5739
284k
      ImageInfo
5740
284k
        *clone_info;
5741
5742
284k
      RectangleInfo
5743
284k
        geometry;
5744
5745
284k
      ssize_t
5746
284k
        x1,
5747
284k
        y1;
5748
5749
284k
      if (primitive_info->text == (char *) NULL)
5750
0
        break;
5751
284k
      clone_info=AcquireImageInfo();
5752
284k
      composite_images=(Image *) NULL;
5753
284k
      if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
5754
9.80k
        composite_images=ReadInlineImage(clone_info,primitive_info->text,
5755
9.80k
          exception);
5756
274k
      else
5757
274k
        if (*primitive_info->text != '\0')
5758
274k
          {
5759
            /*
5760
              Read composite image.
5761
            */
5762
274k
            (void) CopyMagickString(clone_info->filename,primitive_info->text,
5763
274k
              MagickPathExtent);
5764
274k
            (void) SetImageInfo(clone_info,1,exception);
5765
274k
            (void) CopyMagickString(clone_info->filename,primitive_info->text,
5766
274k
              MagickPathExtent);
5767
274k
            if (clone_info->size != (char *) NULL)
5768
0
              clone_info->size=DestroyString(clone_info->size);
5769
274k
            if (clone_info->extract != (char *) NULL)
5770
2.42k
              clone_info->extract=DestroyString(clone_info->extract);
5771
274k
            composite_images=StrictReadImage(clone_info,exception);
5772
274k
          }
5773
284k
      clone_info=DestroyImageInfo(clone_info);
5774
284k
      if (composite_images == (Image *) NULL)
5775
282k
        {
5776
282k
          status=MagickFalse;
5777
282k
          break;
5778
282k
        }
5779
1.43k
      composite_image=RemoveFirstImageFromList(&composite_images);
5780
1.43k
      composite_images=DestroyImageList(composite_images);
5781
1.43k
      (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
5782
1.43k
        NULL,(void *) NULL);
5783
1.43k
      x1=CastDoubleToSsizeT(ceil(primitive_info[1].point.x-0.5));
5784
1.43k
      y1=CastDoubleToSsizeT(ceil(primitive_info[1].point.y-0.5));
5785
1.43k
      if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) ||
5786
37
          ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows)))
5787
1.39k
        {
5788
          /*
5789
            Resize image.
5790
          */
5791
1.39k
          (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5792
1.39k
            "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y);
5793
1.39k
          composite_image->filter=image->filter;
5794
1.39k
          status&=(MagickStatusType) TransformImage(&composite_image,
5795
1.39k
            (char *) NULL,composite_geometry,exception);
5796
1.39k
        }
5797
1.43k
      if (composite_image->alpha_trait == UndefinedPixelTrait)
5798
1.43k
        status&=(MagickStatusType) SetImageAlphaChannel(composite_image,
5799
1.43k
          OpaqueAlphaChannel,exception);
5800
1.43k
      if (draw_info->alpha != OpaqueAlpha)
5801
0
        status&=(MagickStatusType) SetImageAlpha(composite_image,
5802
0
          draw_info->alpha,exception);
5803
1.43k
      SetGeometry(image,&geometry);
5804
1.43k
      image->gravity=draw_info->gravity;
5805
1.43k
      geometry.x=x;
5806
1.43k
      geometry.y=y;
5807
1.43k
      (void) FormatLocaleString(composite_geometry,MagickPathExtent,
5808
1.43k
        "%.17gx%.17g%+.20g%+.20g",(double) composite_image->columns,(double)
5809
1.43k
        composite_image->rows,(double) geometry.x,(double) geometry.y);
5810
1.43k
      (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception);
5811
1.43k
      affine=draw_info->affine;
5812
1.43k
      affine.tx=(double) geometry.x;
5813
1.43k
      affine.ty=(double) geometry.y;
5814
1.43k
      composite_image->interpolate=image->interpolate;
5815
1.43k
      if ((draw_info->compose == OverCompositeOp) ||
5816
1.43k
          (draw_info->compose == SrcOverCompositeOp))
5817
0
        status&=(MagickStatusType) DrawAffineImage(image,composite_image,
5818
0
          &affine,exception);
5819
1.43k
      else
5820
1.43k
        status&=(MagickStatusType) CompositeImage(image,composite_image,
5821
1.43k
          draw_info->compose,MagickTrue,geometry.x,geometry.y,exception);
5822
1.43k
      composite_image=DestroyImage(composite_image);
5823
1.43k
      break;
5824
284k
    }
5825
6.66k
    case PointPrimitive:
5826
6.66k
    {
5827
6.66k
      PixelInfo
5828
6.66k
        fill_color;
5829
5830
6.66k
      Quantum
5831
6.66k
        *q;
5832
5833
6.66k
      if ((y < 0) || (y >= (ssize_t) image->rows))
5834
5.68k
        break;
5835
981
      if ((x < 0) || (x >= (ssize_t) image->columns))
5836
440
        break;
5837
541
      q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
5838
541
      if (q == (Quantum *) NULL)
5839
0
        break;
5840
541
      GetFillColor(draw_info,x,y,&fill_color,exception);
5841
541
      CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q,(double)
5842
541
        GetPixelAlpha(image,q),q);
5843
541
      status&=(MagickStatusType) SyncCacheViewAuthenticPixels(image_view,
5844
541
        exception);
5845
541
      break;
5846
541
    }
5847
64.8k
    case TextPrimitive:
5848
64.8k
    {
5849
64.8k
      char
5850
64.8k
        geometry[MagickPathExtent];
5851
5852
64.8k
      DrawInfo
5853
64.8k
        *clone_info;
5854
5855
64.8k
      if (primitive_info->text == (char *) NULL)
5856
0
        break;
5857
64.8k
      clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5858
64.8k
      (void) CloneString(&clone_info->text,primitive_info->text);
5859
64.8k
      (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f",
5860
64.8k
        primitive_info->point.x,primitive_info->point.y);
5861
64.8k
      (void) CloneString(&clone_info->geometry,geometry);
5862
64.8k
      status&=(MagickStatusType) AnnotateImage(image,clone_info,exception);
5863
64.8k
      clone_info=DestroyDrawInfo(clone_info);
5864
64.8k
      break;
5865
64.8k
    }
5866
21.7k
    default:
5867
21.7k
    {
5868
21.7k
      double
5869
21.7k
        mid,
5870
21.7k
        scale;
5871
5872
21.7k
      DrawInfo
5873
21.7k
        *clone_info;
5874
5875
21.7k
      if (IsEventLogging() != MagickFalse)
5876
0
        LogPrimitiveInfo(primitive_info);
5877
21.7k
      scale=ExpandAffine(&draw_info->affine);
5878
21.7k
      if ((draw_info->dash_pattern != (double *) NULL) &&
5879
8.39k
          (fabs(draw_info->dash_pattern[0]) >= MagickEpsilon) &&
5880
8.39k
          (fabs(scale*draw_info->stroke_width) >= MagickEpsilon) &&
5881
1.59k
          (draw_info->stroke.alpha != (double) TransparentAlpha))
5882
1.47k
        {
5883
          /*
5884
            Draw dash polygon.
5885
          */
5886
1.47k
          clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5887
1.47k
          clone_info->stroke_width=0.0;
5888
1.47k
          clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5889
1.47k
          status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5890
1.47k
            primitive_info,exception);
5891
1.47k
          clone_info=DestroyDrawInfo(clone_info);
5892
1.47k
          if (status != MagickFalse)
5893
1.47k
            status&=(MagickStatusType) DrawDashPolygon(draw_info,primitive_info,
5894
1.47k
              image,exception);
5895
1.47k
          break;
5896
1.47k
        }
5897
20.2k
      mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
5898
20.2k
      if ((mid > 1.0) &&
5899
719
          ((draw_info->stroke.alpha != (double) TransparentAlpha) ||
5900
233
           (draw_info->stroke_pattern != (Image *) NULL)))
5901
602
        {
5902
602
          double
5903
602
            point_x,
5904
602
            point_y;
5905
5906
602
          MagickBooleanType
5907
602
            closed_path;
5908
5909
          /*
5910
            Draw strokes while respecting line cap/join attributes.
5911
          */
5912
602
          closed_path=primitive_info[0].closed_subpath;
5913
602
          i=(ssize_t) primitive_info[0].coordinates;
5914
602
          point_x=fabs(primitive_info[i-1].point.x-primitive_info[0].point.x);
5915
602
          point_y=fabs(primitive_info[i-1].point.y-primitive_info[0].point.y);
5916
602
          if ((point_x < MagickEpsilon) && (point_y < MagickEpsilon))
5917
134
            closed_path=MagickTrue;
5918
602
          if ((((draw_info->linecap == RoundCap) ||
5919
257
                (closed_path != MagickFalse)) &&
5920
454
               (draw_info->linejoin == RoundJoin)) ||
5921
529
               (primitive_info[i].primitive != UndefinedPrimitive))
5922
76
            {
5923
76
              status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5924
76
                primitive_info,exception);
5925
76
              break;
5926
76
            }
5927
526
          clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5928
526
          clone_info->stroke_width=0.0;
5929
526
          clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
5930
526
          status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
5931
526
            primitive_info,exception);
5932
526
          clone_info=DestroyDrawInfo(clone_info);
5933
526
          if (status != MagickFalse)
5934
526
            status&=(MagickStatusType) DrawStrokePolygon(image,draw_info,
5935
526
              primitive_info,exception);
5936
526
          break;
5937
602
        }
5938
19.6k
      status&=(MagickStatusType) DrawPolygonPrimitive(image,draw_info,
5939
19.6k
        primitive_info,exception);
5940
19.6k
      break;
5941
20.2k
    }
5942
385k
  }
5943
385k
  image_view=DestroyCacheView(image_view);
5944
385k
  if (draw_info->compliance == SVGCompliance)
5945
0
    {
5946
0
      status&=(MagickStatusType) SetImageMask(image,WritePixelMask,
5947
0
        (Image *) NULL,exception);
5948
0
      status&=(MagickStatusType) SetImageMask(image,CompositePixelMask,
5949
0
        (Image *) NULL,exception);
5950
0
    }
5951
385k
  if (draw_info->debug != MagickFalse)
5952
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"  end draw-primitive");
5953
385k
  return(status != 0 ? MagickTrue : MagickFalse);
5954
385k
}
5955

5956
/*
5957
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5958
%                                                                             %
5959
%                                                                             %
5960
%                                                                             %
5961
+   D r a w S t r o k e P o l y g o n                                         %
5962
%                                                                             %
5963
%                                                                             %
5964
%                                                                             %
5965
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5966
%
5967
%  DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
5968
%  the image while respecting the line cap and join attributes.
5969
%
5970
%  The format of the DrawStrokePolygon method is:
5971
%
5972
%      MagickBooleanType DrawStrokePolygon(Image *image,
5973
%        const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
5974
%
5975
%  A description of each parameter follows:
5976
%
5977
%    o image: the image.
5978
%
5979
%    o draw_info: the draw info.
5980
%
5981
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5982
%
5983
%
5984
*/
5985
5986
static MagickBooleanType DrawRoundLinecap(Image *image,
5987
  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
5988
  ExceptionInfo *exception)
5989
1.07k
{
5990
1.07k
  PrimitiveInfo
5991
1.07k
    linecap[5];
5992
5993
1.07k
  ssize_t
5994
1.07k
    i;
5995
5996
1.07k
  if (primitive_info->coordinates < 1)
5997
0
    return(MagickFalse);
5998
5.39k
  for (i=0; i < 4; i++)
5999
4.31k
    linecap[i]=(*primitive_info);
6000
1.07k
  linecap[0].coordinates=4;
6001
1.07k
  linecap[1].point.x+=2.0*MagickEpsilon;
6002
1.07k
  linecap[2].point.x+=2.0*MagickEpsilon;
6003
1.07k
  linecap[2].point.y+=2.0*MagickEpsilon;
6004
1.07k
  linecap[3].point.y+=2.0*MagickEpsilon;
6005
1.07k
  linecap[4].primitive=UndefinedPrimitive;
6006
1.07k
  return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
6007
1.07k
}
6008
6009
static MagickBooleanType DrawStrokePolygon(Image *image,
6010
  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
6011
  ExceptionInfo *exception)
6012
38.4k
{
6013
38.4k
  DrawInfo
6014
38.4k
    *clone_info;
6015
6016
38.4k
  MagickBooleanType
6017
38.4k
    closed_path;
6018
6019
38.4k
  MagickStatusType
6020
38.4k
    status;
6021
6022
38.4k
  PrimitiveInfo
6023
38.4k
    *stroke_polygon;
6024
6025
38.4k
  const PrimitiveInfo
6026
38.4k
    *p,
6027
38.4k
    *q;
6028
6029
  /*
6030
    Draw stroked polygon.
6031
  */
6032
38.4k
  if (draw_info->debug != MagickFalse)
6033
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6034
0
      "    begin draw-stroke-polygon");
6035
38.4k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6036
38.4k
  clone_info->fill=draw_info->stroke;
6037
38.4k
  if (clone_info->fill_pattern != (Image *) NULL)
6038
27.6k
    clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern);
6039
38.4k
  if (clone_info->stroke_pattern != (Image *) NULL)
6040
448
    clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
6041
448
      MagickTrue,exception);
6042
38.4k
  clone_info->stroke.alpha=(MagickRealType) TransparentAlpha;
6043
38.4k
  clone_info->stroke_width=0.0;
6044
38.4k
  clone_info->fill_rule=NonZeroRule;
6045
38.4k
  status=MagickTrue;
6046
76.9k
  for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=(ptrdiff_t) p->coordinates)
6047
38.4k
  {
6048
38.4k
    if (p->coordinates == 1)
6049
0
      continue;
6050
38.4k
    stroke_polygon=TraceStrokePolygon(draw_info,p,exception);
6051
38.4k
    if (stroke_polygon == (PrimitiveInfo *) NULL)
6052
0
      {
6053
0
        status=0;
6054
0
        break;
6055
0
      }
6056
38.4k
    status&=(MagickStatusType) DrawPolygonPrimitive(image,clone_info,
6057
38.4k
      stroke_polygon,exception);
6058
38.4k
    stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
6059
38.4k
    if (status == 0)
6060
0
      break;
6061
38.4k
    q=p+p->coordinates-1;
6062
38.4k
    closed_path=p->closed_subpath;
6063
38.4k
    if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
6064
539
      {
6065
539
        status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,p,
6066
539
          exception);
6067
539
        status&=(MagickStatusType) DrawRoundLinecap(image,draw_info,q,
6068
539
          exception);
6069
539
      }
6070
38.4k
  }
6071
38.4k
  clone_info=DestroyDrawInfo(clone_info);
6072
38.4k
  if (draw_info->debug != MagickFalse)
6073
0
    (void) LogMagickEvent(DrawEvent,GetMagickModule(),
6074
0
      "    end draw-stroke-polygon");
6075
38.4k
  return(status != 0 ? MagickTrue : MagickFalse);
6076
38.4k
}
6077

6078
/*
6079
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6080
%                                                                             %
6081
%                                                                             %
6082
%                                                                             %
6083
%   G e t A f f i n e M a t r i x                                             %
6084
%                                                                             %
6085
%                                                                             %
6086
%                                                                             %
6087
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6088
%
6089
%  GetAffineMatrix() returns an AffineMatrix initialized to the identity
6090
%  matrix.
6091
%
6092
%  The format of the GetAffineMatrix method is:
6093
%
6094
%      void GetAffineMatrix(AffineMatrix *affine_matrix)
6095
%
6096
%  A description of each parameter follows:
6097
%
6098
%    o affine_matrix: the affine matrix.
6099
%
6100
*/
6101
MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
6102
10.0M
{
6103
10.0M
  if (IsEventLogging() != MagickFalse)
6104
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6105
10.0M
  assert(affine_matrix != (AffineMatrix *) NULL);
6106
10.0M
  (void) memset(affine_matrix,0,sizeof(*affine_matrix));
6107
10.0M
  affine_matrix->sx=1.0;
6108
10.0M
  affine_matrix->sy=1.0;
6109
10.0M
}
6110

6111
/*
6112
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6113
%                                                                             %
6114
%                                                                             %
6115
%                                                                             %
6116
+   G e t D r a w I n f o                                                     %
6117
%                                                                             %
6118
%                                                                             %
6119
%                                                                             %
6120
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6121
%
6122
%  GetDrawInfo() initializes draw_info to default values from image_info.
6123
%
6124
%  The format of the GetDrawInfo method is:
6125
%
6126
%      void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6127
%
6128
%  A description of each parameter follows:
6129
%
6130
%    o image_info: the image info..
6131
%
6132
%    o draw_info: the draw info.
6133
%
6134
*/
6135
MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6136
2.03M
{
6137
2.03M
  char
6138
2.03M
    *next_token;
6139
6140
2.03M
  const char
6141
2.03M
    *option;
6142
6143
2.03M
  ExceptionInfo
6144
2.03M
    *exception;
6145
6146
  /*
6147
    Initialize draw attributes.
6148
  */
6149
2.03M
  assert(draw_info != (DrawInfo *) NULL);
6150
2.03M
  if (IsEventLogging() != MagickFalse)
6151
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
6152
2.03M
  (void) memset(draw_info,0,sizeof(*draw_info));
6153
2.03M
  draw_info->image_info=CloneImageInfo(image_info);
6154
2.03M
  GetAffineMatrix(&draw_info->affine);
6155
2.03M
  exception=AcquireExceptionInfo();
6156
2.03M
  (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
6157
2.03M
    exception);
6158
2.03M
  (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
6159
2.03M
    exception);
6160
2.03M
  draw_info->stroke_antialias=draw_info->image_info->antialias;
6161
2.03M
  draw_info->stroke_width=1.0;
6162
2.03M
  draw_info->fill_rule=EvenOddRule;
6163
2.03M
  draw_info->alpha=OpaqueAlpha;
6164
2.03M
  draw_info->fill_alpha=OpaqueAlpha;
6165
2.03M
  draw_info->stroke_alpha=OpaqueAlpha;
6166
2.03M
  draw_info->linecap=ButtCap;
6167
2.03M
  draw_info->linejoin=MiterJoin;
6168
2.03M
  draw_info->miterlimit=10;
6169
2.03M
  draw_info->decorate=NoDecoration;
6170
2.03M
  draw_info->pointsize=12.0;
6171
2.03M
  draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
6172
2.03M
  draw_info->compose=OverCompositeOp;
6173
2.03M
  draw_info->render=MagickTrue;
6174
2.03M
  draw_info->clip_path=MagickFalse;
6175
2.03M
  draw_info->debug=(GetLogEventMask() & (DrawEvent | AnnotateEvent)) != 0 ?
6176
2.03M
    MagickTrue : MagickFalse;
6177
2.03M
  if (draw_info->image_info->font != (char *) NULL)
6178
0
    draw_info->font=AcquireString(draw_info->image_info->font);
6179
2.03M
  if (draw_info->image_info->density != (char *) NULL)
6180
0
    draw_info->density=AcquireString(draw_info->image_info->density);
6181
2.03M
  draw_info->text_antialias=draw_info->image_info->antialias;
6182
2.03M
  if (fabs(draw_info->image_info->pointsize) >= MagickEpsilon)
6183
0
    draw_info->pointsize=draw_info->image_info->pointsize;
6184
2.03M
  draw_info->border_color=draw_info->image_info->border_color;
6185
2.03M
  if (draw_info->image_info->server_name != (char *) NULL)
6186
0
    draw_info->server_name=AcquireString(draw_info->image_info->server_name);
6187
2.03M
  option=GetImageOption(draw_info->image_info,"direction");
6188
2.03M
  if (option != (const char *) NULL)
6189
0
    draw_info->direction=(DirectionType) ParseCommandOption(
6190
0
      MagickDirectionOptions,MagickFalse,option);
6191
2.03M
  else
6192
2.03M
    draw_info->direction=UndefinedDirection;
6193
2.03M
  option=GetImageOption(draw_info->image_info,"encoding");
6194
2.03M
  if (option != (const char *) NULL)
6195
0
    (void) CloneString(&draw_info->encoding,option);
6196
2.03M
  option=GetImageOption(draw_info->image_info,"family");
6197
2.03M
  if (option != (const char *) NULL)
6198
0
    (void) CloneString(&draw_info->family,option);
6199
2.03M
  option=GetImageOption(draw_info->image_info,"fill");
6200
2.03M
  if (option != (const char *) NULL)
6201
0
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
6202
0
      exception);
6203
2.03M
  option=GetImageOption(draw_info->image_info,"gravity");
6204
2.03M
  if (option != (const char *) NULL)
6205
0
    draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
6206
0
      MagickFalse,option);
6207
2.03M
  option=GetImageOption(draw_info->image_info,"interline-spacing");
6208
2.03M
  if (option != (const char *) NULL)
6209
0
    draw_info->interline_spacing=GetDrawValue(option,&next_token);
6210
2.03M
  option=GetImageOption(draw_info->image_info,"interword-spacing");
6211
2.03M
  if (option != (const char *) NULL)
6212
0
    draw_info->interword_spacing=GetDrawValue(option,&next_token);
6213
2.03M
  option=GetImageOption(draw_info->image_info,"kerning");
6214
2.03M
  if (option != (const char *) NULL)
6215
0
    draw_info->kerning=GetDrawValue(option,&next_token);
6216
2.03M
  option=GetImageOption(draw_info->image_info,"stroke");
6217
2.03M
  if (option != (const char *) NULL)
6218
0
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
6219
0
      exception);
6220
2.03M
  option=GetImageOption(draw_info->image_info,"strokewidth");
6221
2.03M
  if (option != (const char *) NULL)
6222
0
    draw_info->stroke_width=GetDrawValue(option,&next_token);
6223
2.03M
  option=GetImageOption(draw_info->image_info,"style");
6224
2.03M
  if (option != (const char *) NULL)
6225
0
    draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
6226
0
      MagickFalse,option);
6227
2.03M
  option=GetImageOption(draw_info->image_info,"undercolor");
6228
2.03M
  if (option != (const char *) NULL)
6229
0
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
6230
0
      exception);
6231
2.03M
  option=GetImageOption(draw_info->image_info,"weight");
6232
2.03M
  if (option != (const char *) NULL)
6233
0
    {
6234
0
      ssize_t
6235
0
        weight;
6236
6237
0
      weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
6238
0
      if (weight == -1)
6239
0
        weight=(ssize_t) StringToUnsignedLong(option);
6240
0
      draw_info->weight=(size_t) weight;
6241
0
    }
6242
2.03M
  option=GetImageOption(draw_info->image_info,"word-break");
6243
2.03M
  if (option != (const char *) NULL)
6244
0
    draw_info->word_break=(WordBreakType) ParseCommandOption(
6245
0
      MagickWordBreakOptions,MagickFalse,option);
6246
2.03M
  exception=DestroyExceptionInfo(exception);
6247
2.03M
  draw_info->signature=MagickCoreSignature;
6248
2.03M
}
6249

6250
/*
6251
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6252
%                                                                             %
6253
%                                                                             %
6254
%                                                                             %
6255
+   P e r m u t a t e                                                         %
6256
%                                                                             %
6257
%                                                                             %
6258
%                                                                             %
6259
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6260
%
6261
%  Permutate() returns the permutation of the (n,k).
6262
%
6263
%  The format of the Permutate method is:
6264
%
6265
%      void Permutate(ssize_t n,ssize_t k)
6266
%
6267
%  A description of each parameter follows:
6268
%
6269
%    o n:
6270
%
6271
%    o k:
6272
%
6273
%
6274
*/
6275
static inline double Permutate(const ssize_t n,const ssize_t k)
6276
94.5k
{
6277
94.5k
  double
6278
94.5k
    r;
6279
6280
94.5k
  ssize_t
6281
94.5k
    i;
6282
6283
94.5k
  r=1.0;
6284
228k
  for (i=k+1; i <= n; i++)
6285
134k
    r*=i;
6286
228k
  for (i=1; i <= (n-k); i++)
6287
134k
    r/=i;
6288
94.5k
  return(r);
6289
94.5k
}
6290

6291
/*
6292
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6293
%                                                                             %
6294
%                                                                             %
6295
%                                                                             %
6296
+   T r a c e P r i m i t i v e                                               %
6297
%                                                                             %
6298
%                                                                             %
6299
%                                                                             %
6300
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6301
%
6302
%  TracePrimitive is a collection of methods for generating graphic
6303
%  primitives such as arcs, ellipses, paths, etc.
6304
%
6305
*/
6306
6307
static MagickBooleanType TraceArc(MVGInfo *mvg_info,const PointInfo start,
6308
  const PointInfo end,const PointInfo degrees)
6309
7.00k
{
6310
7.00k
  PointInfo
6311
7.00k
    center,
6312
7.00k
    radius;
6313
6314
7.00k
  center.x=0.5*(end.x+start.x);
6315
7.00k
  center.y=0.5*(end.y+start.y);
6316
7.00k
  radius.x=fabs(center.x-start.x);
6317
7.00k
  radius.y=fabs(center.y-start.y);
6318
7.00k
  return(TraceEllipse(mvg_info,center,radius,degrees));
6319
7.00k
}
6320
6321
static MagickBooleanType TraceArcPath(MVGInfo *mvg_info,const PointInfo start,
6322
  const PointInfo end,const PointInfo arc,const double angle,
6323
  const MagickBooleanType large_arc,const MagickBooleanType sweep)
6324
5.44k
{
6325
5.44k
  double
6326
5.44k
    alpha,
6327
5.44k
    beta,
6328
5.44k
    delta,
6329
5.44k
    factor,
6330
5.44k
    gamma,
6331
5.44k
    theta;
6332
6333
5.44k
  MagickStatusType
6334
5.44k
    status;
6335
6336
5.44k
  PointInfo
6337
5.44k
    center,
6338
5.44k
    points[3],
6339
5.44k
    radii;
6340
6341
5.44k
  double
6342
5.44k
    cosine,
6343
5.44k
    sine;
6344
6345
5.44k
  PrimitiveInfo
6346
5.44k
    *primitive_info;
6347
6348
5.44k
  PrimitiveInfo
6349
5.44k
    *p;
6350
6351
5.44k
  ssize_t
6352
5.44k
    i;
6353
6354
5.44k
  size_t
6355
5.44k
    arc_segments;
6356
6357
5.44k
  ssize_t
6358
5.44k
    offset;
6359
6360
5.44k
  offset=mvg_info->offset;
6361
5.44k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6362
5.44k
  primitive_info->coordinates=0;
6363
5.44k
  if ((fabs(start.x-end.x) < MagickEpsilon) &&
6364
1.14k
      (fabs(start.y-end.y) < MagickEpsilon))
6365
144
    return(TracePoint(primitive_info,end));
6366
5.29k
  radii.x=fabs(arc.x);
6367
5.29k
  radii.y=fabs(arc.y);
6368
5.29k
  if ((radii.x < MagickEpsilon) || (radii.y < MagickEpsilon))
6369
4.09k
    return(TraceLine(primitive_info,start,end));
6370
1.19k
  cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
6371
1.19k
  sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
6372
1.19k
  center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
6373
1.19k
  center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
6374
1.19k
  delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
6375
1.19k
    (radii.y*radii.y);
6376
1.19k
  if (delta < MagickEpsilon)
6377
0
    return(TraceLine(primitive_info,start,end));
6378
1.19k
  if (delta > 1.0)
6379
160
    {
6380
160
      radii.x*=sqrt((double) delta);
6381
160
      radii.y*=sqrt((double) delta);
6382
160
    }
6383
1.19k
  points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
6384
1.19k
  points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
6385
1.19k
  points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
6386
1.19k
  points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
6387
1.19k
  alpha=points[1].x-points[0].x;
6388
1.19k
  beta=points[1].y-points[0].y;
6389
1.19k
  if (fabs(alpha*alpha+beta*beta) < MagickEpsilon)
6390
0
    return(TraceLine(primitive_info,start,end));
6391
1.19k
  factor=MagickSafeReciprocal(alpha*alpha+beta*beta)-0.25;
6392
1.19k
  if (factor <= 0.0)
6393
22
    factor=0.0;
6394
1.17k
  else
6395
1.17k
    {
6396
1.17k
      factor=sqrt((double) factor);
6397
1.17k
      if (sweep == large_arc)
6398
1.04k
        factor=(-factor);
6399
1.17k
    }
6400
1.19k
  center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
6401
1.19k
  center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
6402
1.19k
  alpha=atan2(points[0].y-center.y,points[0].x-center.x);
6403
1.19k
  theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
6404
1.19k
  if ((theta < 0.0) && (sweep != MagickFalse))
6405
1.02k
    theta+=2.0*MagickPI;
6406
176
  else
6407
176
    if ((theta > 0.0) && (sweep == MagickFalse))
6408
132
      theta-=2.0*MagickPI;
6409
1.19k
  arc_segments=(size_t) CastDoubleToSsizeT(ceil(fabs((double) (theta/(0.5*
6410
1.19k
    MagickPI+MagickEpsilon)))));
6411
1.19k
  status=MagickTrue;
6412
1.19k
  p=primitive_info;
6413
5.80k
  for (i=0; i < (ssize_t) arc_segments; i++)
6414
4.61k
  {
6415
4.61k
    beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
6416
4.61k
    gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
6417
4.61k
      sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
6418
4.61k
      sin(fmod((double) beta,DegreesToRadians(360.0)));
6419
4.61k
    points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
6420
4.61k
      arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
6421
4.61k
      (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6422
4.61k
    points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
6423
4.61k
      arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
6424
4.61k
      (double) i*theta/arc_segments),DegreesToRadians(360.0))));
6425
4.61k
    points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
6426
4.61k
      theta/arc_segments),DegreesToRadians(360.0))));
6427
4.61k
    points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
6428
4.61k
      theta/arc_segments),DegreesToRadians(360.0))));
6429
4.61k
    points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
6430
4.61k
      (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6431
4.61k
    points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
6432
4.61k
      (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
6433
4.61k
    p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
6434
4.61k
    p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
6435
4.61k
    (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
6436
4.61k
      points[0].y);
6437
4.61k
    (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
6438
4.61k
      points[0].y);
6439
4.61k
    (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
6440
4.61k
      points[1].y);
6441
4.61k
    (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
6442
4.61k
      points[1].y);
6443
4.61k
    (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
6444
4.61k
      points[2].y);
6445
4.61k
    (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
6446
4.61k
      points[2].y);
6447
4.61k
    if (i == (ssize_t) (arc_segments-1))
6448
1.19k
      (p+3)->point=end;
6449
4.61k
    status&=(MagickStatusType) TraceBezier(mvg_info,4);
6450
4.61k
    if (status == 0)
6451
0
      break;
6452
4.61k
    p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
6453
4.61k
    mvg_info->offset+=(ssize_t) p->coordinates;
6454
4.61k
    p+=(ptrdiff_t) p->coordinates;
6455
4.61k
  }
6456
1.19k
  if (status == 0)
6457
0
    return(MagickFalse);
6458
1.19k
  mvg_info->offset=offset;
6459
1.19k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6460
1.19k
  primitive_info->coordinates=(size_t) (p-primitive_info);
6461
1.19k
  primitive_info->closed_subpath=MagickFalse;
6462
774k
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6463
773k
  {
6464
773k
    p->primitive=primitive_info->primitive;
6465
773k
    p--;
6466
773k
  }
6467
1.19k
  return(MagickTrue);
6468
1.19k
}
6469
6470
static MagickBooleanType TraceBezier(MVGInfo *mvg_info,
6471
  const size_t number_coordinates)
6472
27.8k
{
6473
27.8k
  double
6474
27.8k
    alpha,
6475
27.8k
    *coefficients,
6476
27.8k
    weight;
6477
6478
27.8k
  PointInfo
6479
27.8k
    end,
6480
27.8k
    point,
6481
27.8k
    *points;
6482
6483
27.8k
  PrimitiveInfo
6484
27.8k
    *primitive_info;
6485
6486
27.8k
  PrimitiveInfo
6487
27.8k
    *p;
6488
6489
27.8k
  ssize_t
6490
27.8k
    i,
6491
27.8k
    j;
6492
6493
27.8k
  size_t
6494
27.8k
    control_points,
6495
27.8k
    quantum;
6496
6497
  /*
6498
    Allocate coefficients.
6499
  */
6500
27.8k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6501
27.8k
  quantum=number_coordinates;
6502
122k
  for (i=0; i < (ssize_t) number_coordinates; i++)
6503
94.9k
  {
6504
229k
    for (j=i+1; j < (ssize_t) number_coordinates; j++)
6505
135k
    {
6506
135k
      alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
6507
135k
      if (alpha > (double) GetMaxMemoryRequest())
6508
450
        {
6509
450
          (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6510
450
            ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6511
450
          return(MagickFalse);
6512
450
        }
6513
134k
      if (alpha > (double) quantum)
6514
24.9k
        quantum=(size_t) alpha;
6515
134k
      alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
6516
134k
      if (alpha > (double) quantum)
6517
25.8k
        quantum=(size_t) alpha;
6518
134k
    }
6519
94.9k
  }
6520
27.4k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6521
27.4k
  quantum=MagickMin(quantum/number_coordinates,BezierQuantum);
6522
27.4k
  coefficients=(double *) AcquireQuantumMemory(number_coordinates,
6523
27.4k
    sizeof(*coefficients));
6524
27.4k
  points=(PointInfo *) AcquireQuantumMemory(quantum,number_coordinates*
6525
27.4k
    sizeof(*points));
6526
27.4k
  if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL))
6527
0
    {
6528
0
      if (points != (PointInfo *) NULL)
6529
0
        points=(PointInfo *) RelinquishMagickMemory(points);
6530
0
      if (coefficients != (double *) NULL)
6531
0
        coefficients=(double *) RelinquishMagickMemory(coefficients);
6532
0
      (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
6533
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
6534
0
      return(MagickFalse);
6535
0
    }
6536
27.4k
  control_points=quantum*number_coordinates;
6537
27.4k
  if (CheckPrimitiveExtent(mvg_info,(double) control_points+1) == MagickFalse)
6538
0
    {
6539
0
      points=(PointInfo *) RelinquishMagickMemory(points);
6540
0
      coefficients=(double *) RelinquishMagickMemory(coefficients);
6541
0
      return(MagickFalse);
6542
0
    }
6543
27.4k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6544
  /*
6545
    Compute bezier points.
6546
  */
6547
27.4k
  end=primitive_info[number_coordinates-1].point;
6548
121k
  for (i=0; i < (ssize_t) number_coordinates; i++)
6549
94.5k
    coefficients[i]=Permutate((ssize_t) number_coordinates-1,i);
6550
27.4k
  weight=0.0;
6551
8.31M
  for (i=0; i < (ssize_t) control_points; i++)
6552
8.28M
  {
6553
8.28M
    p=primitive_info;
6554
8.28M
    point.x=0.0;
6555
8.28M
    point.y=0.0;
6556
8.28M
    alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
6557
42.8M
    for (j=0; j < (ssize_t) number_coordinates; j++)
6558
34.5M
    {
6559
34.5M
      point.x+=alpha*coefficients[j]*p->point.x;
6560
34.5M
      point.y+=alpha*coefficients[j]*p->point.y;
6561
34.5M
      alpha*=weight/(1.0-weight);
6562
34.5M
      p++;
6563
34.5M
    }
6564
8.28M
    points[i]=point;
6565
8.28M
    weight+=1.0/control_points;
6566
8.28M
  }
6567
  /*
6568
    Bezier curves are just short segmented polys.
6569
  */
6570
27.4k
  p=primitive_info;
6571
8.31M
  for (i=0; i < (ssize_t) control_points; i++)
6572
8.28M
  {
6573
8.28M
    if (TracePoint(p,points[i]) == MagickFalse)
6574
0
      {
6575
0
        points=(PointInfo *) RelinquishMagickMemory(points);
6576
0
        coefficients=(double *) RelinquishMagickMemory(coefficients);
6577
0
        return(MagickFalse);
6578
0
      }
6579
8.28M
    p+=(ptrdiff_t) p->coordinates;
6580
8.28M
  }
6581
27.4k
  if (TracePoint(p,end) == MagickFalse)
6582
0
    {
6583
0
      points=(PointInfo *) RelinquishMagickMemory(points);
6584
0
      coefficients=(double *) RelinquishMagickMemory(coefficients);
6585
0
      return(MagickFalse);
6586
0
    }
6587
27.4k
  p+=(ptrdiff_t) p->coordinates;
6588
27.4k
  primitive_info->coordinates=(size_t) (p-primitive_info);
6589
27.4k
  primitive_info->closed_subpath=MagickFalse;
6590
8.34M
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6591
8.31M
  {
6592
8.31M
    p->primitive=primitive_info->primitive;
6593
8.31M
    p--;
6594
8.31M
  }
6595
27.4k
  points=(PointInfo *) RelinquishMagickMemory(points);
6596
27.4k
  coefficients=(double *) RelinquishMagickMemory(coefficients);
6597
27.4k
  return(MagickTrue);
6598
27.4k
}
6599
6600
static MagickBooleanType TraceCircle(MVGInfo *mvg_info,const PointInfo start,
6601
  const PointInfo end)
6602
520
{
6603
520
  double
6604
520
    alpha,
6605
520
    beta,
6606
520
    radius;
6607
6608
520
  PointInfo
6609
520
    offset,
6610
520
    degrees;
6611
6612
520
  alpha=end.x-start.x;
6613
520
  beta=end.y-start.y;
6614
520
  radius=hypot((double) alpha,(double) beta);
6615
520
  offset.x=(double) radius;
6616
520
  offset.y=(double) radius;
6617
520
  degrees.x=0.0;
6618
520
  degrees.y=360.0;
6619
520
  return(TraceEllipse(mvg_info,start,offset,degrees));
6620
520
}
6621
6622
static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
6623
  const PointInfo radii,const PointInfo arc)
6624
7.65k
{
6625
7.65k
  double
6626
7.65k
    coordinates,
6627
7.65k
    delta,
6628
7.65k
    step,
6629
7.65k
    x,
6630
7.65k
    y;
6631
6632
7.65k
  PointInfo
6633
7.65k
    angle,
6634
7.65k
    point;
6635
6636
7.65k
  PrimitiveInfo
6637
7.65k
    *primitive_info;
6638
6639
7.65k
  PrimitiveInfo
6640
7.65k
    *p;
6641
6642
7.65k
  ssize_t
6643
7.65k
    i;
6644
6645
  /*
6646
    Ellipses are just short segmented polys.
6647
  */
6648
7.65k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6649
7.65k
  primitive_info->coordinates=0;
6650
7.65k
  if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
6651
630
    return(MagickTrue);
6652
7.02k
  delta=MagickSafeReciprocal(MagickMax(radii.x,radii.y));
6653
7.02k
  step=MagickPI/(MagickPI*MagickSafeReciprocal(delta))/8.0;
6654
7.02k
  angle.x=DegreesToRadians(arc.x);
6655
7.02k
  y=arc.y;
6656
13.4k
  while (y < arc.x)
6657
6.38k
    y+=360.0;
6658
7.02k
  angle.y=DegreesToRadians(y);
6659
7.02k
  coordinates=ceil((angle.y-angle.x)/step+1.0);
6660
7.02k
  if (CheckPrimitiveExtent(mvg_info,coordinates+1) == MagickFalse)
6661
216
    return(MagickFalse);
6662
6.80k
  i=0;
6663
6.80k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6664
47.3M
  for (p=primitive_info; angle.x < angle.y; angle.x+=step)
6665
47.3M
  {
6666
47.3M
    point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
6667
47.3M
    point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
6668
47.3M
    if (i++ >= (ssize_t) coordinates)
6669
9
      break;
6670
47.3M
    if (TracePoint(p,point) == MagickFalse)
6671
0
      return(MagickFalse);
6672
47.3M
    p+=(ptrdiff_t) p->coordinates;
6673
47.3M
  }
6674
6.80k
  point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
6675
6.80k
  point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
6676
6.80k
  if (TracePoint(p,point) == MagickFalse)
6677
0
    return(MagickFalse);
6678
6.80k
  p+=(ptrdiff_t) p->coordinates;
6679
6.80k
  primitive_info->coordinates=(size_t) (p-primitive_info);
6680
6.80k
  primitive_info->closed_subpath=MagickFalse;
6681
6.80k
  x=fabs(primitive_info[0].point.x-
6682
6.80k
    primitive_info[primitive_info->coordinates-1].point.x);
6683
6.80k
  y=fabs(primitive_info[0].point.y-
6684
6.80k
    primitive_info[primitive_info->coordinates-1].point.y);
6685
6.80k
  if ((x < MagickEpsilon) && (y < MagickEpsilon))
6686
541
    primitive_info->closed_subpath=MagickTrue;
6687
47.3M
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
6688
47.3M
  {
6689
47.3M
    p->primitive=primitive_info->primitive;
6690
47.3M
    p--;
6691
47.3M
  }
6692
6.80k
  return(MagickTrue);
6693
6.80k
}
6694
6695
static MagickBooleanType TraceLine(PrimitiveInfo *primitive_info,
6696
  const PointInfo start,const PointInfo end)
6697
18.0k
{
6698
18.0k
  if (TracePoint(primitive_info,start) == MagickFalse)
6699
0
    return(MagickFalse);
6700
18.0k
  if (TracePoint(primitive_info+1,end) == MagickFalse)
6701
0
    return(MagickFalse);
6702
18.0k
  (primitive_info+1)->primitive=primitive_info->primitive;
6703
18.0k
  primitive_info->coordinates=2;
6704
18.0k
  primitive_info->closed_subpath=MagickFalse;
6705
18.0k
  return(MagickTrue);
6706
18.0k
}
6707
6708
static ssize_t TracePath(MVGInfo *mvg_info,const char *path,
6709
  ExceptionInfo *exception)
6710
5.99k
{
6711
5.99k
  char
6712
5.99k
    *next_token,
6713
5.99k
    token[MagickPathExtent] = "";
6714
6715
5.99k
  const char
6716
5.99k
    *p;
6717
6718
5.99k
  double
6719
5.99k
    x,
6720
5.99k
    y;
6721
6722
5.99k
  int
6723
5.99k
    attribute,
6724
5.99k
    last_attribute;
6725
6726
5.99k
  MagickBooleanType
6727
5.99k
    status;
6728
6729
5.99k
  PointInfo
6730
5.99k
    end = {0.0, 0.0},
6731
5.99k
    points[4] = { {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0} },
6732
5.99k
    point = {0.0, 0.0},
6733
5.99k
    start = {0.0, 0.0};
6734
6735
5.99k
  PrimitiveInfo
6736
5.99k
    *primitive_info;
6737
6738
5.99k
  PrimitiveType
6739
5.99k
    primitive_type;
6740
6741
5.99k
  PrimitiveInfo
6742
5.99k
    *q;
6743
6744
5.99k
  ssize_t
6745
5.99k
    i;
6746
6747
5.99k
  size_t
6748
5.99k
    number_coordinates,
6749
5.99k
    z_count;
6750
6751
5.99k
  ssize_t
6752
5.99k
    subpath_offset;
6753
6754
5.99k
  subpath_offset=mvg_info->offset;
6755
5.99k
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
6756
5.99k
  status=MagickTrue;
6757
5.99k
  attribute=0;
6758
5.99k
  number_coordinates=0;
6759
5.99k
  z_count=0;
6760
5.99k
  primitive_type=primitive_info->primitive;
6761
5.99k
  q=primitive_info;
6762
26.6k
  for (p=path; *p != '\0'; )
6763
26.0k
  {
6764
26.0k
    if (status == MagickFalse)
6765
4.79k
      break;
6766
21.4k
    while (isspace((int) ((unsigned char) *p)) != 0)
6767
189
      p++;
6768
21.2k
    if (*p == '\0')
6769
120
      break;
6770
21.1k
    last_attribute=attribute;
6771
21.1k
    attribute=(int) (*p++);
6772
21.1k
    switch (attribute)
6773
21.1k
    {
6774
1.86k
      case 'a':
6775
2.05k
      case 'A':
6776
2.05k
      {
6777
2.05k
        double
6778
2.05k
          angle = 0.0;
6779
6780
2.05k
        MagickBooleanType
6781
2.05k
          large_arc = MagickFalse,
6782
2.05k
          sweep = MagickFalse;
6783
6784
2.05k
        PointInfo
6785
2.05k
          arc = {0.0, 0.0};
6786
6787
        /*
6788
          Elliptical arc.
6789
        */
6790
2.05k
        do
6791
7.40k
        {
6792
7.40k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6793
7.40k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6794
7.40k
            ThrowPointExpectedException(token,exception);
6795
7.40k
          if (*token == ',')
6796
215
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6797
7.40k
          arc.x=GetDrawValue(token,&next_token);
6798
7.40k
          if (token == next_token)
6799
7.08k
            ThrowPointExpectedException(token,exception);
6800
7.08k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6801
7.08k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6802
6.95k
            ThrowPointExpectedException(token,exception);
6803
6.95k
          if (*token == ',')
6804
2.25k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6805
6.95k
          arc.y=GetDrawValue(token,&next_token);
6806
6.95k
          if (token == next_token)
6807
6.95k
            ThrowPointExpectedException(token,exception);
6808
6.95k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6809
6.95k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6810
6.82k
            ThrowPointExpectedException(token,exception);
6811
6.82k
          if (*token == ',')
6812
2.69k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6813
6.82k
          angle=GetDrawValue(token,&next_token);
6814
6.82k
          if (token == next_token)
6815
6.82k
            ThrowPointExpectedException(token,exception);
6816
6.82k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6817
6.82k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6818
5.95k
            ThrowPointExpectedException(token,exception);
6819
5.95k
          if (*token == ',')
6820
3.10k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6821
5.95k
          large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6822
5.95k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6823
5.95k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6824
5.73k
            ThrowPointExpectedException(token,exception);
6825
5.73k
          if (*token == ',')
6826
2.51k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6827
5.73k
          sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse;
6828
5.73k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6829
5.73k
            ThrowPointExpectedException(token,exception);
6830
5.73k
          if (*token == ',')
6831
170
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6832
5.73k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6833
5.73k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6834
5.61k
            ThrowPointExpectedException(token,exception);
6835
5.61k
          if (*token == ',')
6836
1.94k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6837
5.61k
          x=GetDrawValue(token,&next_token);
6838
5.61k
          if (token == next_token)
6839
5.61k
            ThrowPointExpectedException(token,exception);
6840
5.61k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6841
5.61k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6842
5.44k
            ThrowPointExpectedException(token,exception);
6843
5.44k
          if (*token == ',')
6844
138
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6845
5.44k
          y=GetDrawValue(token,&next_token);
6846
5.44k
          if (token == next_token)
6847
5.44k
            ThrowPointExpectedException(token,exception);
6848
5.44k
          end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
6849
5.44k
          end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
6850
5.44k
          if (TraceArcPath(mvg_info,point,end,arc,angle,large_arc,sweep) == MagickFalse)
6851
0
            return(-1);
6852
5.44k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
6853
5.44k
          mvg_info->offset+=(ssize_t) q->coordinates;
6854
5.44k
          q+=(ptrdiff_t) q->coordinates;
6855
5.44k
          point=end;
6856
5.44k
          while (isspace((int) ((unsigned char) *p)) != 0)
6857
0
            p++;
6858
5.44k
          if (*p == ',')
6859
1.13k
            p++;
6860
5.44k
        } while (IsValidPoint(p) != MagickFalse);
6861
2.05k
        break;
6862
2.05k
      }
6863
2.05k
      case 'c':
6864
418
      case 'C':
6865
418
      {
6866
        /*
6867
          Cubic Bézier curve.
6868
        */
6869
418
        do
6870
3.11k
        {
6871
3.11k
          points[0]=point;
6872
5.40k
          for (i=1; i < 4; i++)
6873
5.20k
          {
6874
5.20k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6875
5.20k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6876
3.85k
              ThrowPointExpectedException(token,exception);
6877
3.85k
            if (*token == ',')
6878
569
              (void) GetNextToken(p,&p,MagickPathExtent,token);
6879
3.85k
            x=GetDrawValue(token,&next_token);
6880
3.85k
            if (token == next_token)
6881
3.85k
              ThrowPointExpectedException(token,exception);
6882
3.85k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6883
3.85k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6884
2.46k
              ThrowPointExpectedException(token,exception);
6885
2.46k
            if (*token == ',')
6886
1.02k
              (void) GetNextToken(p,&p,MagickPathExtent,token);
6887
2.46k
            y=GetDrawValue(token,&next_token);
6888
2.46k
            if (token == next_token)
6889
2.28k
              ThrowPointExpectedException(token,exception);
6890
2.28k
            end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
6891
2.28k
            end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
6892
2.28k
            points[i]=end;
6893
2.28k
          }
6894
15.5k
          for (i=0; i < 4; i++)
6895
12.4k
            (q+i)->point=points[i];
6896
3.11k
          if (TraceBezier(mvg_info,4) == MagickFalse)
6897
172
            return(-1);
6898
2.94k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
6899
2.94k
          mvg_info->offset+=(ssize_t) q->coordinates;
6900
2.94k
          q+=(ptrdiff_t) q->coordinates;
6901
2.94k
          point=end;
6902
2.94k
          while (isspace((int) ((unsigned char) *p)) != 0)
6903
0
            p++;
6904
2.94k
          if (*p == ',')
6905
1.27k
            p++;
6906
2.94k
        } while (IsValidPoint(p) != MagickFalse);
6907
246
        break;
6908
418
      }
6909
5.99k
      case 'H':
6910
6.62k
      case 'h':
6911
6.62k
      {
6912
6.62k
        do
6913
11.1k
        {
6914
11.1k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6915
11.1k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6916
10.8k
            ThrowPointExpectedException(token,exception);
6917
10.8k
          if (*token == ',')
6918
96
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6919
10.8k
          x=GetDrawValue(token,&next_token);
6920
10.8k
          if (token == next_token)
6921
10.8k
            ThrowPointExpectedException(token,exception);
6922
10.8k
          point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
6923
10.8k
          if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6924
0
            return(-1);
6925
10.8k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
6926
10.8k
          if (TracePoint(q,point) == MagickFalse)
6927
0
            return(-1);
6928
10.8k
          mvg_info->offset+=(ssize_t) q->coordinates;
6929
10.8k
          q+=(ptrdiff_t) q->coordinates;
6930
10.8k
          while (isspace((int) ((unsigned char) *p)) != 0)
6931
0
            p++;
6932
10.8k
          if (*p == ',')
6933
1.70k
            p++;
6934
10.8k
        } while (IsValidPoint(p) != MagickFalse);
6935
6.62k
        break;
6936
6.62k
      }
6937
6.62k
      case 'l':
6938
912
      case 'L':
6939
912
      {
6940
        /*
6941
          Line to.
6942
        */
6943
912
        do
6944
2.63k
        {
6945
2.63k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6946
2.63k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6947
2.48k
            ThrowPointExpectedException(token,exception);
6948
2.48k
          if (*token == ',')
6949
0
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6950
2.48k
          x=GetDrawValue(token,&next_token);
6951
2.48k
          if (token == next_token)
6952
2.48k
            ThrowPointExpectedException(token,exception);
6953
2.48k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6954
2.48k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6955
2.44k
            ThrowPointExpectedException(token,exception);
6956
2.44k
          if (*token == ',')
6957
1.28k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6958
2.44k
          y=GetDrawValue(token,&next_token);
6959
2.44k
          if (token == next_token)
6960
2.32k
            ThrowPointExpectedException(token,exception);
6961
2.32k
          point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
6962
2.32k
          point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
6963
2.32k
          if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
6964
0
            return(-1);
6965
2.32k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
6966
2.32k
          if (TracePoint(q,point) == MagickFalse)
6967
0
            return(-1);
6968
2.32k
          mvg_info->offset+=(ssize_t) q->coordinates;
6969
2.32k
          q+=(ptrdiff_t) q->coordinates;
6970
2.32k
          while (isspace((int) ((unsigned char) *p)) != 0)
6971
0
            p++;
6972
2.32k
          if (*p == ',')
6973
1.06k
            p++;
6974
2.32k
        } while (IsValidPoint(p) != MagickFalse);
6975
912
        break;
6976
912
      }
6977
912
      case 'M':
6978
1.07k
      case 'm':
6979
1.07k
      {
6980
        /*
6981
          Move to.
6982
        */
6983
1.07k
        if (mvg_info->offset != subpath_offset)
6984
838
          {
6985
838
            primitive_info=(*mvg_info->primitive_info)+subpath_offset;
6986
838
            primitive_info->coordinates=(size_t) (q-primitive_info);
6987
838
            number_coordinates+=primitive_info->coordinates;
6988
838
            primitive_info=q;
6989
838
            subpath_offset=mvg_info->offset;
6990
838
          }
6991
1.07k
        i=0;
6992
1.07k
        do
6993
1.79k
        {
6994
1.79k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
6995
1.79k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
6996
1.63k
            ThrowPointExpectedException(token,exception);
6997
1.63k
          if (*token == ',')
6998
51
            (void) GetNextToken(p,&p,MagickPathExtent,token);
6999
1.63k
          x=GetDrawValue(token,&next_token);
7000
1.63k
          if (token == next_token)
7001
1.63k
            ThrowPointExpectedException(token,exception);
7002
1.63k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
7003
1.63k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7004
1.39k
            ThrowPointExpectedException(token,exception);
7005
1.39k
          if (*token == ',')
7006
268
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7007
1.39k
          y=GetDrawValue(token,&next_token);
7008
1.39k
          if (token == next_token)
7009
1.32k
            ThrowPointExpectedException(token,exception);
7010
1.32k
          point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
7011
1.32k
          point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
7012
1.32k
          if (i == 0)
7013
904
            start=point;
7014
1.32k
          i++;
7015
1.32k
          if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7016
0
            return(-1);
7017
1.32k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
7018
1.32k
          if (TracePoint(q,point) == MagickFalse)
7019
0
            return(-1);
7020
1.32k
          mvg_info->offset+=(ssize_t) q->coordinates;
7021
1.32k
          q+=(ptrdiff_t) q->coordinates;
7022
1.32k
          while (isspace((int) ((unsigned char) *p)) != 0)
7023
0
            p++;
7024
1.32k
          if (*p == ',')
7025
87
            p++;
7026
1.32k
        } while (IsValidPoint(p) != MagickFalse);
7027
1.07k
        break;
7028
1.07k
      }
7029
1.07k
      case 'q':
7030
522
      case 'Q':
7031
522
      {
7032
        /*
7033
          Quadratic Bézier curve.
7034
        */
7035
522
        do
7036
7.08k
        {
7037
7.08k
          points[0]=point;
7038
12.6k
          for (i=1; i < 3; i++)
7039
11.1k
          {
7040
11.1k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7041
11.1k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7042
9.81k
              ThrowPointExpectedException(token,exception);
7043
9.81k
            if (*token == ',')
7044
1.26k
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7045
9.81k
            x=GetDrawValue(token,&next_token);
7046
9.81k
            if (token == next_token)
7047
9.58k
              ThrowPointExpectedException(token,exception);
7048
9.58k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7049
9.58k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7050
7.10k
              ThrowPointExpectedException(token,exception);
7051
7.10k
            if (*token == ',')
7052
5.70k
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7053
7.10k
            y=GetDrawValue(token,&next_token);
7054
7.10k
            if (token == next_token)
7055
5.52k
              ThrowPointExpectedException(token,exception);
7056
5.52k
            if (*p == ',')
7057
3.32k
              p++;
7058
5.52k
            end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
7059
5.52k
            end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
7060
5.52k
            points[i]=end;
7061
5.52k
          }
7062
28.3k
          for (i=0; i < 3; i++)
7063
21.2k
            (q+i)->point=points[i];
7064
7.08k
          if (TraceBezier(mvg_info,3) == MagickFalse)
7065
53
            return(-1);
7066
7.02k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
7067
7.02k
          mvg_info->offset+=(ssize_t) q->coordinates;
7068
7.02k
          q+=(ptrdiff_t) q->coordinates;
7069
7.02k
          point=end;
7070
7.07k
          while (isspace((int) ((unsigned char) *p)) != 0)
7071
45
            p++;
7072
7.02k
          if (*p == ',')
7073
3.19k
            p++;
7074
7.02k
        } while (IsValidPoint(p) != MagickFalse);
7075
469
        break;
7076
522
      }
7077
469
      case 's':
7078
578
      case 'S':
7079
578
      {
7080
        /*
7081
          Cubic Bézier curve.
7082
        */
7083
578
        do
7084
4.70k
        {
7085
4.70k
          points[0]=points[3];
7086
4.70k
          points[1].x=2.0*points[3].x-points[2].x;
7087
4.70k
          points[1].y=2.0*points[3].y-points[2].y;
7088
7.20k
          for (i=2; i < 4; i++)
7089
6.25k
          {
7090
6.25k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7091
6.25k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7092
5.74k
              ThrowPointExpectedException(token,exception);
7093
5.74k
            if (*token == ',')
7094
448
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7095
5.74k
            x=GetDrawValue(token,&next_token);
7096
5.74k
            if (token == next_token)
7097
5.73k
              ThrowPointExpectedException(token,exception);
7098
5.73k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7099
5.73k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7100
4.54k
              ThrowPointExpectedException(token,exception);
7101
4.54k
            if (*token == ',')
7102
2.95k
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7103
4.54k
            y=GetDrawValue(token,&next_token);
7104
4.54k
            if (token == next_token)
7105
2.49k
              ThrowPointExpectedException(token,exception);
7106
2.49k
            if (*p == ',')
7107
1.18k
              p++;
7108
2.49k
            end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
7109
2.49k
            end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
7110
2.49k
            points[i]=end;
7111
2.49k
          }
7112
4.70k
          if (strchr("CcSs",last_attribute) == (char *) NULL)
7113
284
            {
7114
284
              points[0]=point;
7115
284
              points[1]=point;
7116
284
            }
7117
23.5k
          for (i=0; i < 4; i++)
7118
18.8k
            (q+i)->point=points[i];
7119
4.70k
          if (TraceBezier(mvg_info,4) == MagickFalse)
7120
225
            return(-1);
7121
4.48k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
7122
4.48k
          mvg_info->offset+=(ssize_t) q->coordinates;
7123
4.48k
          q+=(ptrdiff_t) q->coordinates;
7124
4.48k
          point=end;
7125
4.48k
          last_attribute=attribute;
7126
4.69k
          while (isspace((int) ((unsigned char) *p)) != 0)
7127
208
            p++;
7128
4.48k
          if (*p == ',')
7129
657
            p++;
7130
4.48k
        } while (IsValidPoint(p) != MagickFalse);
7131
353
        break;
7132
578
      }
7133
385
      case 't':
7134
4.48k
      case 'T':
7135
4.48k
      {
7136
        /*
7137
          Quadratic Bézier curve.
7138
        */
7139
4.48k
        do
7140
8.65k
        {
7141
8.65k
          points[0]=points[2];
7142
8.65k
          points[1].x=2.0*points[2].x-points[1].x;
7143
8.65k
          points[1].y=2.0*points[2].y-points[1].y;
7144
16.7k
          for (i=2; i < 3; i++)
7145
8.65k
          {
7146
8.65k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7147
8.65k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7148
8.50k
              ThrowPointExpectedException(token,exception);
7149
8.50k
            if (*token == ',')
7150
24
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7151
8.50k
            x=GetDrawValue(token,&next_token);
7152
8.50k
            if (token == next_token)
7153
8.50k
              ThrowPointExpectedException(token,exception);
7154
8.50k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7155
8.50k
            if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7156
8.15k
              ThrowPointExpectedException(token,exception);
7157
8.15k
            if (*token == ',')
7158
3.22k
              (void) GetNextToken(p,&p,MagickPathExtent,token);
7159
8.15k
            y=GetDrawValue(token,&next_token);
7160
8.15k
            if (token == next_token)
7161
8.09k
              ThrowPointExpectedException(token,exception);
7162
8.09k
            end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
7163
8.09k
            end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
7164
8.09k
            points[i]=end;
7165
8.09k
          }
7166
8.65k
          if (status == MagickFalse)
7167
560
            break;
7168
8.09k
          if (strchr("QqTt",last_attribute) == (char *) NULL)
7169
1.92k
            {
7170
1.92k
              points[0]=point;
7171
1.92k
              points[1]=point;
7172
1.92k
            }
7173
32.3k
          for (i=0; i < 3; i++)
7174
24.2k
            (q+i)->point=points[i];
7175
8.09k
          if (TraceBezier(mvg_info,3) == MagickFalse)
7176
0
            return(-1);
7177
8.09k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
7178
8.09k
          mvg_info->offset+=(ssize_t) q->coordinates;
7179
8.09k
          q+=(ptrdiff_t) q->coordinates;
7180
8.09k
          point=end;
7181
8.09k
          last_attribute=attribute;
7182
8.09k
          while (isspace((int) ((unsigned char) *p)) != 0)
7183
0
            p++;
7184
8.09k
          if (*p == ',')
7185
3.37k
            p++;
7186
8.09k
        } while (IsValidPoint(p) != MagickFalse);
7187
4.48k
        break;
7188
4.48k
      }
7189
4.48k
      case 'v':
7190
4.07k
      case 'V':
7191
4.07k
      {
7192
        /*
7193
          Line to.
7194
        */
7195
4.07k
        do
7196
7.36k
        {
7197
7.36k
          (void) GetNextToken(p,&p,MagickPathExtent,token);
7198
7.36k
          if (IsValidListChar((int) ((unsigned char) *token)) == MagickFalse)
7199
7.15k
            ThrowPointExpectedException(token,exception);
7200
7.15k
          if (*token == ',')
7201
3.42k
            (void) GetNextToken(p,&p,MagickPathExtent,token);
7202
7.15k
          y=GetDrawValue(token,&next_token);
7203
7.15k
          if (token == next_token)
7204
7.14k
            ThrowPointExpectedException(token,exception);
7205
7.14k
          point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
7206
7.14k
          if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7207
0
            return(-1);
7208
7.14k
          q=(*mvg_info->primitive_info)+mvg_info->offset;
7209
7.14k
          if (TracePoint(q,point) == MagickFalse)
7210
0
            return(-1);
7211
7.14k
          mvg_info->offset+=(ssize_t) q->coordinates;
7212
7.14k
          q+=(ptrdiff_t) q->coordinates;
7213
7.14k
          while (isspace((int) ((unsigned char) *p)) != 0)
7214
0
            p++;
7215
7.14k
          if (*p == ',')
7216
1.76k
            p++;
7217
7.14k
        } while (IsValidPoint(p) != MagickFalse);
7218
4.07k
        break;
7219
4.07k
      }
7220
4.07k
      case 'z':
7221
200
      case 'Z':
7222
200
      {
7223
        /*
7224
          Close path.
7225
        */
7226
200
        point=start;
7227
200
        if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7228
0
          return(-1);
7229
200
        q=(*mvg_info->primitive_info)+mvg_info->offset;
7230
200
        if (TracePoint(q,point) == MagickFalse)
7231
0
          return(-1);
7232
200
        mvg_info->offset+=(ssize_t) q->coordinates;
7233
200
        q+=(ptrdiff_t) q->coordinates;
7234
200
        primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7235
200
        primitive_info->coordinates=(size_t) (q-primitive_info);
7236
200
        primitive_info->closed_subpath=MagickTrue;
7237
200
        number_coordinates+=primitive_info->coordinates;
7238
200
        primitive_info=q;
7239
200
        subpath_offset=mvg_info->offset;
7240
200
        z_count++;
7241
200
        break;
7242
200
      }
7243
154
      default:
7244
154
      {
7245
154
        ThrowPointExpectedException(token,exception);
7246
0
        break;
7247
200
      }
7248
21.1k
    }
7249
21.1k
  }
7250
5.54k
  if (status == MagickFalse)
7251
4.94k
    return(-1);
7252
602
  primitive_info=(*mvg_info->primitive_info)+subpath_offset;
7253
602
  primitive_info->coordinates=(size_t) (q-primitive_info);
7254
602
  number_coordinates+=primitive_info->coordinates;
7255
741k
  for (i=0; i < (ssize_t) number_coordinates; i++)
7256
740k
  {
7257
740k
    q--;
7258
740k
    q->primitive=primitive_type;
7259
740k
    if (z_count > 1)
7260
225
      q->method=FillToBorderMethod;
7261
740k
  }
7262
602
  q=primitive_info;
7263
602
  return((ssize_t) number_coordinates);
7264
5.54k
}
7265
7266
static MagickBooleanType TraceRectangle(PrimitiveInfo *primitive_info,
7267
  const PointInfo start,const PointInfo end)
7268
132
{
7269
132
  PointInfo
7270
132
    point;
7271
7272
132
  PrimitiveInfo
7273
132
    *p;
7274
7275
132
  ssize_t
7276
132
    i;
7277
7278
132
  p=primitive_info;
7279
132
  if (TracePoint(p,start) == MagickFalse)
7280
0
    return(MagickFalse);
7281
132
  p+=(ptrdiff_t) p->coordinates;
7282
132
  point.x=start.x;
7283
132
  point.y=end.y;
7284
132
  if (TracePoint(p,point) == MagickFalse)
7285
0
    return(MagickFalse);
7286
132
  p+=(ptrdiff_t) p->coordinates;
7287
132
  if (TracePoint(p,end) == MagickFalse)
7288
0
    return(MagickFalse);
7289
132
  p+=(ptrdiff_t) p->coordinates;
7290
132
  point.x=end.x;
7291
132
  point.y=start.y;
7292
132
  if (TracePoint(p,point) == MagickFalse)
7293
0
    return(MagickFalse);
7294
132
  p+=(ptrdiff_t) p->coordinates;
7295
132
  if (TracePoint(p,start) == MagickFalse)
7296
0
    return(MagickFalse);
7297
132
  p+=(ptrdiff_t) p->coordinates;
7298
132
  primitive_info->coordinates=(size_t) (p-primitive_info);
7299
132
  primitive_info->closed_subpath=MagickTrue;
7300
792
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7301
660
  {
7302
660
    p->primitive=primitive_info->primitive;
7303
660
    p--;
7304
660
  }
7305
132
  return(MagickTrue);
7306
132
}
7307
7308
static MagickBooleanType TraceRoundRectangle(MVGInfo *mvg_info,
7309
  const PointInfo start,const PointInfo end,PointInfo arc)
7310
0
{
7311
0
  PointInfo
7312
0
    degrees,
7313
0
    point,
7314
0
    segment;
7315
7316
0
  PrimitiveInfo
7317
0
    *primitive_info;
7318
7319
0
  PrimitiveInfo
7320
0
    *p;
7321
7322
0
  ssize_t
7323
0
    i;
7324
7325
0
  ssize_t
7326
0
    offset;
7327
7328
0
  offset=mvg_info->offset;
7329
0
  segment.x=fabs(end.x-start.x);
7330
0
  segment.y=fabs(end.y-start.y);
7331
0
  if ((segment.x < MagickEpsilon) || (segment.y < MagickEpsilon))
7332
0
    {
7333
0
      (*mvg_info->primitive_info+mvg_info->offset)->coordinates=0;
7334
0
      return(MagickTrue);
7335
0
    }
7336
0
  if (arc.x > (0.5*segment.x))
7337
0
    arc.x=0.5*segment.x;
7338
0
  if (arc.y > (0.5*segment.y))
7339
0
    arc.y=0.5*segment.y;
7340
0
  point.x=start.x+segment.x-arc.x;
7341
0
  point.y=start.y+arc.y;
7342
0
  degrees.x=270.0;
7343
0
  degrees.y=360.0;
7344
0
  if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7345
0
    return(MagickFalse);
7346
0
  p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7347
0
  mvg_info->offset+=(ssize_t) p->coordinates;
7348
0
  point.x=start.x+segment.x-arc.x;
7349
0
  point.y=start.y+segment.y-arc.y;
7350
0
  degrees.x=0.0;
7351
0
  degrees.y=90.0;
7352
0
  if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7353
0
    return(MagickFalse);
7354
0
  p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7355
0
  mvg_info->offset+=(ssize_t) p->coordinates;
7356
0
  point.x=start.x+arc.x;
7357
0
  point.y=start.y+segment.y-arc.y;
7358
0
  degrees.x=90.0;
7359
0
  degrees.y=180.0;
7360
0
  if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7361
0
    return(MagickFalse);
7362
0
  p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7363
0
  mvg_info->offset+=(ssize_t) p->coordinates;
7364
0
  point.x=start.x+arc.x;
7365
0
  point.y=start.y+arc.y;
7366
0
  degrees.x=180.0;
7367
0
  degrees.y=270.0;
7368
0
  if (TraceEllipse(mvg_info,point,arc,degrees) == MagickFalse)
7369
0
    return(MagickFalse);
7370
0
  p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7371
0
  mvg_info->offset+=(ssize_t) p->coordinates;
7372
0
  if (CheckPrimitiveExtent(mvg_info,PrimitiveExtentPad) == MagickFalse)
7373
0
    return(MagickFalse);
7374
0
  p=(*mvg_info->primitive_info)+(ptrdiff_t) mvg_info->offset;
7375
0
  if (TracePoint(p,(*mvg_info->primitive_info+offset)->point) == MagickFalse)
7376
0
    return(MagickFalse);
7377
0
  p+=(ptrdiff_t) p->coordinates;
7378
0
  mvg_info->offset=offset;
7379
0
  primitive_info=(*mvg_info->primitive_info)+offset;
7380
0
  primitive_info->coordinates=(size_t) (p-primitive_info);
7381
0
  primitive_info->closed_subpath=MagickTrue;
7382
0
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
7383
0
  {
7384
0
    p->primitive=primitive_info->primitive;
7385
0
    p--;
7386
0
  }
7387
0
  return(MagickTrue);
7388
0
}
7389
7390
static MagickBooleanType TraceSquareLinecap(PrimitiveInfo *primitive_info,
7391
  const size_t number_vertices,const double offset)
7392
0
{
7393
0
  double
7394
0
    distance;
7395
7396
0
  double
7397
0
    dx,
7398
0
    dy;
7399
7400
0
  ssize_t
7401
0
    i;
7402
7403
0
  ssize_t
7404
0
    j;
7405
7406
0
  dx=0.0;
7407
0
  dy=0.0;
7408
0
  for (i=1; i < (ssize_t) number_vertices; i++)
7409
0
  {
7410
0
    dx=primitive_info[0].point.x-primitive_info[i].point.x;
7411
0
    dy=primitive_info[0].point.y-primitive_info[i].point.y;
7412
0
    if ((fabs((double) dx) >= MagickEpsilon) ||
7413
0
        (fabs((double) dy) >= MagickEpsilon))
7414
0
      break;
7415
0
  }
7416
0
  if (i == (ssize_t) number_vertices)
7417
0
    i=(ssize_t) number_vertices-1L;
7418
0
  distance=hypot((double) dx,(double) dy);
7419
0
  primitive_info[0].point.x=(double) (primitive_info[i].point.x+
7420
0
    dx*(distance+offset)/distance);
7421
0
  primitive_info[0].point.y=(double) (primitive_info[i].point.y+
7422
0
    dy*(distance+offset)/distance);
7423
0
  for (j=(ssize_t) number_vertices-2; j >= 0;  j--)
7424
0
  {
7425
0
    dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
7426
0
    dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
7427
0
    if ((fabs((double) dx) >= MagickEpsilon) ||
7428
0
        (fabs((double) dy) >= MagickEpsilon))
7429
0
      break;
7430
0
  }
7431
0
  distance=hypot((double) dx,(double) dy);
7432
0
  primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
7433
0
    dx*(distance+offset)/distance);
7434
0
  primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
7435
0
    dy*(distance+offset)/distance);
7436
0
  return(MagickTrue);
7437
0
}
7438
7439
static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
7440
  const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
7441
38.4k
{
7442
83.4k
#define MaxStrokePad  (6*BezierQuantum+360)
7443
497k
#define CheckPathExtent(pad_p,pad_q) \
7444
497k
{   \
7445
497k
  if ((pad_p) > MaxBezierCoordinates) \
7446
497k
    stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7447
497k
  else \
7448
497k
    if ((p+(ptrdiff_t) (pad_p)) >= (ssize_t) extent_p) \
7449
497k
      { \
7450
3.24k
        if (~extent_p < (pad_p)) \
7451
3.24k
          stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7452
3.24k
        else \
7453
3.24k
          { \
7454
3.24k
            extent_p+=(pad_p); \
7455
3.24k
            stroke_p=(PointInfo *) ResizeQuantumMemory(stroke_p,extent_p+ \
7456
3.24k
              MaxStrokePad,sizeof(*stroke_p)); \
7457
3.24k
          } \
7458
3.24k
      } \
7459
497k
  if ((pad_q) > MaxBezierCoordinates) \
7460
497k
    stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7461
497k
  else \
7462
497k
    if ((q+(ptrdiff_t) (pad_q)) >= (ssize_t) extent_q) \
7463
497k
      { \
7464
3.38k
        if (~extent_q < (pad_q)) \
7465
3.38k
          stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7466
3.38k
        else \
7467
3.38k
          { \
7468
3.38k
            extent_q+=(pad_q); \
7469
3.38k
            stroke_q=(PointInfo *) ResizeQuantumMemory(stroke_q,extent_q+ \
7470
3.38k
              MaxStrokePad,sizeof(*stroke_q)); \
7471
3.38k
          } \
7472
3.38k
      } \
7473
497k
  if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL)) \
7474
497k
    { \
7475
0
      if (stroke_p != (PointInfo *) NULL) \
7476
0
        stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p); \
7477
0
      if (stroke_q != (PointInfo *) NULL) \
7478
0
        stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q); \
7479
0
      polygon_primitive=(PrimitiveInfo *) \
7480
0
        RelinquishMagickMemory(polygon_primitive); \
7481
0
      (void) ThrowMagickException(exception,GetMagickModule(), \
7482
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'",""); \
7483
0
      return((PrimitiveInfo *) NULL); \
7484
0
    } \
7485
497k
}
7486
7487
38.4k
  typedef struct _StrokeSegment
7488
38.4k
  {
7489
38.4k
    double
7490
38.4k
      p,
7491
38.4k
      q;
7492
38.4k
  } StrokeSegment;
7493
7494
38.4k
  double
7495
38.4k
    delta_theta,
7496
38.4k
    dot_product,
7497
38.4k
    mid,
7498
38.4k
    miterlimit;
7499
7500
38.4k
  MagickBooleanType
7501
38.4k
    closed_path;
7502
7503
38.4k
  PointInfo
7504
38.4k
    box_p[5],
7505
38.4k
    box_q[5],
7506
38.4k
    center,
7507
38.4k
    offset,
7508
38.4k
    *stroke_p,
7509
38.4k
    *stroke_q;
7510
7511
38.4k
  PrimitiveInfo
7512
38.4k
    *polygon_primitive,
7513
38.4k
    *stroke_polygon;
7514
7515
38.4k
  ssize_t
7516
38.4k
    i;
7517
7518
38.4k
  size_t
7519
38.4k
    arc_segments,
7520
38.4k
    extent_p,
7521
38.4k
    extent_q,
7522
38.4k
    number_vertices;
7523
7524
38.4k
  ssize_t
7525
38.4k
    j,
7526
38.4k
    n,
7527
38.4k
    p,
7528
38.4k
    q;
7529
7530
38.4k
  StrokeSegment
7531
38.4k
    dx = {0.0, 0.0},
7532
38.4k
    dy = {0.0, 0.0},
7533
38.4k
    inverse_slope = {0.0, 0.0},
7534
38.4k
    slope = {0.0, 0.0},
7535
38.4k
    theta = {0.0, 0.0};
7536
7537
  /*
7538
    Allocate paths.
7539
  */
7540
38.4k
  number_vertices=primitive_info->coordinates;
7541
38.4k
  polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7542
38.4k
    number_vertices+2UL,sizeof(*polygon_primitive));
7543
38.4k
  if (polygon_primitive == (PrimitiveInfo *) NULL)
7544
0
    {
7545
0
      (void) ThrowMagickException(exception,GetMagickModule(),
7546
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7547
0
      return((PrimitiveInfo *) NULL);
7548
0
    }
7549
38.4k
  (void) memcpy(polygon_primitive,primitive_info,(size_t) number_vertices*
7550
38.4k
    sizeof(*polygon_primitive));
7551
38.4k
  offset.x=primitive_info[number_vertices-1].point.x-primitive_info[0].point.x;
7552
38.4k
  offset.y=primitive_info[number_vertices-1].point.y-primitive_info[0].point.y;
7553
38.4k
  closed_path=(fabs(offset.x) < MagickEpsilon) &&
7554
38.3k
    (fabs(offset.y) < MagickEpsilon) ? MagickTrue : MagickFalse;
7555
38.4k
  if ((draw_info->linejoin == RoundJoin) ||
7556
35.1k
      ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
7557
3.39k
    {
7558
3.39k
      polygon_primitive[number_vertices]=primitive_info[1];
7559
3.39k
      number_vertices++;
7560
3.39k
    }
7561
38.4k
  polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
7562
  /*
7563
    Compute the slope for the first line segment, p.
7564
  */
7565
38.4k
  closed_path=primitive_info[0].closed_subpath;
7566
38.4k
  dx.p=0.0;
7567
38.4k
  dy.p=0.0;
7568
38.8k
  for (n=1; n < (ssize_t) number_vertices; n++)
7569
38.5k
  {
7570
38.5k
    dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
7571
38.5k
    dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
7572
38.5k
    if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
7573
38.1k
      break;
7574
38.5k
  }
7575
38.4k
  if (n == (ssize_t) number_vertices)
7576
352
    {
7577
352
      if ((draw_info->linecap != RoundCap) || (closed_path != MagickFalse))
7578
96
        {
7579
          /*
7580
            Zero length subpath.
7581
          */
7582
96
          stroke_polygon=(PrimitiveInfo *) AcquireCriticalMemory(
7583
96
            sizeof(*stroke_polygon));
7584
96
          stroke_polygon[0]=polygon_primitive[0];
7585
96
          stroke_polygon[0].coordinates=0;
7586
96
          polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7587
96
            polygon_primitive);
7588
96
          return(stroke_polygon);
7589
96
        }
7590
256
      n=(ssize_t) number_vertices-1L;
7591
256
    }
7592
38.3k
  extent_p=2*number_vertices;
7593
38.3k
  extent_q=2*number_vertices;
7594
38.3k
  stroke_p=(PointInfo *) AcquireQuantumMemory((size_t) extent_p+MaxStrokePad,
7595
38.3k
    sizeof(*stroke_p));
7596
38.3k
  stroke_q=(PointInfo *) AcquireQuantumMemory((size_t) extent_q+MaxStrokePad,
7597
38.3k
    sizeof(*stroke_q));
7598
38.3k
  if ((stroke_p == (PointInfo *) NULL) || (stroke_q == (PointInfo *) NULL))
7599
0
    {
7600
0
      if (stroke_p != (PointInfo *) NULL)
7601
0
        stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7602
0
      if (stroke_q != (PointInfo *) NULL)
7603
0
        stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7604
0
      polygon_primitive=(PrimitiveInfo *)
7605
0
        RelinquishMagickMemory(polygon_primitive);
7606
0
      (void) ThrowMagickException(exception,GetMagickModule(),
7607
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7608
0
      return((PrimitiveInfo *) NULL);
7609
0
    }
7610
38.3k
  slope.p=0.0;
7611
38.3k
  inverse_slope.p=0.0;
7612
38.3k
  if (fabs(dx.p) < MagickEpsilon)
7613
463
    {
7614
463
      if (dx.p >= 0.0)
7615
463
        slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7616
0
      else
7617
0
        slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7618
463
    }
7619
37.9k
  else
7620
37.9k
    if (fabs(dy.p) < MagickEpsilon)
7621
307
      {
7622
307
        if (dy.p >= 0.0)
7623
307
          inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7624
0
        else
7625
0
          inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7626
307
      }
7627
37.6k
    else
7628
37.6k
      {
7629
37.6k
        slope.p=dy.p/dx.p;
7630
37.6k
        inverse_slope.p=(-1.0*MagickSafeReciprocal(slope.p));
7631
37.6k
      }
7632
38.3k
  mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
7633
38.3k
  miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit*mid*mid);
7634
38.3k
  if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
7635
0
    (void) TraceSquareLinecap(polygon_primitive,number_vertices,mid);
7636
38.3k
  offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
7637
38.3k
  offset.y=(double) (offset.x*inverse_slope.p);
7638
38.3k
  if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
7639
36.4k
    {
7640
36.4k
      box_p[0].x=polygon_primitive[0].point.x-offset.x;
7641
36.4k
      box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
7642
36.4k
      box_p[1].x=polygon_primitive[n].point.x-offset.x;
7643
36.4k
      box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
7644
36.4k
      box_q[0].x=polygon_primitive[0].point.x+offset.x;
7645
36.4k
      box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
7646
36.4k
      box_q[1].x=polygon_primitive[n].point.x+offset.x;
7647
36.4k
      box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
7648
36.4k
    }
7649
1.93k
  else
7650
1.93k
    {
7651
1.93k
      box_p[0].x=polygon_primitive[0].point.x+offset.x;
7652
1.93k
      box_p[0].y=polygon_primitive[0].point.y+offset.y;
7653
1.93k
      box_p[1].x=polygon_primitive[n].point.x+offset.x;
7654
1.93k
      box_p[1].y=polygon_primitive[n].point.y+offset.y;
7655
1.93k
      box_q[0].x=polygon_primitive[0].point.x-offset.x;
7656
1.93k
      box_q[0].y=polygon_primitive[0].point.y-offset.y;
7657
1.93k
      box_q[1].x=polygon_primitive[n].point.x-offset.x;
7658
1.93k
      box_q[1].y=polygon_primitive[n].point.y-offset.y;
7659
1.93k
    }
7660
  /*
7661
    Create strokes for the line join attribute: bevel, miter, round.
7662
  */
7663
38.3k
  p=0;
7664
38.3k
  q=0;
7665
38.3k
  stroke_q[p++]=box_q[0];
7666
38.3k
  stroke_p[q++]=box_p[0];
7667
2.22M
  for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++)
7668
2.18M
  {
7669
    /*
7670
      Compute the slope for this line segment, q.
7671
    */
7672
2.18M
    dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
7673
2.18M
    dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
7674
2.18M
    dot_product=dx.q*dx.q+dy.q*dy.q;
7675
2.18M
    if (dot_product < 0.25)
7676
1.70M
      continue;
7677
474k
    slope.q=0.0;
7678
474k
    inverse_slope.q=0.0;
7679
474k
    if (fabs(dx.q) < MagickEpsilon)
7680
22
      {
7681
22
        if (dx.q >= 0.0)
7682
22
          slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7683
0
        else
7684
0
          slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7685
22
      }
7686
474k
    else
7687
474k
      if (fabs(dy.q) < MagickEpsilon)
7688
0
        {
7689
0
          if (dy.q >= 0.0)
7690
0
            inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
7691
0
          else
7692
0
            inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
7693
0
        }
7694
474k
      else
7695
474k
        {
7696
474k
          slope.q=dy.q/dx.q;
7697
474k
          inverse_slope.q=(-1.0*MagickSafeReciprocal(slope.q));
7698
474k
        }
7699
474k
    offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
7700
474k
    offset.y=(double) (offset.x*inverse_slope.q);
7701
474k
    dot_product=dy.q*offset.x-dx.q*offset.y;
7702
474k
    if (dot_product > 0.0)
7703
236k
      {
7704
236k
        box_p[2].x=polygon_primitive[n].point.x-offset.x;
7705
236k
        box_p[2].y=polygon_primitive[n].point.y-offset.y;
7706
236k
        box_p[3].x=polygon_primitive[i].point.x-offset.x;
7707
236k
        box_p[3].y=polygon_primitive[i].point.y-offset.y;
7708
236k
        box_q[2].x=polygon_primitive[n].point.x+offset.x;
7709
236k
        box_q[2].y=polygon_primitive[n].point.y+offset.y;
7710
236k
        box_q[3].x=polygon_primitive[i].point.x+offset.x;
7711
236k
        box_q[3].y=polygon_primitive[i].point.y+offset.y;
7712
236k
      }
7713
237k
    else
7714
237k
      {
7715
237k
        box_p[2].x=polygon_primitive[n].point.x+offset.x;
7716
237k
        box_p[2].y=polygon_primitive[n].point.y+offset.y;
7717
237k
        box_p[3].x=polygon_primitive[i].point.x+offset.x;
7718
237k
        box_p[3].y=polygon_primitive[i].point.y+offset.y;
7719
237k
        box_q[2].x=polygon_primitive[n].point.x-offset.x;
7720
237k
        box_q[2].y=polygon_primitive[n].point.y-offset.y;
7721
237k
        box_q[3].x=polygon_primitive[i].point.x-offset.x;
7722
237k
        box_q[3].y=polygon_primitive[i].point.y-offset.y;
7723
237k
      }
7724
474k
    if (fabs((double) (slope.p-slope.q)) < MagickEpsilon)
7725
275
      {
7726
275
        box_p[4]=box_p[1];
7727
275
        box_q[4]=box_q[1];
7728
275
      }
7729
474k
    else
7730
474k
      {
7731
474k
        box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
7732
474k
          box_p[3].y)/(slope.p-slope.q));
7733
474k
        box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
7734
474k
        box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
7735
474k
          box_q[3].y)/(slope.p-slope.q));
7736
474k
        box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
7737
474k
      }
7738
474k
    DisableMSCWarning(4127)
7739
474k
    CheckPathExtent(MaxStrokePad,MaxStrokePad);
7740
474k
    RestoreMSCWarning
7741
474k
    dot_product=dx.q*dy.p-dx.p*dy.q;
7742
474k
    if (dot_product <= 0.0)
7743
473k
      switch (draw_info->linejoin)
7744
473k
      {
7745
147k
        case BevelJoin:
7746
147k
        {
7747
147k
          stroke_q[q++]=box_q[1];
7748
147k
          stroke_q[q++]=box_q[2];
7749
147k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7750
147k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7751
147k
          if (dot_product <= miterlimit)
7752
147k
            stroke_p[p++]=box_p[4];
7753
0
          else
7754
0
            {
7755
0
              stroke_p[p++]=box_p[1];
7756
0
              stroke_p[p++]=box_p[2];
7757
0
            }
7758
147k
          break;
7759
0
        }
7760
303k
        case MiterJoin:
7761
303k
        {
7762
303k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7763
303k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7764
303k
          if (dot_product <= miterlimit)
7765
302k
            {
7766
302k
              stroke_q[q++]=box_q[4];
7767
302k
              stroke_p[p++]=box_p[4];
7768
302k
            }
7769
956
          else
7770
956
            {
7771
956
              stroke_q[q++]=box_q[1];
7772
956
              stroke_q[q++]=box_q[2];
7773
956
              stroke_p[p++]=box_p[1];
7774
956
              stroke_p[p++]=box_p[2];
7775
956
            }
7776
303k
          break;
7777
0
        }
7778
22.1k
        case RoundJoin:
7779
22.1k
        {
7780
22.1k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7781
22.1k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7782
22.1k
          if (dot_product <= miterlimit)
7783
14
            stroke_p[p++]=box_p[4];
7784
22.1k
          else
7785
22.1k
            {
7786
22.1k
              stroke_p[p++]=box_p[1];
7787
22.1k
              stroke_p[p++]=box_p[2];
7788
22.1k
            }
7789
22.1k
          center=polygon_primitive[n].point;
7790
22.1k
          theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
7791
22.1k
          theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
7792
22.1k
          if (theta.q < theta.p)
7793
1.35k
            theta.q+=2.0*MagickPI;
7794
22.1k
          arc_segments=(size_t) CastDoubleToSsizeT(ceil((double) ((theta.q-
7795
22.1k
            theta.p)/(2.0*sqrt(MagickSafeReciprocal(mid))))));
7796
22.1k
          DisableMSCWarning(4127)
7797
22.1k
          CheckPathExtent(MaxStrokePad,arc_segments+MaxStrokePad);
7798
22.1k
          RestoreMSCWarning
7799
22.1k
          stroke_q[q].x=box_q[1].x;
7800
22.1k
          stroke_q[q].y=box_q[1].y;
7801
22.1k
          q++;
7802
112k
          for (j=1; j < (ssize_t) arc_segments; j++)
7803
90.2k
          {
7804
90.2k
            delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7805
90.2k
            stroke_q[q].x=(double) (center.x+mid*cos(fmod((double)
7806
90.2k
              (theta.p+delta_theta),DegreesToRadians(360.0))));
7807
90.2k
            stroke_q[q].y=(double) (center.y+mid*sin(fmod((double)
7808
90.2k
              (theta.p+delta_theta),DegreesToRadians(360.0))));
7809
90.2k
            q++;
7810
90.2k
          }
7811
22.1k
          stroke_q[q++]=box_q[2];
7812
22.1k
          break;
7813
22.1k
        }
7814
0
        default:
7815
0
          break;
7816
473k
      }
7817
758
    else
7818
758
      switch (draw_info->linejoin)
7819
758
      {
7820
0
        case BevelJoin:
7821
0
        {
7822
0
          stroke_p[p++]=box_p[1];
7823
0
          stroke_p[p++]=box_p[2];
7824
0
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7825
0
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7826
0
          if (dot_product <= miterlimit)
7827
0
            stroke_q[q++]=box_q[4];
7828
0
          else
7829
0
            {
7830
0
              stroke_q[q++]=box_q[1];
7831
0
              stroke_q[q++]=box_q[2];
7832
0
            }
7833
0
          break;
7834
0
        }
7835
6
        case MiterJoin:
7836
6
        {
7837
6
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7838
6
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7839
6
          if (dot_product <= miterlimit)
7840
5
            {
7841
5
              stroke_q[q++]=box_q[4];
7842
5
              stroke_p[p++]=box_p[4];
7843
5
            }
7844
1
          else
7845
1
            {
7846
1
              stroke_q[q++]=box_q[1];
7847
1
              stroke_q[q++]=box_q[2];
7848
1
              stroke_p[p++]=box_p[1];
7849
1
              stroke_p[p++]=box_p[2];
7850
1
            }
7851
6
          break;
7852
0
        }
7853
752
        case RoundJoin:
7854
752
        {
7855
752
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
7856
752
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
7857
752
          if (dot_product <= miterlimit)
7858
0
            stroke_q[q++]=box_q[4];
7859
752
          else
7860
752
            {
7861
752
              stroke_q[q++]=box_q[1];
7862
752
              stroke_q[q++]=box_q[2];
7863
752
            }
7864
752
          center=polygon_primitive[n].point;
7865
752
          theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
7866
752
          theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
7867
752
          if (theta.p < theta.q)
7868
48
            theta.p+=2.0*MagickPI;
7869
752
          arc_segments=(size_t) CastDoubleToSsizeT(ceil((double) ((theta.p-
7870
752
            theta.q)/(2.0*sqrt((double) (MagickSafeReciprocal(mid)))))));
7871
752
          DisableMSCWarning(4127)
7872
752
          CheckPathExtent(arc_segments+MaxStrokePad,MaxStrokePad);
7873
752
          RestoreMSCWarning
7874
752
          stroke_p[p++]=box_p[1];
7875
811k
          for (j=1; j < (ssize_t) arc_segments; j++)
7876
810k
          {
7877
810k
            delta_theta=(double) (j*(theta.q-theta.p)/arc_segments);
7878
810k
            stroke_p[p].x=(double) (center.x+mid*cos(fmod((double)
7879
810k
              (theta.p+delta_theta),DegreesToRadians(360.0))));
7880
810k
            stroke_p[p].y=(double) (center.y+mid*sin(fmod((double)
7881
810k
              (theta.p+delta_theta),DegreesToRadians(360.0))));
7882
810k
            p++;
7883
810k
          }
7884
752
          stroke_p[p++]=box_p[2];
7885
752
          break;
7886
752
        }
7887
0
        default:
7888
0
          break;
7889
758
      }
7890
474k
    slope.p=slope.q;
7891
474k
    inverse_slope.p=inverse_slope.q;
7892
474k
    box_p[0]=box_p[2];
7893
474k
    box_p[1]=box_p[3];
7894
474k
    box_q[0]=box_q[2];
7895
474k
    box_q[1]=box_q[3];
7896
474k
    dx.p=dx.q;
7897
474k
    dy.p=dy.q;
7898
474k
    n=i;
7899
474k
  }
7900
38.3k
  stroke_p[p++]=box_p[1];
7901
38.3k
  stroke_q[q++]=box_q[1];
7902
  /*
7903
    Trace stroked polygon.
7904
  */
7905
38.3k
  stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
7906
38.3k
    (p+q+2L),(size_t) (closed_path+2L)*sizeof(*stroke_polygon));
7907
38.3k
  if (stroke_polygon == (PrimitiveInfo *) NULL)
7908
0
    {
7909
0
      (void) ThrowMagickException(exception,GetMagickModule(),
7910
0
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
7911
0
      stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7912
0
      stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7913
0
      polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(
7914
0
        polygon_primitive);
7915
0
      return(stroke_polygon);
7916
0
    }
7917
1.42M
  for (i=0; i < (ssize_t) p; i++)
7918
1.38M
  {
7919
1.38M
    stroke_polygon[i]=polygon_primitive[0];
7920
1.38M
    stroke_polygon[i].point=stroke_p[i];
7921
1.38M
  }
7922
38.3k
  if (closed_path != MagickFalse)
7923
106
    {
7924
106
      stroke_polygon[i]=polygon_primitive[0];
7925
106
      stroke_polygon[i].point=stroke_polygon[0].point;
7926
106
      i++;
7927
106
    }
7928
851k
  for ( ; i < (ssize_t) (p+q+closed_path); i++)
7929
812k
  {
7930
812k
    stroke_polygon[i]=polygon_primitive[0];
7931
812k
    stroke_polygon[i].point=stroke_q[p+q+closed_path-(i+1)];
7932
812k
  }
7933
38.3k
  if (closed_path != MagickFalse)
7934
106
    {
7935
106
      stroke_polygon[i]=polygon_primitive[0];
7936
106
      stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
7937
106
      i++;
7938
106
    }
7939
38.3k
  stroke_polygon[i]=polygon_primitive[0];
7940
38.3k
  stroke_polygon[i].point=stroke_polygon[0].point;
7941
38.3k
  i++;
7942
38.3k
  stroke_polygon[i].primitive=UndefinedPrimitive;
7943
38.3k
  stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1);
7944
38.3k
  stroke_p=(PointInfo *) RelinquishMagickMemory(stroke_p);
7945
38.3k
  stroke_q=(PointInfo *) RelinquishMagickMemory(stroke_q);
7946
38.3k
  polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
7947
38.3k
  return(stroke_polygon);
7948
38.3k
}