Coverage Report

Created: 2026-05-24 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/render.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                 RRRR   EEEEE  N   N  DDDD   EEEEE  RRRR                     %
15
%                 R   R  E      NN  N  D   D  E      R   R                    %
16
%                 RRRR   EEE    N N N  D   D  EEE    RRRR                     %
17
%                 R R    E      N  NN  D   D  E      R R                      %
18
%                 R  R   EEEEE  N   N  DDDD   EEEEE  R  R                     %
19
%                                                                             %
20
%                                                                             %
21
%                   GraphicsMagick Image Drawing Methods                      %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1998                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
% Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon
33
% rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion",
34
% Graphics Gems, 1990.  Leonard Rosenthal and David Harr of Appligent
35
% (www.appligent.com) contributed the dash pattern, linecap stroking
36
% algorithm, and minor rendering improvements.
37
%
38
*/
39

40
/*
41
  Include declarations.
42
*/
43
#include "magick/studio.h"
44
#include "magick/alpha_composite.h"
45
#include "magick/attribute.h"
46
#include "magick/blob.h"
47
#include "magick/color.h"
48
#include "magick/color_lookup.h"
49
#include "magick/constitute.h"
50
#include "magick/enhance.h"
51
#include "magick/enum_strings.h"
52
#include "magick/gem.h"
53
#include "magick/gradient.h"
54
#include "magick/log.h"
55
#include "magick/monitor.h"
56
#include "magick/omp_data_view.h"
57
#include "magick/paint.h"
58
#include "magick/pixel_cache.h"
59
#include "magick/render.h"
60
#include "magick/resource.h"
61
#include "magick/transform.h"
62
#include "magick/utility.h"
63

64
/*
65
  Define declarations.
66
*/
67
3.12M
#define BezierQuantum  200
68
69
/* Maximum amount of recursion allowed when executing MVG */
70
#if !defined(MAX_DRAWIMAGE_RECURSION)
71
120k
#  define MAX_DRAWIMAGE_RECURSION 100
72
#endif /* !defined(MAX_DRAWIMAGE_RECURSION) */
73
/* Maximum number of points to allocate in PrimitiveInfo array */
74
#if !defined(PRIMITIVE_INFO_POINTS_MAX)
75
859k
#  define PRIMITIVE_INFO_POINTS_MAX ((~((size_t)0)) >> 8)
76
#endif /* !defined(PRIMITIVE_INFO_POINTS_MAX) */
77
/* Largest double value which can be converted to a long and survive AbsoluteValue() */
78
#if LONG_MAX == 2147483647L
79
#  define DTOLONG_MAX 2147483647.0
80
#  define DTOLONG_MIN -2147483647.0
81
#else
82
606k
#  define DTOLONG_MAX    9007199254740992.0
83
544k
#  define DTOLONG_MIN   -9007199254740992.0
84
#endif
85

86
/*
87
  Typedef declarations.
88
*/
89
90
typedef struct _EdgeInfo
91
{
92
  SegmentInfo
93
    bounds;
94
95
  double
96
    scanline;
97
98
  PointInfo
99
    *points;
100
101
  size_t
102
    highwater,
103
    number_points;
104
105
  long
106
    direction;
107
108
  MagickBool
109
    ghostline;
110
111
} EdgeInfo;
112
113
typedef struct _PolygonInfo
114
{
115
  EdgeInfo
116
    *edges;
117
118
  size_t
119
    number_edges;
120
} PolygonInfo;
121
122
typedef enum
123
{
124
  MoveToCode,
125
  OpenCode,
126
  GhostlineCode,
127
  LineToCode,
128
  EndCode
129
} PathInfoCode;
130
131
typedef struct _PathInfo
132
{
133
  PointInfo
134
    point;
135
136
  PathInfoCode
137
    code;
138
} PathInfo;
139
140
141
/*
142
  A data structure to help manage automatically growing a PrimitiveInfo array.
143
  Intended to be used in DrawImage() and the TraceXXX() functions.  See static
144
  function PrimitiveInfoRealloc().  Motivated by ticket #516.
145
*/
146
typedef struct _PrimitiveInfoMgr
147
{
148
  PrimitiveInfo ** pp_PrimitiveInfo;  /* address of a pointer to the start of the PrimitiveInfo array */
149
                                      /* (updated when the array is reallocated) */
150
151
  size_t *         p_AllocCount;      /* address of a location containing the number of elements allocated */
152
                                      /* (updated when the array is reallocated) */
153
154
  size_t           StoreStartingAt;   /* index to the element at which to start storing new elements */
155
                                      /* (must be updated before calling PrimitiveInfoRealloc()) */
156
157
  ExceptionInfo *  p_Exception;       /* for when reallocation fails */
158
} PrimitiveInfoMgr;
159
160
161
/*
162
  DrawInfoExtra allows for expansion of DrawInfo without increasing its
163
  size.  The internals are defined only in this source file.  Clients
164
  outside of this source file can access the internals via the provided
165
  access functions (see below).
166
*/
167
typedef struct _DrawInfoExtra
168
{
169
  char
170
    /*
171
      clip_path is (typically) the name of the attribute whose value contains
172
      the clipping path's graphical elements.
173
    */
174
    *clip_path,
175
    /*
176
      composite_path is (typically) the name of the attribute whose value contains
177
      the composite path's graphical elements.
178
    */
179
    *composite_path;
180
} DrawInfoExtra;
181

182
/* provide public access to the clip_path member of DrawInfo */
183
MagickExport char **
184
DrawInfoGetClipPath(const DrawInfo * draw_info)
185
587
{
186
587
  return(&draw_info->extra->clip_path);
187
587
}
188

189
/* provide public access to the composite_path member of DrawInfo */
190
MagickExport char **
191
DrawInfoGetCompositePath(const DrawInfo * draw_info)
192
0
{
193
0
  return(&draw_info->extra->composite_path);
194
0
}
195
196
/*
197
  Forward declarations.
198
*/
199
static PrimitiveInfo  /* added Image* param so DrawInfo::stroke_width can be clamped */
200
  *TraceStrokePolygon(const Image *,const DrawInfo *,const PrimitiveInfo *,ExceptionInfo *exception);
201
202
static MagickPassFail
203
  DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
204
                         const PolygonInfo *polygon_info) MAGICK_FUNC_WARN_UNUSED_RESULT,
205
  DrawPrimitive(Image *,const DrawInfo *,const PrimitiveInfo *) MAGICK_FUNC_WARN_UNUSED_RESULT,
206
  DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *) MAGICK_FUNC_WARN_UNUSED_RESULT,
207
  PrimitiveInfoRealloc(PrimitiveInfoMgr *p_PIMgr,const size_t Needed) MAGICK_FUNC_WARN_UNUSED_RESULT,
208
  TraceArc(PrimitiveInfoMgr *,const PointInfo,const PointInfo,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
209
  TraceArcPath(PrimitiveInfoMgr *,const PointInfo,const PointInfo,const PointInfo,
210
               const double,const unsigned int,const unsigned int) MAGICK_FUNC_WARN_UNUSED_RESULT,
211
  TraceBezier(PrimitiveInfoMgr *,const size_t) MAGICK_FUNC_WARN_UNUSED_RESULT,
212
  TraceCircle(PrimitiveInfoMgr *,const PointInfo,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
213
  TraceEllipse(PrimitiveInfoMgr *,const PointInfo,const PointInfo,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
214
  TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
215
  TracePath(Image *image,PrimitiveInfoMgr *p_PIMgr,const char *path,size_t *number_coordinates),
216
  TracePoint(PrimitiveInfo *,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
217
  TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
218
  TraceRoundRectangle(PrimitiveInfoMgr *,const PointInfo,const PointInfo,PointInfo) MAGICK_FUNC_WARN_UNUSED_RESULT,
219
  TraceSquareLinecap(PrimitiveInfo *,const unsigned long,const double) MAGICK_FUNC_WARN_UNUSED_RESULT;
220
221
static MagickBool
222
  /*IsDrawInfoClippingPath(const DrawInfo *),*/           /* is DrawInfo a clipping path */
223
  IsDrawInfoSVGCompliant(const DrawInfo *),               /* is DrawInfo drawn as SVG compliant */
224
  IsDrawInfoSVGCompliantClippingPath(const DrawInfo *);   /* is DrawInfo drawn as SVG compliant clipping path */
225
226
static void
227
  SetDrawInfoClippingPath(DrawInfo *, MagickBool ClippingPath),   /* tag DrawInfo as clipping path or not */
228
  SetDrawInfoSVGCompliant(DrawInfo *, MagickBool SVGCompliant);   /* tag DrawInfo as SVG compliant or not */
229
230
static void
231
#if 0
232
  DestroyGradientInfo(GradientInfo *),
233
#endif
234
  DestroyPolygonInfo(void *polygon_info_void);
235

236
/*
237
  Ticket #515 showed how an excessively big DrawInfo::stroke_width can cause writes
238
  beyond the end of an array of points when computing a stroked polygon.  So we want
239
  to clamp it to some reasonable value before using it in computations.  The value
240
  sqrt(2)*max(width,height) will always be >= the diagonal size of any image.  The
241
  computed upper limit will be twice this value.  Note that 1.415 is just slightly
242
  bigger than sqrt(2).
243
*/
244
245
6.80M
#define STROKE_WIDTH_LIMIT(image) ((2.0*1.415) * Max((image)->columns,(image)->rows))
246
247

248
/*
249
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250
%                                                                             %
251
%                                                                             %
252
%                                                                             %
253
%   C l o n e D r a w I n f o                                                 %
254
%                                                                             %
255
%                                                                             %
256
%                                                                             %
257
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258
%
259
%  CloneDrawInfo() makes a copy of the given draw info structure.  If NULL
260
%  is specified, a new DrawInfo structure is created initialized to
261
%  default values.
262
%
263
%  The format of the CloneDrawInfo method is:
264
%
265
%      DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
266
%        const DrawInfo *draw_info)
267
%
268
%  A description of each parameter follows:
269
%
270
%    o image_info: The image info.
271
%
272
%    o draw_info: The draw info.
273
%
274
%
275
*/
276
MagickExport DrawInfo *
277
CloneDrawInfo(const ImageInfo *image_info,const DrawInfo *draw_info)
278
3.77M
{
279
3.77M
  DrawInfo
280
3.77M
    *clone_info;
281
282
3.77M
  clone_info=MagickAllocateMemory(DrawInfo *,sizeof(DrawInfo));
283
3.77M
  if (clone_info == (DrawInfo *) NULL)
284
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
285
3.77M
      UnableToAllocateDrawInfo);
286
3.77M
  GetDrawInfo(image_info,clone_info);
287
3.77M
  if (draw_info == (DrawInfo *) NULL)
288
38.0k
    return(clone_info);
289
3.73M
  if (clone_info->primitive != (char *) NULL)
290
0
    (void) CloneString(&clone_info->primitive,draw_info->primitive);
291
3.73M
  if (draw_info->geometry != (char *) NULL)
292
20.9k
    clone_info->geometry=AllocateString(draw_info->geometry);
293
3.73M
  clone_info->affine=draw_info->affine;
294
3.73M
  clone_info->gravity=draw_info->gravity;
295
3.73M
  clone_info->fill=draw_info->fill;
296
3.73M
  clone_info->stroke=draw_info->stroke;
297
3.73M
  clone_info->stroke_width=draw_info->stroke_width;
298
3.73M
  if (draw_info->fill_pattern != (Image *) NULL)
299
103k
    clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,True,
300
103k
      &draw_info->fill_pattern->exception);
301
3.63M
  else
302
3.63M
    if (draw_info->tile != (Image *) NULL)
303
0
      clone_info->fill_pattern=CloneImage(draw_info->tile,0,0,True,
304
0
        &draw_info->tile->exception);
305
3.73M
  clone_info->tile=(Image *) NULL;  /* tile is deprecated */
306
3.73M
  if (draw_info->stroke_pattern != (Image *) NULL)
307
63.2k
    clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,True,
308
63.2k
      &draw_info->stroke_pattern->exception);
309
3.73M
  clone_info->stroke_antialias=draw_info->stroke_antialias;
310
3.73M
  clone_info->text_antialias=draw_info->text_antialias;
311
3.73M
  clone_info->fill_rule=draw_info->fill_rule;
312
3.73M
  clone_info->linecap=draw_info->linecap;
313
3.73M
  clone_info->linejoin=draw_info->linejoin;
314
3.73M
  clone_info->miterlimit=draw_info->miterlimit;
315
3.73M
  clone_info->dash_offset=draw_info->dash_offset;
316
3.73M
  clone_info->decorate=draw_info->decorate;
317
3.73M
  clone_info->compose=draw_info->compose;
318
3.73M
  if (draw_info->text != (char *) NULL)
319
77.3k
    clone_info->text=AllocateString(draw_info->text);
320
3.73M
  if (draw_info->font != (char *) NULL)
321
183k
    (void) CloneString(&clone_info->font,draw_info->font);
322
3.73M
  if (draw_info->family != (char *) NULL)
323
118k
    clone_info->family=AllocateString(draw_info->family);
324
3.73M
  clone_info->style=draw_info->style;
325
3.73M
  clone_info->stretch=draw_info->stretch;
326
3.73M
  clone_info->weight=draw_info->weight;
327
3.73M
  if (draw_info->encoding != (char *) NULL)
328
461
    clone_info->encoding=AllocateString(draw_info->encoding);
329
3.73M
  clone_info->pointsize=draw_info->pointsize;
330
3.73M
  if (draw_info->density != (char *) NULL)
331
0
    (void) CloneString(&clone_info->density,draw_info->density);
332
3.73M
  clone_info->align=draw_info->align;
333
3.73M
  clone_info->undercolor=draw_info->undercolor;
334
3.73M
  clone_info->border_color=draw_info->border_color;
335
3.73M
  if (draw_info->server_name != (char *) NULL)
336
0
    (void) CloneString(&clone_info->server_name,draw_info->server_name);
337
3.73M
  if (draw_info->dash_pattern != (double *) NULL)
338
3.13M
    {
339
3.13M
      register long
340
3.13M
        x;
341
342
9.58M
      for (x=0; draw_info->dash_pattern[x] != 0.0; x++);
343
3.13M
      clone_info->dash_pattern=
344
3.13M
        MagickAllocateArray(double *,((size_t) x+1),sizeof(double));
345
3.13M
      if (clone_info->dash_pattern == (double *) NULL)
346
0
        MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
347
3.13M
          UnableToAllocateDashPattern);
348
3.13M
      (void) memcpy(clone_info->dash_pattern,draw_info->dash_pattern,
349
3.13M
        ((size_t) x+1)*sizeof(double));
350
3.13M
    }
351
3.73M
  if (draw_info->extra->clip_path != (char *) NULL)
352
417k
    clone_info->extra->clip_path=AllocateString(draw_info->extra->clip_path);
353
3.73M
  if (draw_info->extra->composite_path != (char *) NULL)
354
187k
    clone_info->extra->composite_path=AllocateString(draw_info->extra->composite_path);
355
3.73M
  clone_info->bounds=draw_info->bounds;
356
3.73M
  clone_info->clip_units=draw_info->clip_units;
357
3.73M
  clone_info->render=draw_info->render;
358
3.73M
  clone_info->opacity=draw_info->opacity;
359
3.73M
  clone_info->element_reference=draw_info->element_reference;
360
3.73M
  clone_info->flags=draw_info->flags;   /* contains SVG compliance bit, etc. */
361
3.73M
  return(clone_info);
362
3.73M
}
363

364
/*
365
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366
%                                                                             %
367
%                                                                             %
368
%                                                                             %
369
+   C o n v e r t P a t h T o P o l y g o n                                   %
370
%                                                                             %
371
%                                                                             %
372
%                                                                             %
373
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374
%
375
%  Method ConvertPathToPolygon converts a path to the more efficient sorted
376
%  rendering form.
377
%
378
%  The format of the ConvertPathToPolygon method is:
379
%
380
%      PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
381
%        const PathInfo *path_info)
382
%
383
%  A description of each parameter follows:
384
%
385
%    o Method ConvertPathToPolygon returns the path in a more efficient sorted
386
%      rendering form of type PolygonInfo.
387
%
388
%    o draw_info: Specifies a pointer to an DrawInfo structure.
389
%
390
%    o path_info: Specifies a pointer to an PathInfo structure.
391
%
392
%
393
*/
394
395
#if defined(__cplusplus) || defined(c_plusplus)
396
extern "C" {
397
#endif
398
399
/*
400
  Ticket #562: Inconsistent results from qsort callback.
401
402
  NOTE: The right-handed coordinate system is: x right positive, y down positive,
403
  z into the plane positive.
404
405
  Prior to fixing ticket #562, the compare procedure in CompareEdges() looked at the
406
  first segment in each edge (edges can have multiple segments) and compared them as
407
  follows:
408
409
    (1) Within MagickEpsilon, the edge with the smaller starting y coordinate should come
410
        first.  Otherwise:
411
    (2) Within MagickEpsilon, the edge with the smaller starting x coordinate should come
412
        first.  Otherwise:
413
    (3) As indicated by the cross product v0 X v1, the edge that rotates counter-clockwise
414
        (as viewed) into the other edge through the smaller angle between them should come
415
        first.  I.e., if v0 X v1 is negative, v0 should come first.
416
417
  This procedure fails to properly differentiate between parallel and anti-parallel edges
418
  whose starting x and y coordinates are within MagickEpsilon of each other (the cross
419
  product of such edges is zero).  For these cases, the result for (v0,v1) says v0 comes
420
  first, but the result for (v1,v0) says v1 comes first (the problem reported by ticket #562).
421
422
  It's unclear to me why the x and y coordinate comparisons should use MagickEpsilon, but to
423
  avoid changing existing behavior this part of the code has not been changed.
424
425
  The new compare procedure is as follows:
426
427
    (1) Within MagickEpsilon, the edge with the smaller starting y coordinate should come
428
        first (UNCHANGED).  Otherwise:
429
    (2) Within MagickEpsilon, the edge with the smaller starting x coordinate should come
430
        first (UNCHANGED).  Otherwise:
431
    (3) As indicated by the cross product v0 X v1, the edge that rotates counter-clockwise
432
        (as viewed) into the other edge through the smaller angle between them should come
433
        first AS LONG AS THE ANGLE BETWEEN THEM IS NON-ZERO.  I.e., if v0 X v1 is STRICTLY
434
        NEGATIVE, v0 should come first.  If v0 X v1 IS STRICTLY POSITIVE, v1 should come
435
        first.  Otherwise:
436
    (4) (If we get here, the edges are either parallel or anti-parallel, but their starting
437
        points may or may not be identical.)  The edge with the smaller starting y coordinate
438
        should come first (DO NOT use MagickEpsilon).  Otherwise:
439
    (5) The edge with the smaller starting x coordinate should come first (DO NOT use
440
        MagickEpsilon).  Otherwise:
441
    (6) (If we get here, the edges starting points are identical) The edge with the smaller
442
        ending y coordinate should come first.  Otherwise:
443
    (7) The edge with the smaller ending x coordinate should come first.  Otherwise:
444
    (8) The edges are identical with respect to their first segments; return 0 to indicate
445
        this.  Note that previously CompareEdges() never returned a 0.
446
447
  While it may seem that steps 4 - 8 are a lot of additional code, note that this code will
448
  be executed very infrequently, since most cases will be resolved by steps 1 - 3.
449
450
  The new code subtracts the two coordinate values and then compares the result to +/- MagickEpsilon.
451
  When using finite precision floating point arithmetic, this computation is numerically more
452
  accurate that adding +/- MagickEpsilon to one coordinate and then comparing that result to
453
  the other coordinate.
454
*/
455
456
static int
457
CompareEdges(const void *edge0,const void *edge1)
458
106M
{
459
106M
  register const PointInfo
460
106M
    *v0Points,
461
106M
    *v1Points;
462
463
106M
  double
464
106M
    v0CROSSv1,
465
106M
    v0x0,
466
106M
    v0y0,
467
106M
    v1x0,
468
106M
    v1y0,
469
106M
    DeltaYStart,
470
106M
    DeltaXStart,
471
106M
    DeltaYEnd,
472
106M
    DeltaXEnd,
473
106M
    NegMagickEpsilon = -MagickEpsilon;
474
475
  /*
476
    Compare two edges.
477
  */
478
106M
  v0Points=((const EdgeInfo *) edge0)->points;
479
106M
  v1Points=((const EdgeInfo *) edge1)->points;
480
481
  /* (1) the edge with the smaller starting y coordinate should come first */
482
106M
  DeltaYStart = (v0y0 = v0Points[0].y) - (v1y0 = v1Points[0].y);
483
106M
  if  ( DeltaYStart < NegMagickEpsilon )
484
40.5M
    return(-1);  /* v0y0 + epsi < v1y0 */
485
66.1M
  if  ( DeltaYStart > MagickEpsilon )
486
37.7M
    return(1);  /* v0y0 - epsi > v1y0 */
487
488
  /* y start coordinates are the same within MagickEpsilon */
489
490
  /* (2) the edge with the smaller starting x coordinate should come first */
491
28.3M
  DeltaXStart = (v0x0 = v0Points[0].x) - (v1x0 = v1Points[0].x);
492
28.3M
  if  ( DeltaXStart < NegMagickEpsilon )
493
3.93M
    return(-1);  /* v0x0 + epsi < v1x0 */
494
24.4M
  if  ( DeltaXStart > MagickEpsilon )
495
4.37M
    return(1);  /* v0x0 - epsi > v1x0 */
496
497
  /* y and x start coordinates are the same within MagickEpsilon */
498
499
  /* compute cross product v0 X v1 */
500
20.0M
  v0CROSSv1 = (v0Points[1].x-v0x0)*(v1Points[1].y-v1y0)-(v0Points[1].y-v0y0)*(v1Points[1].x-v1x0);
501
502
  /* (3) the edge that rotates ccwise through the smaller angle between them should come first */
503
20.0M
  if  ( v0CROSSv1 < 0.0 )
504
3.17M
  return(-1);
505
16.9M
  if  ( v0CROSSv1 > 0.0 )
506
10.0M
    return(1);
507
508
  /* edges are either parallel or anti-parallel; check (4) y and (5) x start coords again w/o MagickEpsilon */
509
6.87M
  if  ( DeltaYStart < 0.0 )
510
1.36k
    return(-1);  /* v0y0 < v1y0 */
511
6.87M
  if  ( DeltaYStart > 0.0 )
512
1.69k
    return(1);  /* v0y0 > v1y0 */
513
6.87M
  if  ( DeltaXStart < 0.0 )
514
39.4k
    return(-1);  /* v0x0 < v1x0 */
515
6.83M
  if  ( DeltaXStart > 0.0 )
516
195k
    return(1);  /* v0x0 > v1x0 */
517
518
  /* starting points are the same; compare (6) y and (7) x ending points */
519
6.63M
  DeltaYEnd = v0Points[1].y - v1Points[1].y;
520
6.63M
  if  ( DeltaYEnd < 0.0 )
521
48.8k
    return(-1);  /* v0y1 < v1y1 */
522
6.58M
  if  ( DeltaYEnd > 0.0 )
523
2.63M
    return(1);  /* v0y1 > v1y1 */
524
3.94M
  DeltaXEnd = v0Points[1].x - v1Points[1].x;
525
3.94M
  if  ( DeltaXEnd < 0.0 )
526
32.8k
    return(-1);  /* v0x1 < v1x1 */
527
3.91M
  if  ( DeltaXEnd > 0.0 )
528
257k
    return(1);  /* v0x1 > v1x1 */
529
530
  /* (8) the edges are identical with respect to their first segments */
531
3.65M
  return(0);
532
3.91M
}
533
534
#if defined(__cplusplus) || defined(c_plusplus)
535
}
536
#endif
537
538
static void
539
LogPolygonInfo(const PolygonInfo *polygon_info)
540
0
{
541
0
  register EdgeInfo
542
0
    *p;
543
544
0
  size_t
545
0
    i;
546
547
0
  register size_t
548
0
    j;
549
550
0
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    begin active-edge");
551
0
  p=polygon_info->edges;
552
0
  for (i=0; i < polygon_info->number_edges; i++)
553
0
  {
554
0
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"      edge %" MAGICK_SIZE_T_F "u:",(MAGICK_SIZE_T) i);
555
0
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"      direction: %s",
556
0
      p->direction ? "down" : "up");
557
0
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"      ghostline: %s",
558
0
      p->ghostline ? "transparent" : "opaque");
559
0
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),
560
0
      "      bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,p->bounds.x2,
561
0
      p->bounds.y2);
562
0
    for (j=0; j < p->number_points; j++)
563
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"        %g,%g",
564
0
        p->points[j].x,p->points[j].y);
565
0
    p++;
566
0
  }
567
0
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end active-edge");
568
0
}
569
570
static void
571
ReversePoints(PointInfo *points,const size_t number_points)
572
18.5M
{
573
18.5M
  PointInfo
574
18.5M
    point;
575
576
18.5M
  register size_t
577
18.5M
    i;
578
579
120M
  for (i=0; i < (number_points >> 1); i++)
580
101M
  {
581
101M
    point=points[i];
582
101M
    points[i]=points[number_points-(i+1)];
583
101M
    points[number_points-(i+1)]=point;
584
101M
  }
585
18.5M
}
586
587
static PolygonInfo *
588
ConvertPathToPolygon(const PathInfo *path_info, ExceptionInfo *exception)
589
3.69M
{
590
3.69M
  size_t
591
3.69M
    edge,
592
3.69M
    number_edges,
593
3.69M
    number_points;
594
595
3.69M
  long
596
3.69M
    direction,
597
3.69M
    next_direction;
598
599
3.69M
  PointInfo
600
3.69M
    point,
601
3.69M
    *points;
602
603
3.69M
  PolygonInfo
604
3.69M
    *polygon_info;
605
606
3.69M
  SegmentInfo
607
3.69M
    bounds;
608
609
3.69M
  register unsigned long
610
3.69M
    n;
611
612
3.69M
  register long
613
3.69M
    i;
614
615
3.69M
  MagickBool
616
3.69M
    ghostline;
617
618
  /*
619
    Convert a path to the more efficient sorted rendering form.
620
  */
621
3.69M
  polygon_info=MagickAllocateMemory(PolygonInfo *,sizeof(PolygonInfo));
622
3.69M
  if (polygon_info == (PolygonInfo *) NULL)
623
0
    return((PolygonInfo *) NULL);
624
3.69M
  number_edges=16;
625
3.69M
  polygon_info->edges=
626
3.69M
    MagickAllocateResourceLimitedArray(EdgeInfo *,number_edges,sizeof(EdgeInfo));
627
3.69M
  if (polygon_info->edges == (EdgeInfo *) NULL)
628
1
    {
629
1
      DestroyPolygonInfo(polygon_info);
630
1
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
631
1
                      UnableToDrawOnImage);
632
1
      return((PolygonInfo *) NULL);
633
1
    }
634
3.69M
  direction=0;
635
3.69M
  edge=0;
636
3.69M
  ghostline=MagickFalse;
637
3.69M
  n=0;
638
3.69M
  number_points=0;
639
3.69M
  points=(PointInfo *) NULL;
640
3.69M
  (void) memset(&point,0,sizeof(PointInfo));
641
3.69M
  (void) memset(&bounds,0,sizeof(SegmentInfo));
642
3.69M
  polygon_info->edges[edge].number_points=n;
643
3.69M
  polygon_info->edges[edge].scanline=0.0;
644
3.69M
  polygon_info->edges[edge].highwater=0;
645
3.69M
  polygon_info->edges[edge].ghostline=ghostline;
646
3.69M
  polygon_info->edges[edge].direction=direction;
647
3.69M
  polygon_info->edges[edge].points=points;
648
3.69M
  polygon_info->edges[edge].bounds=bounds;
649
3.69M
  polygon_info->number_edges=0;
650
398M
  for (i=0; path_info[i].code != EndCode; i++)
651
394M
  {
652
394M
    if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
653
384M
        (path_info[i].code == GhostlineCode))
654
12.4M
      {
655
        /*
656
          Move to.
657
        */
658
12.4M
        if ((points != (PointInfo *) NULL) && (n >= 2))
659
2.85M
          {
660
2.85M
            if (edge == number_edges)
661
3.85k
              {
662
3.85k
                EdgeInfo *new_edges;
663
3.85k
                number_edges<<=1;
664
3.85k
                new_edges=MagickReallocateResourceLimitedArray(EdgeInfo *,
665
3.85k
                                                               polygon_info->edges,
666
3.85k
                                                               number_edges,
667
3.85k
                                                               sizeof(EdgeInfo));
668
3.85k
                if (new_edges == (EdgeInfo *) NULL)
669
2
                  {
670
2
                    MagickFreeResourceLimitedMemory(PointInfo *, points);
671
2
                    DestroyPolygonInfo(polygon_info);
672
2
                    ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
673
2
                                    UnableToDrawOnImage);
674
2
                    return((PolygonInfo *) NULL);
675
2
                  }
676
3.84k
                polygon_info->edges=new_edges;
677
3.84k
              }
678
2.85M
            polygon_info->edges[edge].number_points=n;
679
2.85M
            polygon_info->edges[edge].scanline=(-1.0);
680
2.85M
            polygon_info->edges[edge].highwater=0;
681
2.85M
            polygon_info->edges[edge].ghostline=ghostline;
682
2.85M
            polygon_info->edges[edge].direction=direction > 0;
683
2.85M
            if (direction < 0)
684
1.75M
              ReversePoints(points,n);
685
2.85M
            polygon_info->edges[edge].points=points;
686
2.85M
            polygon_info->edges[edge].bounds=bounds;
687
2.85M
            polygon_info->edges[edge].bounds.y1=points[0].y;
688
2.85M
            polygon_info->edges[edge].bounds.y2=points[n-1].y;
689
2.85M
            points=(PointInfo *) NULL;
690
2.85M
#if !defined(__COVERITY__) /* 384800 Unused value */
691
2.85M
            ghostline=MagickFalse;
692
2.85M
#endif /* if !defined(__COVERITY__) */
693
2.85M
            edge++;
694
2.85M
            polygon_info->number_edges=edge;
695
2.85M
          }
696
12.4M
        if (points == (PointInfo *) NULL)
697
6.54M
          {
698
6.54M
            number_points=16;
699
6.54M
            points=
700
6.54M
              MagickAllocateResourceLimitedArray(PointInfo *,
701
6.54M
                                                 number_points,sizeof(PointInfo));
702
6.54M
            if (points == (PointInfo *) NULL)
703
18
              {
704
18
                DestroyPolygonInfo(polygon_info);
705
18
                ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
706
18
                                UnableToDrawOnImage);
707
18
                return((PolygonInfo *) NULL);
708
18
              }
709
6.54M
          }
710
12.4M
        ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
711
12.4M
        point=path_info[i].point;
712
12.4M
        points[0]=point;
713
12.4M
        bounds.x1=point.x;
714
12.4M
        bounds.x2=point.x;
715
12.4M
        direction=0;
716
12.4M
        n=1;
717
12.4M
        continue;
718
12.4M
      }
719
    /*
720
      Line to.
721
    */
722
381M
    next_direction=((path_info[i].point.y > point.y) ||
723
193M
      ((path_info[i].point.y == point.y) &&
724
192M
       (path_info[i].point.x > point.x))) ? 1 : -1;
725
381M
    if ((points != (PointInfo *) NULL) && (direction != 0) &&
726
375M
        (direction != next_direction))
727
28.3M
      {
728
        /*
729
          New edge.
730
        */
731
28.3M
        point=points[n-1];
732
28.3M
        if (edge == number_edges)
733
87.0k
          {
734
87.0k
            EdgeInfo *new_edges;
735
87.0k
            number_edges<<=1;
736
87.0k
            new_edges=MagickReallocateResourceLimitedArray(EdgeInfo *,
737
87.0k
                                                           polygon_info->edges,
738
87.0k
                                                           number_edges,sizeof(EdgeInfo));
739
87.0k
            if (new_edges == (EdgeInfo *) NULL)
740
28
              {
741
28
                MagickFreeResourceLimitedMemory(PointInfo *, points);
742
28
                DestroyPolygonInfo(polygon_info);
743
28
                ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
744
28
                                UnableToDrawOnImage);
745
28
                return((PolygonInfo *) NULL);
746
28
              }
747
87.0k
            polygon_info->edges=new_edges;
748
87.0k
          }
749
28.3M
        polygon_info->edges[edge].number_points=n;
750
28.3M
        polygon_info->edges[edge].scanline=(-1.0);
751
28.3M
        polygon_info->edges[edge].highwater=0;
752
28.3M
        polygon_info->edges[edge].ghostline=ghostline;
753
28.3M
        polygon_info->edges[edge].direction=direction > 0;
754
28.3M
        if (direction < 0)
755
13.6M
          ReversePoints(points,n);
756
28.3M
        polygon_info->edges[edge].points=points;
757
28.3M
        polygon_info->edges[edge].bounds=bounds;
758
28.3M
        polygon_info->edges[edge].bounds.y1=points[0].y;
759
28.3M
        polygon_info->edges[edge].bounds.y2=points[n-1].y;
760
28.3M
        points=(PointInfo *) NULL;
761
28.3M
        polygon_info->number_edges=edge+1;
762
28.3M
        number_points=16;
763
28.3M
        points=
764
28.3M
          MagickAllocateResourceLimitedArray(PointInfo *,
765
28.3M
                                             number_points,sizeof(PointInfo));
766
28.3M
        if (points == (PointInfo *) NULL)
767
23
          {
768
23
            DestroyPolygonInfo(polygon_info);
769
23
            ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
770
23
                            UnableToDrawOnImage);
771
23
            return((PolygonInfo *) NULL);
772
23
          }
773
28.3M
        n=1;
774
28.3M
        ghostline=MagickFalse;
775
28.3M
        points[0]=point;
776
28.3M
        bounds.x1=point.x;
777
28.3M
        bounds.x2=point.x;
778
28.3M
        edge++;
779
28.3M
      }
780
381M
    direction=next_direction;
781
381M
    if (points == (PointInfo *) NULL)
782
0
      continue;
783
381M
    if (n == number_points)
784
1.08M
      {
785
1.08M
        PointInfo *new_points;
786
1.08M
        number_points<<=1;
787
1.08M
        new_points=MagickReallocateResourceLimitedArray(PointInfo *,points,
788
1.08M
                                                        number_points,sizeof(PointInfo));
789
1.08M
        if (new_points == (PointInfo *) NULL)
790
79
          {
791
79
            MagickFreeResourceLimitedMemory(PointInfo *, points);
792
79
            DestroyPolygonInfo(polygon_info);
793
79
            ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
794
79
                            UnableToDrawOnImage);
795
79
            return((PolygonInfo *) NULL);
796
79
          }
797
1.08M
        points=new_points;
798
1.08M
      }
799
381M
    point=path_info[i].point;
800
381M
    points[n]=point;
801
381M
    if (point.x < bounds.x1)
802
138M
      bounds.x1=point.x;
803
381M
    if (point.x > bounds.x2)
804
142M
      bounds.x2=point.x;
805
381M
    n++;
806
381M
  }
807
3.69M
  if (points != (PointInfo *) NULL)
808
3.69M
    {
809
3.69M
      if (n < 2)
810
2.52k
        {
811
2.52k
          MagickFreeResourceLimitedMemory(PointInfo *, points);
812
2.52k
        }
813
3.69M
      else
814
3.69M
        {
815
3.69M
          if (edge == number_edges)
816
10.9k
            {
817
10.9k
              EdgeInfo *new_edges;
818
10.9k
              number_edges<<=1;
819
10.9k
              new_edges=MagickReallocateResourceLimitedArray(EdgeInfo *,polygon_info->edges,
820
10.9k
                                                             number_edges,sizeof(EdgeInfo));
821
10.9k
              if (new_edges == (EdgeInfo *) NULL)
822
0
                {
823
0
                  MagickFreeResourceLimitedMemory(PointInfo *, points);
824
0
                  DestroyPolygonInfo(polygon_info);
825
0
                  ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
826
0
                                  UnableToDrawOnImage);
827
0
                  return((PolygonInfo *) NULL);
828
0
                }
829
10.9k
              polygon_info->edges=new_edges;
830
10.9k
            }
831
3.69M
          polygon_info->edges[edge].number_points=n;
832
3.69M
          polygon_info->edges[edge].scanline=(-1.0);
833
3.69M
          polygon_info->edges[edge].highwater=0;
834
3.69M
          polygon_info->edges[edge].ghostline=ghostline;
835
3.69M
          polygon_info->edges[edge].direction=direction > 0;
836
3.69M
          if (direction < 0)
837
3.06M
            ReversePoints(points,n);
838
3.69M
          polygon_info->edges[edge].points=points;
839
3.69M
          polygon_info->edges[edge].bounds=bounds;
840
3.69M
          polygon_info->edges[edge].bounds.y1=points[0].y;
841
3.69M
          polygon_info->edges[edge].bounds.y2=points[n-1].y;
842
3.69M
          points=(PointInfo *) NULL;
843
3.69M
          ghostline=MagickFalse;
844
3.69M
          edge++;
845
3.69M
          polygon_info->number_edges=edge;
846
3.69M
        }
847
3.69M
    }
848
3.69M
  polygon_info->number_edges=edge;
849
3.69M
  qsort(polygon_info->edges,polygon_info->number_edges,sizeof(EdgeInfo),
850
3.69M
    CompareEdges);
851
3.69M
  if (IsEventLogged(RenderEvent))
852
0
    LogPolygonInfo(polygon_info);
853
3.69M
  return(polygon_info);
854
3.69M
}
855

856
/*
857
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858
%                                                                             %
859
%                                                                             %
860
%                                                                             %
861
+   C o n v e r t P r i m i t i v e T o P a t h                               %
862
%                                                                             %
863
%                                                                             %
864
%                                                                             %
865
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866
%
867
%  Method ConvertPrimitiveToPath converts a PrimitiveInfo structure into a
868
%  vector path structure.
869
%
870
%  The format of the ConvertPrimitiveToPath method is:
871
%
872
%      PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
873
%        const PrimitiveInfo *primitive_info)
874
%
875
%  A description of each parameter follows:
876
%
877
%    o Method ConvertPrimitiveToPath returns a vector path structure of type
878
%      PathInfo.
879
%
880
%    o draw_info: a structure of type DrawInfo.
881
%
882
%    o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
883
%
884
%
885
*/
886
887
static void
888
LogPathInfo(const PathInfo *path_info)
889
0
{
890
0
  register const PathInfo
891
0
    *p;
892
893
0
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    begin vector-path");
894
0
  for (p=path_info; p->code != EndCode; p++)
895
0
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),
896
0
      "      %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
897
0
      "moveto ghostline" : p->code == OpenCode ? "moveto open" :
898
0
      p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
899
0
      "?");
900
0
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end vector-path");
901
0
}
902
903
static PathInfo *
904
ConvertPrimitiveToPath(const DrawInfo *draw_info,
905
                       const PrimitiveInfo *primitive_info,
906
                       ExceptionInfo *exception)
907
3.69M
{
908
3.69M
  PathInfo
909
3.69M
    *path_info;
910
911
3.69M
  PathInfoCode
912
3.69M
    code;
913
914
3.69M
  PointInfo
915
3.69M
    p,  /* first point in subpath (i.e., just did a "moveto" to this point) */
916
3.69M
    q;  /* previous point in subpath */
917
918
3.69M
  register size_t
919
3.69M
    i,
920
3.69M
    n;
921
922
3.69M
  size_t
923
3.69M
    path_info_elem, /* Number of elements in path_info */
924
3.69M
    start;        /* index to start of subpath in path_info */
925
926
3.69M
  ssize_t
927
3.69M
    coordinates;  /* number of points in subpath */
928
929
3.69M
  MagickBool
930
3.69M
    IsClosedSubPath = MagickFalse;
931
932
3.69M
  ARG_NOT_USED(draw_info);
933
934
  /*
935
    Converts a PrimitiveInfo structure into a vector path structure.
936
  */
937
3.69M
  switch (primitive_info->primitive)
938
3.69M
  {
939
0
    case PointPrimitive:
940
0
    case ColorPrimitive:
941
0
    case MattePrimitive:
942
0
    case TextPrimitive:
943
0
    case ImagePrimitive:
944
0
      return((PathInfo *) NULL);
945
3.69M
    default:
946
3.69M
      break;
947
3.69M
  }
948
641M
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++);
949
3.69M
  path_info_elem=(2*i+6);
950
3.69M
  path_info=MagickAllocateResourceLimitedArray(PathInfo *,path_info_elem,sizeof(PathInfo));
951
3.69M
  if (path_info == (PathInfo *) NULL)
952
55
    {
953
55
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
954
55
                      UnableToDrawOnImage);
955
55
      return((PathInfo *) NULL);
956
55
    }
957
3.69M
  coordinates=0;
958
3.69M
  n=0;
959
3.69M
  p.x=(-1.0);
960
3.69M
  p.y=(-1.0);
961
3.69M
  q.x=(-1.0);
962
3.69M
  q.y=(-1.0);
963
3.69M
  start=0;
964
506M
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
965
503M
  {
966
503M
    code=LineToCode;
967
503M
    if (coordinates <= 0)
968
11.1M
      {
969
        /* start of a new subpath */
970
11.1M
        coordinates=(long) primitive_info[i].coordinates;
971
11.1M
        p=primitive_info[i].point;  /* first point in subpath */
972
11.1M
        start=n;  /* index to start of subpath in path_info */
973
11.1M
        code=MoveToCode;
974
11.1M
        IsClosedSubPath=PRIMINF_GET_IS_CLOSED_SUBPATH(&primitive_info[i]);
975
11.1M
      }
976
503M
    coordinates--;
977
    /*
978
      Do not put the current point into path_info if it is a duplicate of
979
      (i.e. "too close" to) the previous point.  However, the current point
980
      is always put into path_info when it is the first (code==MoveToCode)
981
      or last point in a subpath.  Note that for the first point the "previous
982
      point" (q) is not valid (usually a leftover from the previous subpath),
983
      so the first point test should be done first.  W.r.t. the last point,
984
      sometimes eliminating the last point as a "duplicate" will result in a
985
      polygon (path) not being completely closed, causing small "nubs" to
986
      protrude from the filled shape at that point.
987
    */
988
503M
    if ((code == MoveToCode) || (coordinates <= 0) ||
989
487M
        (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
990
53.1M
        (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
991
459M
      {
992
        /* put current point into path_info*/
993
459M
        path_info[n].code=code;
994
459M
        path_info[n].point=primitive_info[i].point;
995
459M
        q=primitive_info[i].point;  /* will be "previous point" for next iteration */
996
459M
        n++;
997
459M
        if (n == path_info_elem - 1)
998
346
          break;
999
459M
      }
1000
503M
    if (coordinates > 0)
1001
492M
      continue;   /* go process next point in current subpath */
1002
    /*
1003
      The current point is the last point in the subpath.  If it closes
1004
      the subpath, go on to the next subpath.
1005
    */
1006
11.1M
    if  ( IsClosedSubPath )
1007
8.69M
      {
1008
8.69M
        IsClosedSubPath = MagickFalse;
1009
8.69M
        continue;
1010
8.69M
      }
1011
    /*
1012
      The just completed subpath is not closed.  Mark it as "open" and add two
1013
      more points (repeat of current point + subpath start point) to "virtually"
1014
      close it (this is a "ghost line").
1015
    */
1016
2.45M
    if ((start >= path_info_elem - 3) || (n >= path_info_elem - 3))
1017
806
      break;
1018
2.45M
    path_info[start].code=OpenCode;
1019
2.45M
    path_info[n].code=GhostlineCode;
1020
2.45M
    path_info[n].point=primitive_info[i].point;
1021
2.45M
    n++;
1022
2.45M
    path_info[n].code=LineToCode;
1023
2.45M
    path_info[n].point=p;
1024
2.45M
    n++;
1025
2.45M
  }
1026
3.69M
  path_info[n].code=EndCode;
1027
3.69M
  path_info[n].point.x=0.0;
1028
3.69M
  path_info[n].point.y=0.0;
1029
3.69M
  if (IsEventLogged(RenderEvent))
1030
0
    LogPathInfo(path_info);
1031
3.69M
  return(path_info);
1032
3.69M
}
1033

1034
/*
1035
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036
%                                                                             %
1037
%                                                                             %
1038
%                                                                             %
1039
%   D e s t r o y D r a w I n f o                                             %
1040
%                                                                             %
1041
%                                                                             %
1042
%                                                                             %
1043
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044
%
1045
%  DestroyDrawInfo() deallocates memory associated with an DrawInfo
1046
%  structure.  Nothing is done if the pointer passed is NULL.
1047
%
1048
%  The format of the DestroyDrawInfo method is:
1049
%
1050
%      void DestroyDrawInfo(DrawInfo *draw_info)
1051
%
1052
%  A description of each parameter follows:
1053
%
1054
%    o draw_info: The draw info.
1055
%
1056
%
1057
*/
1058
MagickExport void
1059
DestroyDrawInfo(DrawInfo *draw_info)
1060
4.36M
{
1061
4.36M
  if (draw_info == (DrawInfo *) NULL)
1062
15
    return;
1063
4.36M
  assert(draw_info->signature == MagickSignature);
1064
4.36M
  MagickFreeMemory(draw_info->primitive);
1065
4.36M
  MagickFreeMemory(draw_info->text);
1066
4.36M
  MagickFreeMemory(draw_info->geometry);
1067
4.36M
  if (draw_info->tile != 0)
1068
0
    DestroyImage(draw_info->tile);
1069
4.36M
  if (draw_info->fill_pattern != (Image *) NULL)
1070
75.1k
    DestroyImage(draw_info->fill_pattern);
1071
4.36M
  if (draw_info->stroke_pattern != (Image *) NULL)
1072
63.1k
    DestroyImage(draw_info->stroke_pattern);
1073
4.36M
  MagickFreeMemory(draw_info->font);
1074
4.36M
  MagickFreeMemory(draw_info->family);
1075
4.36M
  MagickFreeMemory(draw_info->encoding);
1076
4.36M
  MagickFreeMemory(draw_info->density);
1077
4.36M
  MagickFreeMemory(draw_info->server_name);
1078
4.36M
  MagickFreeMemory(draw_info->dash_pattern);
1079
4.36M
  MagickFreeMemory(draw_info->extra->clip_path);
1080
4.36M
  MagickFreeMemory(draw_info->extra->composite_path);
1081
4.36M
  MagickFreeMemory(draw_info->extra);
1082
4.36M
  (void) memset((void *)draw_info,0xbf,sizeof(DrawInfo));
1083
4.36M
  MagickFreeMemory(draw_info);
1084
4.36M
}
1085

1086
/*
1087
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088
%                                                                             %
1089
%                                                                             %
1090
%                                                                             %
1091
+   D e s t r o y E d g e                                                     %
1092
%                                                                             %
1093
%                                                                             %
1094
%                                                                             %
1095
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096
%
1097
%  Method DestroyEdge destroys the specified polygon edge.
1098
%
1099
%  The format of the DestroyEdge method is:
1100
%
1101
%      size_t DestroyEdge(PolygonInfo *polygon_info,const size_t edge)
1102
%
1103
%  A description of each parameter follows:
1104
%
1105
%    o polygon_info: Specifies a pointer to an PolygonInfo structure.
1106
%
1107
%    o edge: the polygon edge number to destroy.
1108
%
1109
%
1110
*/
1111
static size_t
1112
DestroyEdge(PolygonInfo * restrict polygon_info,const size_t edge)
1113
4.46M
{
1114
4.46M
  assert(edge < polygon_info->number_edges);
1115
4.46M
  MagickFreeResourceLimitedMemory(PointInfo *,polygon_info->edges[edge].points);
1116
4.46M
  polygon_info->number_edges--;
1117
4.46M
  if (edge < polygon_info->number_edges)
1118
2.77M
    (void) memmove(polygon_info->edges+edge,polygon_info->edges+edge+1,
1119
2.77M
      (polygon_info->number_edges-edge)*sizeof(EdgeInfo));
1120
4.46M
  return(polygon_info->number_edges);
1121
4.46M
}
1122

1123
/*
1124
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125
%                                                                             %
1126
%                                                                             %
1127
%                                                                             %
1128
+   D e s t r o y G r a d i e n t I n f o                                     %
1129
%                                                                             %
1130
%                                                                             %
1131
%                                                                             %
1132
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1133
%
1134
%  DestroyGradientInfo() deallocates memory associated with the GradientInfo
1135
%  list.
1136
%
1137
%  The format of the DestroyGradientInfo method is:
1138
%
1139
%      DestroyGradientInfo(GradientInfo *gradient_info)
1140
%
1141
%  A description of each parameter follows:
1142
%
1143
%    o gradient_info: The gradient info.
1144
%
1145
%
1146
*/
1147
#if 0
1148
static void
1149
DestroyGradientInfo(GradientInfo *gradient_info)
1150
{
1151
  register GradientInfo
1152
    *p;
1153
1154
  if (gradient_info == (GradientInfo *) NULL)
1155
    return;
1156
  assert(gradient_info->signature == MagickSignature);
1157
  for (p=gradient_info; p->previous != (GradientInfo *) NULL; p=p->previous);
1158
  for (gradient_info=p; p != (GradientInfo *) NULL; gradient_info=p)
1159
  {
1160
    p=p->next;
1161
    MagickFreeMemory(gradient_info);
1162
  }
1163
}
1164
#endif
1165

1166
/*
1167
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168
%                                                                             %
1169
%                                                                             %
1170
%                                                                             %
1171
+   D e s t r o y P o l y g o n I n f o                                       %
1172
%                                                                             %
1173
%                                                                             %
1174
%                                                                             %
1175
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176
%
1177
%  Method DestroyPolygonInfo destroys the PolygonInfo data structure.
1178
%
1179
%  The format of the DestroyPolygonInfo method is:
1180
%
1181
%      void DestroyPolygonInfo(PolygonInfo *polygon_info)
1182
%
1183
%  A description of each parameter follows:
1184
%
1185
%    o polygon_info: Specifies a pointer to an PolygonInfo structure.
1186
%
1187
%
1188
*/
1189
static void
1190
DestroyPolygonInfo(void *polygon_info_void)
1191
3.69M
{
1192
3.69M
  PolygonInfo
1193
3.69M
    *polygon_info = (PolygonInfo *) polygon_info_void;
1194
1195
3.69M
  if (polygon_info != (PolygonInfo *) NULL)
1196
3.69M
    {
1197
3.69M
      register size_t
1198
3.69M
        i;
1199
1200
3.69M
      if (polygon_info->edges != (EdgeInfo *) NULL)
1201
3.69M
        {
1202
34.1M
          for (i=0; i < polygon_info->number_edges; i++)
1203
30.4M
            MagickFreeResourceLimitedMemory(PointInfo *, polygon_info->edges[i].points);
1204
3.69M
          MagickFreeResourceLimitedMemory(EdgeInfo *, polygon_info->edges);
1205
3.69M
        }
1206
3.69M
      MagickFreeMemory(polygon_info);
1207
3.69M
    }
1208
3.69M
}
1209

1210
/*
1211
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212
%                                                                             %
1213
%                                                                             %
1214
%     D r a w A f f i n e I m a g e                                           %
1215
%                                                                             %
1216
%                                                                             %
1217
%                                                                             %
1218
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219
%
1220
%  DrawAffineImage() composites the source over the destination image as
1221
%  dictated by the affine transform.
1222
%
1223
%  The format of the DrawAffineImage method is:
1224
%
1225
%      unsigned int DrawAffineImage(Image *image,const Image *composite,
1226
%        const AffineMatrix *affine)
1227
%
1228
%  A description of each parameter follows:
1229
%
1230
%    o image: The image.
1231
%
1232
%    o image: The composite image.
1233
%
1234
%    o affine: The affine transform.
1235
%
1236
%
1237
*/
1238
1239
static SegmentInfo
1240
AffineEdge(const Image *image,const AffineMatrix *affine,
1241
           const long y,const SegmentInfo *edge)
1242
116k
{
1243
116k
  double
1244
116k
    intercept,
1245
116k
    z;
1246
1247
116k
  register long
1248
116k
    x;
1249
1250
116k
  SegmentInfo
1251
116k
    inverse_edge;
1252
1253
  /*
1254
    Determine left and right edges.
1255
  */
1256
116k
  inverse_edge.x1=edge->x1;
1257
116k
  inverse_edge.y1=0;
1258
116k
  inverse_edge.x2=edge->x2;
1259
116k
  inverse_edge.y2=0;
1260
116k
  z=affine->ry*(y+0.5)+affine->tx;
1261
116k
  if (affine->sx > MagickEpsilon)
1262
109k
    {
1263
109k
      intercept=(-z/affine->sx);
1264
109k
      x=MagickDoubleToLong(ceil(intercept+MagickEpsilon-0.5)); /* FIXME: validate */
1265
109k
      if (x > inverse_edge.x1)
1266
8.94k
        inverse_edge.x1=x;
1267
109k
      intercept=(-z+image->columns)/affine->sx;
1268
109k
      x=MagickDoubleToLong(ceil(intercept-MagickEpsilon-0.5)); /* FIXME: validate */
1269
109k
      if (x < inverse_edge.x2)
1270
11.4k
        inverse_edge.x2=x;
1271
109k
    }
1272
7.25k
  else
1273
7.25k
    if (affine->sx < -MagickEpsilon)
1274
4.47k
      {
1275
4.47k
        intercept=(-z+image->columns)/affine->sx;
1276
4.47k
        x=MagickDoubleToLong(ceil(intercept+MagickEpsilon-0.5)); /* FIXME: validate */
1277
4.47k
        if (x > inverse_edge.x1)
1278
2.26k
          inverse_edge.x1=x;
1279
4.47k
        intercept=(-z/affine->sx);
1280
4.47k
        x=MagickDoubleToLong(ceil(intercept-MagickEpsilon-0.5)); /* FIXME: validate */
1281
4.47k
        if (x < inverse_edge.x2)
1282
61
          inverse_edge.x2=x;
1283
4.47k
      }
1284
2.78k
    else
1285
2.78k
      if ((z < 0.0) || (z >= image->columns))
1286
424
        {
1287
424
          inverse_edge.x2=edge->x1;
1288
424
          return(inverse_edge);
1289
424
        }
1290
  /*
1291
    Determine top and bottom edges.
1292
  */
1293
115k
  z=affine->sy*(y+0.5)+affine->ty;
1294
115k
  if (affine->rx > MagickEpsilon)
1295
6.91k
    {
1296
6.91k
      intercept=(-z /affine->rx);
1297
6.91k
      x=MagickDoubleToLong(ceil(intercept+MagickEpsilon-0.5)); /* FIXME: validate */
1298
6.91k
      if (x > inverse_edge.x1)
1299
592
        inverse_edge.x1=x;
1300
6.91k
      intercept=(-z+image->rows)/affine->rx;
1301
6.91k
      x=MagickDoubleToLong(ceil(intercept-MagickEpsilon-0.5)); /* FIXME: validate */
1302
6.91k
      if (x < inverse_edge.x2)
1303
1.18k
        inverse_edge.x2=x;
1304
6.91k
    }
1305
109k
  else
1306
109k
    if (affine->rx < -MagickEpsilon)
1307
7.31k
      {
1308
7.31k
        intercept=(-z+image->rows)/affine->rx;
1309
7.31k
        x=MagickDoubleToLong(ceil(intercept+MagickEpsilon-0.5)); /* FIXME: validate */
1310
7.31k
        if (x > inverse_edge.x1)
1311
1.60k
          inverse_edge.x1=x;
1312
7.31k
        intercept=(-z/affine->rx);
1313
7.31k
        x=MagickDoubleToLong(ceil(intercept-MagickEpsilon-0.5)); /* FIXME: validate */
1314
7.31k
        if (x < inverse_edge.x2)
1315
2.15k
          inverse_edge.x2=x;
1316
7.31k
      }
1317
101k
    else
1318
101k
      if ((z < 0) || (z >= image->rows))
1319
5.90k
        {
1320
5.90k
          inverse_edge.x2=edge->x1;
1321
5.90k
          return(inverse_edge);
1322
5.90k
        }
1323
110k
  return(inverse_edge);
1324
115k
}
1325
1326
static MagickPassFail
1327
InverseAffineMatrix(const AffineMatrix *affine, AffineMatrix *inverse_affine,
1328
                    ExceptionInfo *exception)
1329
22.3k
{
1330
22.3k
  double
1331
22.3k
    divisor;
1332
1333
22.3k
  MagickPassFail
1334
22.3k
    status;
1335
1336
22.3k
  divisor=affine->sx*affine->sy-affine->rx*affine->ry;
1337
22.3k
  if (AbsoluteValue(divisor) < 0.0001)
1338
6
    {
1339
6
      char message[MaxTextExtent];
1340
6
      MagickFormatString(message,sizeof(message),"Inverse affine divisor: %g", divisor);
1341
6
      ThrowException(exception,DrawError,UnreasonableAffineMatrix,message);
1342
6
      status=MagickFail;
1343
6
    }
1344
22.3k
  else
1345
22.3k
    {
1346
22.3k
      double
1347
22.3k
        determinant;
1348
1349
22.3k
      determinant=1.0/divisor; /* oss-fuzz 28293 runtime error: division by zero */
1350
22.3k
      inverse_affine->sx=determinant*affine->sy;
1351
22.3k
      inverse_affine->rx=determinant*(-affine->rx);
1352
22.3k
      inverse_affine->ry=determinant*(-affine->ry);
1353
22.3k
      inverse_affine->sy=determinant*affine->sx;
1354
22.3k
      inverse_affine->tx=
1355
22.3k
        (-affine->tx)*inverse_affine->sx-affine->ty*inverse_affine->ry;
1356
22.3k
      inverse_affine->ty=
1357
22.3k
        (-affine->tx)*inverse_affine->rx-affine->ty*inverse_affine->sy;
1358
22.3k
      status=MagickPass;
1359
22.3k
    }
1360
22.3k
  return status;
1361
22.3k
}
1362
1363
0
#define AffineDrawImageText "[%s] Affine composite..."
1364
MagickExport MagickPassFail
1365
DrawAffineImage(Image *image,const Image *composite,
1366
                const AffineMatrix *affine)
1367
22.3k
{
1368
22.3k
  MagickPassFail
1369
22.3k
    status = MagickPass;
1370
1371
22.3k
  unsigned long
1372
22.3k
    row_count=0;
1373
1374
22.3k
  MagickBool
1375
22.3k
    monitor_active;
1376
1377
22.3k
  AffineMatrix
1378
22.3k
    inverse_affine;
1379
1380
22.3k
  long
1381
22.3k
    y,
1382
22.3k
    y_max,
1383
22.3k
    y_min;
1384
1385
22.3k
  PointInfo
1386
22.3k
    extent[4],
1387
22.3k
    min,
1388
22.3k
    max;
1389
1390
22.3k
  register long
1391
22.3k
    i;
1392
1393
22.3k
  SegmentInfo
1394
22.3k
    edge;
1395
1396
22.3k
  assert(image != (Image *) NULL);
1397
22.3k
  assert(image->signature == MagickSignature);
1398
22.3k
  assert(composite != (const Image *) NULL);
1399
22.3k
  assert(composite->signature == MagickSignature);
1400
22.3k
  assert(affine != (AffineMatrix *) NULL);
1401
1402
  /*
1403
    Determine bounding box.
1404
  */
1405
22.3k
  extent[0].x=0;
1406
22.3k
  extent[0].y=0;
1407
22.3k
  extent[1].x=composite->columns;
1408
22.3k
  extent[1].y=0;
1409
22.3k
  extent[2].x=composite->columns;
1410
22.3k
  extent[2].y=composite->rows;
1411
22.3k
  extent[3].x=0;
1412
22.3k
  extent[3].y=composite->rows;
1413
111k
  for (i=0; i < 4; i++)
1414
89.2k
    {
1415
89.2k
      register long
1416
89.2k
        x;
1417
1418
89.2k
      x=(long) extent[i].x;
1419
89.2k
      y=(long) extent[i].y;
1420
89.2k
      extent[i].x=x*affine->sx+y*affine->ry+affine->tx;
1421
89.2k
      extent[i].y=x*affine->rx+y*affine->sy+affine->ty;
1422
89.2k
    }
1423
22.3k
  min=extent[0];
1424
22.3k
  max=extent[0];
1425
89.2k
  for (i=1; i < 4; i++)
1426
66.9k
    {
1427
66.9k
      if (min.x > extent[i].x)
1428
2.20k
        min.x=extent[i].x;
1429
66.9k
      if (min.y > extent[i].y)
1430
3.15k
        min.y=extent[i].y;
1431
66.9k
      if (max.x < extent[i].x)
1432
22.2k
        max.x=extent[i].x;
1433
66.9k
      if (max.y < extent[i].y)
1434
21.0k
        max.y=extent[i].y;
1435
66.9k
    }
1436
  /*
1437
    Affine transform image.
1438
  */
1439
22.3k
  status &= SetImageType(image,TrueColorType);
1440
22.3k
  if (status == MagickFail)
1441
0
    return status;
1442
1443
22.3k
  edge.x1=min.x;
1444
22.3k
  edge.y1=min.y;
1445
22.3k
  edge.x2=max.x;
1446
22.3k
  edge.y2=max.y;
1447
22.3k
  status &= InverseAffineMatrix(affine,&inverse_affine,&image->exception);
1448
22.3k
  if (status == MagickFail)
1449
6
    return status;
1450
22.3k
  if (edge.y1 < 0)
1451
1.69k
    edge.y1=0.0;
1452
22.3k
  if (edge.y2 > image->rows - 1)
1453
16.6k
    edge.y2=image->rows-1;
1454
22.3k
  y_min=(long) ceil(edge.y1-0.5);
1455
22.3k
  y_max=(long) floor(edge.y2+0.5);
1456
1457
22.3k
  monitor_active=MagickMonitorActive();
1458
1459
#if defined(HAVE_OPENMP)
1460
#  if defined(TUNE_OPENMP)
1461
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1462
#  else
1463
#    pragma omp parallel for schedule(guided) shared(row_count, status)
1464
#  endif
1465
#endif
1466
149k
  for (y=y_min; y <= y_max; y++)
1467
126k
    {
1468
126k
      MagickBool
1469
126k
        thread_status;
1470
1471
126k
      long
1472
126k
        start,
1473
126k
        stop;
1474
1475
126k
      SegmentInfo
1476
126k
        inverse_edge;
1477
1478
126k
      register PixelPacket
1479
126k
        *q;
1480
1481
126k
      register long
1482
126k
        x;
1483
1484
126k
      thread_status=status;
1485
126k
      if (thread_status == MagickFail)
1486
10.5k
        continue;
1487
1488
116k
      inverse_edge=AffineEdge(composite,&inverse_affine,y,&edge);
1489
116k
      if (inverse_edge.x2 < inverse_edge.x1)
1490
438
        continue;
1491
115k
      if (inverse_edge.x1 < 0)
1492
8.99k
        inverse_edge.x1=0.0;
1493
115k
      if (inverse_edge.x2 > image->columns-1)
1494
95.1k
        inverse_edge.x2=image->columns-1;
1495
115k
      start=(long) ceil(inverse_edge.x1-0.5);
1496
115k
      stop=(long) floor(inverse_edge.x2+0.5);
1497
115k
      if (stop >= start)
1498
93.9k
        x=start;
1499
22.0k
      else
1500
22.0k
        x=stop;
1501
115k
      q=GetImagePixelsEx(image,x,y,stop-x+1,1,&image->exception);
1502
115k
      if (q == (PixelPacket *) NULL)
1503
39
        thread_status=MagickFail;
1504
115k
      if (thread_status != MagickFail)
1505
115k
        {
1506
4.02M
          for ( ; x <= stop; x++)
1507
3.90M
            {
1508
3.90M
              PointInfo
1509
3.90M
                point;
1510
1511
3.90M
              PixelPacket
1512
3.90M
                pixel;
1513
1514
3.90M
              point.x=x*inverse_affine.sx+y*inverse_affine.ry+inverse_affine.tx;
1515
3.90M
              point.y=x*inverse_affine.rx+y*inverse_affine.sy+inverse_affine.ty;
1516
3.90M
              if (InterpolateViewColor(AccessDefaultCacheView(composite),
1517
3.90M
                                       &pixel,
1518
3.90M
                                       point.x,
1519
3.90M
                                       point.y,
1520
3.90M
                                       &image->exception) == MagickFail)
1521
42
                {
1522
42
                  thread_status=MagickFail;
1523
42
                  break;
1524
42
                }
1525
3.90M
              if (!composite->matte)
1526
0
                pixel.opacity=OpaqueOpacity;
1527
3.90M
              AlphaCompositePixel(q,&pixel,pixel.opacity,q,q->opacity);
1528
3.90M
              q++;
1529
3.90M
            }
1530
115k
          if (thread_status != MagickFail)
1531
115k
            if (!SyncImagePixelsEx(image,&image->exception))
1532
0
              thread_status=MagickFail;
1533
115k
        }
1534
1535
115k
      if (monitor_active)
1536
0
        {
1537
0
          unsigned long
1538
0
            thread_row_count;
1539
1540
#if defined(HAVE_OPENMP)
1541
#  pragma omp atomic
1542
#endif
1543
0
          row_count++;
1544
#if defined(HAVE_OPENMP)
1545
#  pragma omp flush (row_count)
1546
#endif
1547
0
          thread_row_count=row_count;
1548
0
          if (QuantumTick(thread_row_count,((size_t) y_max-(size_t) y_min)+1))
1549
0
            if (!MagickMonitorFormatted(thread_row_count,((size_t) y_max- (size_t) y_min)+1,&image->exception,
1550
0
                                        AffineDrawImageText,image->filename))
1551
0
              thread_status=MagickFail;
1552
0
        }
1553
1554
115k
      if (thread_status == MagickFail)
1555
81
        {
1556
81
          status=MagickFail;
1557
#if defined(HAVE_OPENMP)
1558
#  pragma omp flush (status)
1559
#endif
1560
81
        }
1561
115k
    }
1562
1563
22.3k
  return (status);
1564
22.3k
}
1565

1566
/*
1567
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1568
%                                                                             %
1569
%                                                                             %
1570
%                                                                             %
1571
+   D r a w B o u n d i n g R e c t a n g l e s                               %
1572
%                                                                             %
1573
%                                                                             %
1574
%                                                                             %
1575
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576
%
1577
%  Method DrawBoundingRectangles draws the bounding rectangles on the image.
1578
%  This is only useful for developers debugging the rendering algorithm.
1579
%
1580
%  The format of the DrawBoundingRectangles method is:
1581
%
1582
%      void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1583
%        PolygonInfo *polygon_info)
1584
%
1585
%  A description of each parameter follows:
1586
%
1587
%    o image: The image.
1588
%
1589
%    o draw_info: The draw info.
1590
%
1591
%    o polygon_info: Specifies a pointer to a PolygonInfo structure.
1592
%
1593
%
1594
*/
1595
static MagickPassFail
1596
DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
1597
                       const PolygonInfo *polygon_info)
1598
0
{
1599
0
  DrawInfo
1600
0
    *clone_info;
1601
0
1602
0
  double
1603
0
    mid,
1604
0
    stroke_width_limited;
1605
0
1606
0
  PointInfo
1607
0
    end,
1608
0
    resolution,
1609
0
    start;
1610
0
1611
0
  PrimitiveInfo
1612
0
    primitive_info[6];
1613
0
1614
0
  register size_t
1615
0
    i;
1616
0
1617
0
  SegmentInfo
1618
0
    bounds;
1619
0
1620
0
  long
1621
0
    coordinates;
1622
0
1623
0
  MagickPassFail
1624
0
    status = MagickPass;
1625
0
1626
0
  memset(primitive_info,0,sizeof(primitive_info));
1627
0
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1628
0
  if ((status &= QueryColorDatabase("#000000ff",&clone_info->fill,&image->exception)) == MagickFail)
1629
0
    goto draw_bounding_rectangles_end;
1630
0
  resolution.x=72.0;
1631
0
  resolution.y=72.0;
1632
0
  if (clone_info->density != (char *) NULL)
1633
0
    {
1634
0
      int
1635
0
        count;
1636
0
1637
0
      count=GetMagickDimension(clone_info->density,&resolution.x,&resolution.y,NULL,NULL);
1638
0
      if (count != 2)
1639
0
        resolution.y=resolution.x;
1640
0
    }
1641
0
1642
0
  /* sanity check for excessively big stroke_width (ticket #515) */
1643
0
  if  ( (stroke_width_limited = STROKE_WIDTH_LIMIT(image)) > clone_info->stroke_width )
1644
0
      stroke_width_limited = clone_info->stroke_width;
1645
0
1646
0
  mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
1647
0
    stroke_width_limited/2.0;
1648
0
  bounds.x1=0.0;
1649
0
  bounds.y1=0.0;
1650
0
  bounds.x2=0.0;
1651
0
  bounds.y2=0.0;
1652
0
  if (polygon_info != (PolygonInfo *) NULL)
1653
0
    {
1654
0
      bounds=polygon_info->edges[0].bounds;
1655
0
      for (i=1; i < polygon_info->number_edges; i++)
1656
0
      {
1657
0
        if (polygon_info->edges[i].bounds.x1 < bounds.x1)
1658
0
          bounds.x1=polygon_info->edges[i].bounds.x1;
1659
0
        if (polygon_info->edges[i].bounds.y1 < bounds.y1)
1660
0
          bounds.y1=polygon_info->edges[i].bounds.y1;
1661
0
        if (polygon_info->edges[i].bounds.x2 > bounds.x2)
1662
0
          bounds.x2=polygon_info->edges[i].bounds.x2;
1663
0
        if (polygon_info->edges[i].bounds.y2 > bounds.y2)
1664
0
          bounds.y2=polygon_info->edges[i].bounds.y2;
1665
0
      }
1666
0
      bounds.x1-=mid;
1667
0
      bounds.x1=bounds.x1 < 0.0 ? 0.0 :
1668
0
        bounds.x1 >= image->columns ? image->columns-1 : bounds.x1;
1669
0
      bounds.y1-=mid;
1670
0
      bounds.y1=bounds.y1 < 0.0 ? 0.0 :
1671
0
        bounds.y1 >= image->rows ? image->rows-1 : bounds.y1;
1672
0
      bounds.x2+=mid;
1673
0
      bounds.x2=bounds.x2 < 0.0 ? 0.0 :
1674
0
        bounds.x2 >= image->columns ? image->columns-1 : bounds.x2;
1675
0
      bounds.y2+=mid;
1676
0
      bounds.y2=bounds.y2 < 0.0 ? 0.0 :
1677
0
        bounds.y2 >= image->rows ? image->rows-1 : bounds.y2;
1678
0
      for (i=0; i < polygon_info->number_edges; i++)
1679
0
      {
1680
0
        if (polygon_info->edges[i].direction)
1681
0
          {
1682
0
          if ((status &= QueryColorDatabase("red",&clone_info->stroke,
1683
0
                                            &image->exception)) == MagickFail)
1684
0
            goto draw_bounding_rectangles_end;
1685
0
          }
1686
0
        else
1687
0
          {
1688
0
            if ((status &= QueryColorDatabase("green",&clone_info->stroke,
1689
0
                                              &image->exception)) == MagickFail)
1690
0
              goto draw_bounding_rectangles_end;
1691
0
          }
1692
0
        start.x=polygon_info->edges[i].bounds.x1-mid;
1693
0
        start.y=polygon_info->edges[i].bounds.y1-mid;
1694
0
        end.x=polygon_info->edges[i].bounds.x2+mid;
1695
0
        end.y=polygon_info->edges[i].bounds.y2+mid;
1696
0
        primitive_info[0].primitive=RectanglePrimitive;
1697
0
        if ((status &= TraceRectangle(primitive_info,start,end)) == MagickFail)
1698
0
          goto draw_bounding_rectangles_end;
1699
0
        primitive_info[0].method=ReplaceMethod;
1700
0
        coordinates=(long) primitive_info[0].coordinates;
1701
0
        primitive_info[coordinates].primitive=UndefinedPrimitive;
1702
0
        if ((status &= DrawPrimitive(image,clone_info,primitive_info)) == MagickFail)
1703
0
          goto draw_bounding_rectangles_end;
1704
0
      }
1705
0
    }
1706
0
  if ((status &= QueryColorDatabase("blue",&clone_info->stroke,&image->exception)) == MagickFail)
1707
0
    goto draw_bounding_rectangles_end;
1708
0
  start.x=bounds.x1-mid;
1709
0
  start.y=bounds.y1-mid;
1710
0
  end.x=bounds.x2+mid;
1711
0
  end.y=bounds.y2+mid;
1712
0
  primitive_info[0].primitive=RectanglePrimitive;
1713
0
  if ((status &= TraceRectangle(primitive_info,start,end)) == MagickFail)
1714
0
    goto draw_bounding_rectangles_end;
1715
0
  primitive_info[0].method=ReplaceMethod;
1716
0
  coordinates=(long) primitive_info[0].coordinates;
1717
0
  primitive_info[coordinates].primitive=UndefinedPrimitive;
1718
0
  if ((status &= DrawPrimitive(image,clone_info,primitive_info)) == MagickFail)
1719
0
    goto draw_bounding_rectangles_end;
1720
0
1721
0
 draw_bounding_rectangles_end:;
1722
0
1723
0
  DestroyDrawInfo(clone_info);
1724
0
1725
0
  return status;
1726
0
}
1727

1728
/*
1729
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1730
%                                                                             %
1731
%                                                                             %
1732
%                                                                             %
1733
%   D r a w C l i p P a t h                                                   %
1734
%                                                                             %
1735
%                                                                             %
1736
%                                                                             %
1737
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1738
%
1739
%  DrawClipPath() draws the clip path on the image mask.
1740
%
1741
%  The format of the DrawClipPath method is:
1742
%
1743
%      MagickPassFail DrawClipPath(Image *image,const DrawInfo *draw_info,
1744
%        const char *name)
1745
%
1746
%  A description of each parameter follows:
1747
%
1748
%    o image: The image.
1749
%
1750
%    o draw_info: The draw info.
1751
%
1752
%    o name: The name of the clip path.
1753
%
1754
%
1755
*/
1756
MagickExport MagickPassFail
1757
DrawClipPath(Image *image,const DrawInfo *draw_info, const char *name)
1758
23.2k
{
1759
23.2k
  char
1760
23.2k
    clip_path[MaxTextExtent];
1761
1762
23.2k
  const ImageAttribute
1763
23.2k
    *attribute;
1764
1765
23.2k
  DrawInfo
1766
23.2k
    *clone_info = (DrawInfo *) NULL;
1767
1768
23.2k
  MagickPassFail
1769
23.2k
    status=MagickPass;
1770
1771
23.2k
  Image
1772
23.2k
    *image_clip_mask;
1773
1774
23.2k
  assert(image != (Image *) NULL);
1775
23.2k
  assert(image->signature == MagickSignature);
1776
23.2k
  assert(draw_info != (const DrawInfo *) NULL);
1777
23.2k
  assert(name != (const char *) NULL);
1778
23.2k
  MagickFormatString(clip_path,sizeof(clip_path),"[MVG:%.1024s]",name);
1779
23.2k
  attribute=GetImageAttribute(image,clip_path);
1780
23.2k
  if (attribute == (ImageAttribute *) NULL)
1781
4.16k
    return(MagickPass);
1782
19.1k
  do
1783
19.1k
    {
1784
19.1k
      image_clip_mask = *ImageGetClipMaskInlined(image);
1785
19.1k
      if (image_clip_mask == (Image *) NULL)
1786
12.1k
        {
1787
          /*
1788
            FIXME: Desired error handling for missing clip-path attribute is
1789
            not clear (was returning MagickFail).  Maybe the caller does not
1790
            know.
1791
          */
1792
12.1k
          Image
1793
12.1k
            *clip_mask;
1794
1795
12.1k
          if ((clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
1796
12.1k
                                    &image->exception)) == (Image *) NULL)
1797
0
            break;
1798
12.1k
          status &= SetImageClipMask(image,clip_mask);
1799
          /*
1800
            SetImageClipMask() clones the provided clip_mask image so
1801
            destroy original reference.
1802
          */
1803
12.1k
          DestroyImage(clip_mask);
1804
12.1k
          if (status == MagickFail)
1805
0
            break;
1806
12.1k
          image_clip_mask = *ImageGetClipMaskInlined(image);
1807
12.1k
        }
1808
7.00k
      else
1809
7.00k
        {
1810
          /*
1811
            Re-clone the image attributes, since more may have been added since
1812
            the clip_mask image was created.
1813
          */
1814
7.00k
          DestroyImageAttributes(image_clip_mask);
1815
7.00k
          CloneImageAttributes(image_clip_mask,image);
1816
7.00k
        }
1817
19.1k
      if ((status &= QueryColorDatabase("none",&image_clip_mask->background_color,
1818
19.1k
                                        &image->exception)) == MagickFail)
1819
0
        break;
1820
19.1k
      if ((status &= SetImage(image_clip_mask,TransparentOpacity)) == MagickFail)
1821
10
        break;
1822
19.1k
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
1823
19.1k
                            "\nbegin clip-path %.1024s",draw_info->extra->clip_path);
1824
19.1k
      if ((clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info)) == (DrawInfo *) NULL)
1825
0
        {
1826
0
          ThrowException3(&image->exception,ResourceLimitError,
1827
0
                          MemoryAllocationFailed,UnableToDrawOnImage);
1828
0
          status=MagickFail;
1829
0
          break;
1830
0
        }
1831
19.1k
      if ((status &= CloneString(&clone_info->primitive,attribute->value)) == MagickFail)
1832
0
        break;
1833
19.1k
      if ((status &= QueryColorDatabase("white",&clone_info->fill,&image->exception)) == MagickFail)
1834
0
        break;
1835
1836
      /*
1837
        According to the SVG spec:
1838
1839
        The raw geometry of each child element exclusive of rendering properties such
1840
        as ‘fill’, ‘stroke’, ‘stroke-width’ within a ‘clipPath’ conceptually defines a
1841
        1-bit mask (with the possible exception of anti-aliasing along the edge of the
1842
        geometry) which represents the silhouette of the graphics associated with that
1843
        element.  Anything outside the outline of the object is masked out.
1844
1845
        To conform with the spec, we make sure that fill color (set above), stroke color,
1846
        stroke width, and group/global opacity are set to appropriate values.
1847
      */
1848
19.1k
      SetDrawInfoClippingPath(clone_info,MagickTrue);
1849
19.1k
      if  ( IsDrawInfoSVGCompliant(clone_info) )
1850
6.67k
        {
1851
          /* changes to fill, etc. will be ignored */
1852
6.67k
          if ((status &= QueryColorDatabase("none",
1853
6.67k
                                            &clone_info->stroke,&image->exception))  /* SVG default */
1854
6.67k
              == MagickFail)
1855
0
            {
1856
0
              break;
1857
0
            }
1858
6.67k
          clone_info->stroke_width = 0.0;   /* SVG default */
1859
6.67k
          clone_info->opacity = OpaqueOpacity;  /* SVG default */
1860
6.67k
        }
1861
1862
19.1k
      MagickFreeMemory(clone_info->extra->clip_path);
1863
19.1k
      if ((status &= DrawImage(image_clip_mask,clone_info)) == MagickFail) /* FIXME: oss-fuzz 499125828 recursive call + leak */
1864
9.05k
        {
1865
          /* Copy exception into base image */
1866
9.05k
          if (image_clip_mask->exception.severity > image->exception.severity)
1867
7.57k
            CopyException(&image->exception, &image_clip_mask->exception);
1868
9.05k
          break;
1869
9.05k
        }
1870
10.0k
      if ((status &= NegateImage(image_clip_mask,False)) == MagickFail)
1871
0
        {
1872
          /* Copy exception into base image */
1873
0
          if (image_clip_mask->exception.severity > image->exception.severity)
1874
0
            CopyException(&image->exception, &image_clip_mask->exception);
1875
0
          break;
1876
0
        }
1877
1878
10.0k
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"end clip-path");
1879
1880
10.0k
    } while (0);
1881
1882
19.1k
  if (clone_info != (DrawInfo *) NULL)
1883
19.1k
    {
1884
19.1k
      MagickFreeMemory(clone_info->extra->clip_path);
1885
19.1k
      DestroyDrawInfo(clone_info);
1886
19.1k
      clone_info = (DrawInfo *) NULL;
1887
19.1k
    }
1888
1889
19.1k
  return(status);
1890
23.2k
}
1891
1892
/* code below for DrawCompositeMask() cloned/modified from DrawClipPath() */
1893
1894
/*
1895
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1896
%                                                                             %
1897
%                                                                             %
1898
%                                                                             %
1899
%   D r a w C o m p o s i t e M a s k                                                   %
1900
%                                                                             %
1901
%                                                                             %
1902
%                                                                             %
1903
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904
%
1905
%  DrawCompositeMask() draws the composite mask on the image mask.
1906
%
1907
%  The format of the DrawCompositeMask method is:
1908
%
1909
%      MagickPassFail DrawCompositeMask(Image *image,const DrawInfo *draw_info,
1910
%        const char *name)
1911
%
1912
%  A description of each parameter follows:
1913
%
1914
%    o image: The image.
1915
%
1916
%    o draw_info: The draw info.
1917
%
1918
%    o name: The name of the composite mask.
1919
%
1920
%
1921
*/
1922
MagickExport MagickPassFail
1923
DrawCompositeMask(Image *image,const DrawInfo *draw_info, const char *name)
1924
39.1k
{
1925
39.1k
  char
1926
39.1k
    composite_path[MaxTextExtent];
1927
1928
39.1k
  const ImageAttribute
1929
39.1k
    *attribute;
1930
1931
39.1k
  DrawInfo
1932
39.1k
    *clone_info = (DrawInfo *) NULL;
1933
1934
39.1k
  MagickPassFail
1935
39.1k
    status=MagickPass;
1936
1937
39.1k
  Image
1938
39.1k
    *image_composite_mask;
1939
1940
39.1k
  assert(image != (Image *) NULL);
1941
39.1k
  assert(image->signature == MagickSignature);
1942
39.1k
  assert(draw_info != (const DrawInfo *) NULL);
1943
39.1k
  MagickFormatString(composite_path,sizeof(composite_path),"[MVG:%.1024s]",name);
1944
39.1k
  do
1945
39.1k
    {
1946
39.1k
      if ((attribute=GetImageAttribute(image,composite_path)) == (ImageAttribute *) NULL)
1947
12
        {
1948
12
          status = MagickFail;
1949
12
          break;
1950
12
        }
1951
39.1k
      image_composite_mask = *ImageGetCompositeMaskInlined(image);
1952
39.1k
      if (image_composite_mask == (Image *) NULL)
1953
31.4k
        {
1954
31.4k
          Image
1955
31.4k
            *composite_mask;
1956
1957
31.4k
          composite_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
1958
31.4k
                                    &image->exception);
1959
31.4k
          if (composite_mask == (Image *) NULL)
1960
0
            {
1961
0
              status = MagickFail;
1962
0
              break;
1963
0
            }
1964
31.4k
          status &= SetImageCompositeMask(image,composite_mask);
1965
31.4k
          DestroyImage(composite_mask);
1966
31.4k
          image_composite_mask = *ImageGetCompositeMaskInlined(image);
1967
31.4k
          if (status == MagickFail)
1968
0
            break;
1969
31.4k
        }
1970
7.65k
      else
1971
7.65k
        {
1972
          /*
1973
            Re-clone the image attributes, since more may have been added since
1974
            the composite_mask image was created.
1975
          */
1976
7.65k
          DestroyImageAttributes(image_composite_mask);
1977
7.65k
          CloneImageAttributes(image_composite_mask,image);
1978
7.65k
        }
1979
39.1k
      if ((status &= QueryColorDatabase("none",&image_composite_mask->background_color,
1980
39.1k
                                        &image->exception)) == MagickFail)
1981
0
        break;
1982
39.1k
      if ((status &= SetImage(image_composite_mask,TransparentOpacity)) == MagickFail)
1983
1
        break;
1984
39.1k
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
1985
39.1k
                            "\nbegin mask %.1024s",draw_info->extra->composite_path);
1986
39.1k
      if ((clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info)) == (DrawInfo *) NULL)
1987
0
        {
1988
0
          ThrowException3(&image->exception,ResourceLimitError,
1989
0
                          MemoryAllocationFailed,UnableToDrawOnImage);
1990
0
          status=MagickFail;
1991
0
          break;
1992
0
        }
1993
39.1k
      if ((status &= CloneString(&clone_info->primitive,attribute->value)) == MagickFail)
1994
0
        break;
1995
      /* these settings are per the SVG spec */
1996
39.1k
      if ((status &= QueryColorDatabase("black",&clone_info->fill,&image->exception)) == MagickFail)
1997
0
        break;
1998
39.1k
      if ((status &= QueryColorDatabase("none",&clone_info->stroke,&image->exception)) == MagickFail)
1999
0
        break;
2000
39.1k
      clone_info->stroke_width = 1.0;
2001
39.1k
      clone_info->opacity = OpaqueOpacity;
2002
39.1k
      if ((status &= DrawImage(image_composite_mask,clone_info)) == MagickFail)
2003
30.1k
        {
2004
          /* Copy exception into base image */
2005
30.1k
          if (image_composite_mask->exception.severity > image->exception.severity)
2006
24.1k
            CopyException(&image->exception, &image_composite_mask->exception);
2007
30.1k
          break;
2008
30.1k
        }
2009
8.98k
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"end composite-path");
2010
8.98k
    } while (0);
2011
2012
39.1k
  DestroyDrawInfo(clone_info);
2013
39.1k
  return(status);
2014
39.1k
}
2015
2016
/*
2017
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2018
%                                                                             %
2019
%                                                                             %
2020
%                                                                             %
2021
+   D r a w D a s h P o l y g o n                                             %
2022
%                                                                             %
2023
%                                                                             %
2024
%                                                                             %
2025
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2026
%
2027
%  Method DrawDashPolygon draws a dashed polygon (line, rectangle, ellipse)
2028
%  on the image while respecting the dash offset and dash pattern attributes.
2029
%
2030
%  The format of the DrawDashPolygon method is:
2031
%
2032
%      MagickPassFail DrawDashPolygon(const DrawInfo *draw_info,
2033
%        const PrimitiveInfo *primitive_info,Image *image)
2034
%
2035
%  A description of each parameter follows:
2036
%
2037
%    o draw_info: The draw info.
2038
%
2039
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
2040
%
2041
%    o image: The image.
2042
%
2043
%
2044
*/
2045
static MagickPassFail
2046
DrawDashPolygon(const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
2047
                Image *image)
2048
28.1k
{
2049
28.1k
  DrawInfo
2050
28.1k
    *clone_info;
2051
2052
28.1k
  double
2053
28.1k
    length,
2054
28.1k
    maximum_length,
2055
28.1k
    maximum_length_limit,
2056
28.1k
    offset,
2057
28.1k
    scale,
2058
28.1k
    total_length;
2059
2060
28.1k
  MagickPassFail
2061
28.1k
    status = MagickPass;
2062
2063
28.1k
  PrimitiveInfo
2064
28.1k
    *dash_polygon;
2065
2066
28.1k
  register long
2067
28.1k
    i;
2068
2069
28.1k
  register double
2070
28.1k
    dx,
2071
28.1k
    dy;
2072
2073
28.1k
  int
2074
28.1k
    number_vertices;
2075
2076
28.1k
  int
2077
28.1k
    j,
2078
28.1k
    n;
2079
2080
28.1k
  assert(draw_info != (const DrawInfo *) NULL);
2081
28.1k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    begin draw-dash");
2082
28.1k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2083
28.1k
  if (clone_info == (DrawInfo *) NULL)
2084
0
    return(MagickFail);
2085
28.1k
  clone_info->miterlimit=0;
2086
77.4M
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++);
2087
28.1k
  number_vertices=i;
2088
28.1k
  dash_polygon=MagickAllocateResourceLimitedArray(PrimitiveInfo *,
2089
28.1k
                                                  (size_t) 2*number_vertices+1,
2090
28.1k
                                                  sizeof(PrimitiveInfo));
2091
28.1k
  if (dash_polygon == (PrimitiveInfo *) NULL)
2092
7
    {
2093
7
      DestroyDrawInfo(clone_info);
2094
7
      ThrowException3(&image->exception,ResourceLimitError,MemoryAllocationFailed,
2095
7
                      UnableToDrawOnImage);
2096
7
      return(MagickFail);
2097
7
    }
2098
28.1k
  dash_polygon[0]=primitive_info[0];
2099
28.1k
  scale=ExpandAffine(&draw_info->affine); /* oss-fuzz 24236 scale=121287375, total_length=542727042660000, maximum_length=605375610468750! */
2100
28.1k
  length=scale*draw_info->dash_pattern[0];
2101
28.1k
  offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
2102
28.1k
  j=1;
2103
63.1k
  for (n=0; offset > 0.0; j=0)
2104
40.1k
  {
2105
40.1k
    if (draw_info->dash_pattern[n] <= 0.0)
2106
3.81k
      break;
2107
36.3k
    length=scale*draw_info->dash_pattern[n];
2108
36.3k
    if (offset > length)
2109
21.5k
      {
2110
21.5k
        offset-=length;
2111
21.5k
        n++;
2112
21.5k
        length=scale*draw_info->dash_pattern[n];
2113
21.5k
        continue;
2114
21.5k
      }
2115
14.7k
    if (offset < length)
2116
1.30k
      {
2117
1.30k
        length-=offset;
2118
1.30k
        offset=0.0;
2119
1.30k
        break;
2120
1.30k
      }
2121
13.4k
    offset=0.0;
2122
13.4k
    n++;
2123
13.4k
  }
2124
28.1k
  maximum_length=0.0;
2125
28.1k
  total_length=0.0;
2126
28.1k
  maximum_length_limit=100.0*sqrt((double)image->columns*(double)image->columns+
2127
28.1k
                                  (double) image->rows*(double) image->rows+
2128
28.1k
                                  +MagickEpsilon);
2129
23.8M
  for (i=1; (i < number_vertices) && (length >= 0.0); i++)
2130
23.7M
  {
2131
23.7M
    dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
2132
23.7M
    dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
2133
23.7M
    maximum_length=sqrt(dx*dx+dy*dy+MagickEpsilon);
2134
    /*
2135
      Apply an arbitrary limit on maximum length in order to avoid
2136
      seemingly unending iterations due to affine and other factors
2137
      (e.g. oss-fuzz 10614 & 24236).
2138
2139
      FIXME: It is TBD if the arbitrary limit factors used have any
2140
      basis in reality!  Please report if this limit causes issues
2141
      with valid files.
2142
    */
2143
23.7M
    if (maximum_length > maximum_length_limit)
2144
41
      {
2145
41
        char message[MaxTextExtent];
2146
41
        MagickFormatString(message,sizeof(message),"Maximum length: %g, Scale: %g", maximum_length, scale);
2147
41
        ThrowException(&image->exception,DrawError,UnreasonableDashPolygonLength,message);
2148
41
        status=MagickFail;
2149
41
        goto draw_dash_polygon_fail;
2150
41
      }
2151
23.7M
    if (length == 0.0)
2152
1.52k
      {
2153
1.52k
        if (draw_info->dash_pattern[n] != 0.0)
2154
0
          n++;
2155
1.52k
        if (draw_info->dash_pattern[n] == 0.0)
2156
1.52k
          n=0;
2157
1.52k
        length=scale*draw_info->dash_pattern[n];
2158
1.52k
      }  /* oss-fuzz 10614 maximum_length = 2120554, length = 0.5 yields 4,241,108 iterations*/
2159
29.7M
    for (total_length=0.0; (length >= 0.0) && (maximum_length >= (length+total_length)); )
2160
5.95M
    {
2161
5.95M
      total_length+=length;
2162
5.95M
      if (n & 0x01)
2163
2.95M
        {
2164
2.95M
          dash_polygon[0]=primitive_info[0];
2165
2.95M
          dash_polygon[0].point.x=primitive_info[i-1].point.x+
2166
2.95M
            dx*total_length/maximum_length;
2167
2.95M
          dash_polygon[0].point.y=primitive_info[i-1].point.y+
2168
2.95M
            dy*total_length/maximum_length;
2169
2.95M
          j=1;
2170
2.95M
        }
2171
2.99M
      else
2172
2.99M
        {
2173
2.99M
          if (j+1 > number_vertices)
2174
724
            break;
2175
2.99M
          dash_polygon[j]=primitive_info[i-1];
2176
2.99M
          dash_polygon[j].point.x=primitive_info[i-1].point.x+
2177
2.99M
            dx*total_length/maximum_length;
2178
2.99M
          dash_polygon[j].point.y=primitive_info[i-1].point.y+
2179
2.99M
            dy*total_length/maximum_length;
2180
2.99M
          dash_polygon[j].coordinates=1;
2181
2.99M
          j++;
2182
2.99M
          dash_polygon[0].coordinates=j;
2183
2.99M
          dash_polygon[j].primitive=UndefinedPrimitive;
2184
2.99M
          status &= DrawStrokePolygon(image,clone_info,dash_polygon);
2185
2.99M
          if (status == MagickFail)
2186
175
            break;
2187
2.99M
        }
2188
5.95M
      if (draw_info->dash_pattern[n] != 0.0)
2189
5.94M
        n++;
2190
5.95M
      if (draw_info->dash_pattern[n] == 0.0)
2191
2.96M
        n=0;
2192
5.95M
      length=scale*draw_info->dash_pattern[n];
2193
5.95M
    }
2194
23.7M
    length-=(maximum_length-total_length);
2195
23.7M
    if (n & 0x01)
2196
7.37M
      continue;
2197
16.3M
    dash_polygon[j]=primitive_info[i];
2198
16.3M
    dash_polygon[j].coordinates=1;
2199
16.3M
    j++;
2200
16.3M
  }
2201
28.0k
  if ((status != MagickFail) && (total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
2202
9.20k
    {
2203
9.20k
      dash_polygon[j]=primitive_info[i-1];
2204
9.20k
      dash_polygon[j].point.x+=MagickEpsilon;
2205
9.20k
      dash_polygon[j].point.y+=MagickEpsilon;
2206
9.20k
      dash_polygon[j].coordinates=1;
2207
9.20k
      j++;
2208
9.20k
      dash_polygon[0].coordinates=(size_t) j;
2209
9.20k
      dash_polygon[j].primitive=UndefinedPrimitive;
2210
9.20k
      status &= DrawStrokePolygon(image,clone_info,dash_polygon);
2211
9.20k
    }
2212
28.1k
 draw_dash_polygon_fail:;
2213
28.1k
  MagickFreeResourceLimitedMemory(PrimitiveInfo *, dash_polygon);
2214
28.1k
  DestroyDrawInfo(clone_info);
2215
28.1k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end draw-dash");
2216
28.1k
  return(status);
2217
28.0k
}
2218

2219
/*
2220
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2221
%                                                                             %
2222
%                                                                             %
2223
%                                                                             %
2224
%   D r a w I m a g e                                                         %
2225
%                                                                             %
2226
%                                                                             %
2227
%                                                                             %
2228
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2229
%
2230
%  Use DrawImage() to draw a graphic primitive on your image.  The primitive
2231
%  may be represented as a string or filename.  Precede the filename with an
2232
%  "at" sign (@) and the contents of the file are drawn on the image.  You
2233
%  can affect how text is drawn by setting one or more members of the draw
2234
%  info structure.
2235
%
2236
%  The format of the drawing primitive text is known as "MVG" ("Magick
2237
%  Vector Graphics"), which is a powerful yet simple drawing syntax fronted
2238
%  by the "MVG" coder, and substantially supporting W3C SVG 1.1
2239
%  (https://www.w3.org/TR/SVG11/) rendering when fronted by the "SVG" coder
2240
%  (which translates SVG into MVG).  The parser is non-validating and in fact
2241
%  is designed to attempt to skip over unhandled textual elements which may be
2242
%  passed through from the SVG coder. While being used as the basis of a useful
2243
%  W3C SVG implementation and while SVG is commonly used on the "web" as an
2244
%  untrusted input such as within web browsers, this implementation is not
2245
%  designed to handle arbitrary untrusted inputs delivered from the "Internet".
2246
%  The drawing capabilities are best used under controlled circumstances
2247
%  where drawing inputs can be reasonably trusted.
2248
%
2249
%  Note that this is a legacy interface. Authors of new code should consider
2250
%  using the Draw* methods defined by magick/draw.h since they are better
2251
%  documented and less error prone.
2252
%
2253
%  The format of the DrawImage method is:
2254
%
2255
%      MagickPassFail DrawImage(Image *image,const DrawInfo *draw_info)
2256
%
2257
%  A description of each parameter follows:
2258
%
2259
%    o image: The image.
2260
%
2261
%    o draw_info: The draw info.
2262
%
2263
%
2264
*/
2265
2266
static MagickBool
2267
IsPoint(const char *point)
2268
2.30M
{
2269
2.30M
  char
2270
2.30M
    *p;
2271
2272
2.30M
  double
2273
2.30M
    value;
2274
2275
  /* check for a floating-point (vs. integer) value so .25 will not be rejected */
2276
2.30M
  value=strtod(point,&p);
2277
2.30M
  (void) value;
2278
2.30M
  return(p != point);
2279
2.30M
}
2280
2281
/* Add two size_t values and check for unsigned overflow. */
2282
static MagickPassFail MagickAddSizeT(const size_t b, const size_t o, size_t *r)
2283
1.61M
{
2284
1.61M
  *r = b+o;
2285
1.61M
  return (((*r < b) || (*r < o)) ? MagickFail : MagickPass);
2286
1.61M
}
2287
2288
static const char recursion_key[] = "[DrawImageRecursion]";
2289
static long DrawImageGetCurrentRecurseLevel(Image *image)
2290
240k
{
2291
240k
  const ImageAttribute
2292
240k
    *attribute;
2293
2294
240k
  long
2295
240k
    recurse_level=0;
2296
2297
240k
  if ((attribute=GetImageAttribute(image,recursion_key)) != (ImageAttribute *) NULL)
2298
178k
    recurse_level=MagickAtoL(attribute->value);
2299
2300
240k
  return recurse_level;
2301
240k
}
2302
static void DrawImageSetCurrentRecurseLevel(Image *image,const long recurse_level)
2303
240k
{
2304
240k
  char
2305
240k
    recursion_str[MaxTextExtent];
2306
2307
240k
  MagickFormatString(recursion_str,sizeof(recursion_str),"%ld",recurse_level);
2308
  /* SetImageAttribute concatenates values! Delete with NULL */
2309
240k
  (void) SetImageAttribute(image,recursion_key,NULL);
2310
240k
  (void) SetImageAttribute(image,recursion_key,recursion_str);
2311
240k
}
2312
static MagickPassFail DrawImageRecurseIn(Image *image)
2313
120k
{
2314
120k
  long
2315
120k
    recurse_level;
2316
2317
120k
  recurse_level=DrawImageGetCurrentRecurseLevel(image);
2318
120k
  recurse_level++;
2319
120k
  DrawImageSetCurrentRecurseLevel(image,recurse_level);
2320
2321
120k
  if ((recurse_level < 0) || (recurse_level > MAX_DRAWIMAGE_RECURSION))
2322
351
    {
2323
351
      char
2324
351
        recursion_str[MaxTextExtent];
2325
351
      MagickFormatString(recursion_str,sizeof(recursion_str),"%ld",recurse_level);
2326
351
      ThrowException(&image->exception,DrawError,DrawingRecursionDetected,
2327
351
                     recursion_str);
2328
351
      return MagickFail;
2329
351
    }
2330
2331
119k
  return MagickPass;
2332
120k
}
2333
static void DrawImageRecurseOut(Image *image)
2334
119k
{
2335
119k
  long
2336
119k
    recurse_level=0;
2337
2338
119k
  recurse_level=DrawImageGetCurrentRecurseLevel(image);
2339
119k
  recurse_level--;
2340
119k
  DrawImageSetCurrentRecurseLevel(image,recurse_level);
2341
119k
}
2342
2343
2344
/*
2345
  Code from DrawImage() that extracted tokens between push/pop clip-path
2346
  and added them as an attribute has been refactored into new function
2347
  ExtractTokensBetweenPushPop().  Added to support new elements "use" and
2348
  "class".
2349
*/
2350
static
2351
char *  ExtractTokensBetweenPushPop (
2352
                                     char * q,                 /* address of pointer into primitive string */
2353
                                     char * token,             /* big enough buffer for extracted string */
2354
                                     size_t token_max_length,
2355
                                     char const * pop_string,  /* stop when we see pop pop_string */
2356
                                     Image *image,
2357
                                     size_t * pExtractedLength /* if not null, length of extracted string returned */
2358
                                     )
2359
13.1k
{/*ExtractTokensBetweenPushPop*/
2360
2361
13.1k
  char const *p;
2362
13.1k
  char const *pq;
2363
13.1k
  char *pAfterPopString = 0;
2364
13.1k
  char
2365
13.1k
    name[MaxTextExtent],
2366
13.1k
    pop_message[MaxTextExtent];
2367
13.1k
  size_t ExtractedLength = 0;
2368
13.1k
  MagickBool found_pop = MagickFalse;
2369
2370
13.1k
  MagickFormatString(pop_message,sizeof(pop_message),"push %.512s", pop_string);
2371
2372
  /* next token is name associated with push/pop data */
2373
13.1k
  pq = q;
2374
13.1k
  MagickGetToken(q,&q,token,token_max_length);
2375
13.1k
  if (pq == q)
2376
3
    {
2377
      /* failed to get a token */
2378
3
        if  ( pExtractedLength )
2379
1
          *pExtractedLength = ExtractedLength;
2380
3
        return NULL;
2381
3
    }
2382
13.1k
  MagickFormatString(name,sizeof(name),"[MVG:%.1024s]",token);
2383
13.1k
  MagickFormatString(pop_message,sizeof(pop_message),"push %.512s %.512s", pop_string, token);
2384
2385
  /* search for "pop <pop_string>" */
2386
169k
  for (p=q; *q != '\0'; )
2387
168k
    {
2388
168k
      char * qStart = q;
2389
168k
      MagickGetToken(q,&q,token,token_max_length);
2390
168k
      if  ( q == qStart )
2391
0
        {
2392
          /* infinite loop detection */
2393
0
          pAfterPopString = q;  /* need this to be valid */
2394
0
          break;
2395
0
        }
2396
168k
      if (LocaleCompare(token,"pop") == 0)
2397
22.5k
        {
2398
22.5k
          pAfterPopString = q;
2399
22.5k
          MagickGetToken(q,&pAfterPopString,token,token_max_length);
2400
22.5k
          if ( q == pAfterPopString )
2401
1
            {
2402
              /* infinite loop detection */
2403
1
              break;
2404
1
            }
2405
22.5k
          if (LocaleCompare(token,pop_string) == 0)
2406
12.7k
            {
2407
12.7k
              found_pop = MagickTrue;
2408
12.7k
              break;  /* found "pop <pop_string>" */
2409
12.7k
            }
2410
22.5k
        }
2411
168k
    }
2412
2413
13.1k
  if (found_pop)
2414
12.7k
    {
2415
      /* sanity check on extracted string length */
2416
12.7k
      if  ( q > (p+4U) )
2417
6.49k
        {
2418
6.49k
          ExtractedLength = q - (p+4U);
2419
6.49k
          (void) strncpy(token,p,ExtractedLength);
2420
6.49k
        }
2421
12.7k
      token[ExtractedLength] = '\0';
2422
      /* SetImageAttribute concatenates values! Delete with NULL */
2423
12.7k
      (void) SetImageAttribute(image,name,NULL);
2424
12.7k
      (void) SetImageAttribute(image,name,token);
2425
12.7k
      if (pAfterPopString != NULL)
2426
12.7k
        q = pAfterPopString;  /* skip ID string after "pop" */
2427
12.7k
    }
2428
462
  else
2429
462
    {
2430
462
      ThrowException(&image->exception,DrawError,UnbalancedPushPop,pop_message);
2431
462
    }
2432
13.1k
  if  ( pExtractedLength )
2433
5.95k
    *pExtractedLength = ExtractedLength;
2434
13.1k
  return(q);
2435
2436
13.1k
}/*ExtractTokensBetweenPushPop*/
2437
2438
2439
/*
2440
  Extract attribute name from input stream, get attribute, and insert it's
2441
  value into the input stream.  Return updated pointer into input stream.
2442
  Added to support new elements "use" and "class".
2443
2444
  FIXME: Need to add anti-recursion measures.
2445
*/
2446
static
2447
char * InsertAttributeIntoInputStream (
2448
  const char *keyword,        /* MVG keyword for diagnostics/debugging */
2449
  char * q,                   /* address of pointer into primitive string*/
2450
  char ** pprimitive,         /* ptr to ptr to primitive string buffer */
2451
  size_t * pprimitive_extent,
2452
  char ** ptoken,             /* ptr to ptr to big enough buffer for extracted string */
2453
  size_t * ptoken_max_length,
2454
  Image *image,
2455
  MagickPassFail * pStatus,
2456
  MagickBool UndefAttrIsError
2457
  )
2458
7.59k
{/*InsertAttributeIntoInputStream*/
2459
2460
7.59k
  char AttributeName[MaxTextExtent];
2461
7.59k
  const ImageAttribute *attribute;
2462
7.59k
  size_t AttributeLength;
2463
7.59k
  size_t RemainingLength;
2464
7.59k
  size_t NeededLength;
2465
2466
7.59k
  if (*pStatus == MagickFail)
2467
0
    return (char *) NULL;
2468
2469
  /* get attribute name, then get attribute value */
2470
7.59k
  if (MagickGetToken(q,&q,*ptoken,*ptoken_max_length) < 1)
2471
1
    {
2472
1
      *pStatus = MagickFail;
2473
1
      return(q);
2474
1
    }
2475
7.59k
  MagickFormatString(AttributeName,sizeof(AttributeName),"[MVG:%.1024s]",*ptoken);
2476
7.59k
  attribute=GetImageAttribute(image,AttributeName);
2477
7.59k
  if (attribute == (ImageAttribute *) NULL)
2478
6.67k
    {
2479
      /* the client specifies whether or not an undefined attribute is an error */
2480
6.67k
      if  ( UndefAttrIsError )
2481
15
        {
2482
15
          char message[MaxTextExtent];
2483
15
          MagickFormatString(message,sizeof(message),"Primitive \"%s\" id \"%s\" not defined",keyword,*ptoken);
2484
15
          ThrowException(&image->exception,DrawError,InvalidPrimitiveArgument,message);
2485
15
          *pStatus = MagickFail;
2486
15
        }
2487
6.67k
      return(q);
2488
6.67k
    }
2489
2490
  /*
2491
    Insert attribute->value into input stream by concatenating it with the remainder
2492
    of the primitive, and updating the required state variables.
2493
  */
2494
924
  AttributeLength = attribute->length;
2495
924
  RemainingLength = *pprimitive_extent - (size_t)(q - *pprimitive);
2496
924
  NeededLength = AttributeLength + RemainingLength;
2497
2498
#if 0
2499
  if (image->logging)
2500
    (void) LogMagickEvent(RenderEvent,GetMagickModule(),
2501
                          "InsertAttributeIntoInputStream(): keyword \"%s\" "
2502
                          "AttributeName \"%s\", AttributeLength=%zu, "
2503
                          "RemainingLength=%zu, NeededLength=%zu",
2504
                          keyword, AttributeName, AttributeLength,
2505
                          RemainingLength, NeededLength);
2506
#endif
2507
2508
924
  if  ( NeededLength <= *pprimitive_extent )
2509
796
    {/*combined strings fit in existing primitive buffer*/
2510
2511
796
      q -= AttributeLength;
2512
796
      memcpy(q,attribute->value,AttributeLength);
2513
2514
796
    }/*combined strings fit in existing primitive buffer*/
2515
128
  else
2516
128
    {/*combined strings need bigger buffer*/
2517
128
#if 1
2518
      /*
2519
        Based on original free/new design
2520
      */
2521
128
      char * primitiveNew;
2522
128
      char * tokenNew;
2523
128
      *pprimitive_extent = NeededLength;
2524
128
      primitiveNew = MagickAllocateResourceLimitedMemory(char *,NeededLength+1);
2525
128
      if (primitiveNew == (char *) NULL)
2526
0
        {
2527
0
          *pStatus = MagickFail;
2528
0
          return(q);
2529
0
        }
2530
128
      (void) memcpy(primitiveNew,attribute->value,AttributeLength); /* Attribute value */
2531
128
      (void) memcpy(primitiveNew+AttributeLength,q,RemainingLength); /* Remaining */
2532
128
      primitiveNew[NeededLength] = '\0';
2533
2534
128
      MagickFreeResourceLimitedMemory(char *, *pprimitive);
2535
128
      q = *pprimitive = primitiveNew;
2536
128
      MagickFreeResourceLimitedMemory(char *, *ptoken);
2537
128
      tokenNew = MagickAllocateResourceLimitedMemory(char *,NeededLength+1);
2538
128
      if (tokenNew == (char *) NULL)
2539
0
        {
2540
0
          *pStatus = MagickFail;
2541
0
          return(q);
2542
0
        }
2543
128
      *ptoken = tokenNew;
2544
128
      *ptoken_max_length = NeededLength;
2545
#else
2546
      /*
2547
        Based on reallocation design
2548
      */
2549
      {
2550
        char * primitiveNew;
2551
        size_t Offset;
2552
        *pprimitive_extent = NeededLength;
2553
        Offset = q-*pprimitive;
2554
        primitiveNew = MagickReallocateResourceLimitedMemory(char *,*pprimitive,NeededLength+1);
2555
        if (primitiveNew == (char *) NULL)
2556
          {
2557
            *pStatus = MagickFail;
2558
            return(q);
2559
          }
2560
        /* FIXME: Need to verify this! */
2561
        (void) memmove(/* dest */primitiveNew+AttributeLength,/* source */primitiveNew+Offset,RemainingLength); /* Remaining */
2562
        (void) memcpy(primitiveNew,attribute->value,AttributeLength); /* Attribute value */
2563
        primitiveNew[NeededLength] = '\0';
2564
        q = *pprimitive = primitiveNew;
2565
      }
2566
      {
2567
        char * tokenNew;
2568
        tokenNew = MagickReallocateResourceLimitedMemory(char *,*ptoken,NeededLength+1);
2569
        if (tokenNew == (char *) NULL)
2570
          {
2571
            *pStatus = MagickFail;
2572
            return(q);
2573
          }
2574
        *ptoken = tokenNew;
2575
        *ptoken_max_length = NeededLength;
2576
      }
2577
#if 0
2578
     if (image->logging)
2579
        (void) LogMagickEvent(RenderEvent,GetMagickModule(),
2580
                              "InsertAttributeIntoInputStream(): \"%s\"", *pprimitive);
2581
#endif
2582
#endif
2583
2584
128
    }/*combined strings need bigger buffer*/
2585
2586
924
  return(q);
2587
2588
924
}/*InsertAttributeIntoInputStream*/
2589
2590
2591
/*
2592
  Re-allocate PrimitiveInfo array if Needed elements are not
2593
  available.  Return MagickFail if allocation has failed.
2594
  PrimitiveInfo array pointer is set to NULL if allocation has failed,
2595
  and any already allocated memory is freed.
2596
*/
2597
static MagickPassFail
2598
PrimitiveInfoRealloc(PrimitiveInfoMgr * p_PIMgr, const size_t Needed)
2599
808k
{/*PrimitiveInfoRealloc*/
2600
2601
808k
  MagickPassFail status = MagickPass;
2602
808k
  size_t NeedAllocCount;
2603
2604
#if 0
2605
  fprintf(stderr,"Needed = %" MAGICK_SIZE_T_F "u\n",(MAGICK_SIZE_T) Needed);
2606
#endif
2607
  /* NeedAllocCount = p_PIMgr->StoreStartingAt + Needed + ((size_t)100 */
2608
808k
  status &= MagickAddSizeT(p_PIMgr->StoreStartingAt, Needed, &NeedAllocCount); /* + Needed */
2609
808k
  status &= MagickAddSizeT(NeedAllocCount, 100, &NeedAllocCount); /* +100 for headroom */
2610
2611
808k
  if (status == MagickFail)
2612
0
    ThrowException3(p_PIMgr->p_Exception,DrawError,ArithmeticOverflow,UnableToDrawOnImage);
2613
2614
  /* Check if we have exceeded primitive info allocation hard limit */
2615
808k
  if (NeedAllocCount > PRIMITIVE_INFO_POINTS_MAX)
2616
0
    {
2617
0
      MagickFreeResourceLimitedMemory(PrimitiveInfo *, *p_PIMgr->pp_PrimitiveInfo);
2618
0
      ThrowException3(p_PIMgr->p_Exception,ResourceLimitError,MemoryAllocationFailed,UnableToDrawOnImage);
2619
0
      status = MagickFail;
2620
0
    }
2621
2622
808k
  if ((status == MagickPass) && (NeedAllocCount > *p_PIMgr->p_AllocCount))
2623
162k
    {
2624
162k
      PrimitiveInfo *new_primitive_info;
2625
162k
      new_primitive_info=MagickReallocateResourceLimitedClearedArray(PrimitiveInfo *,
2626
162k
                                                                     *p_PIMgr->pp_PrimitiveInfo,
2627
162k
                                                                     NeedAllocCount,
2628
162k
                                                                     sizeof(PrimitiveInfo));
2629
162k
      if (new_primitive_info == (PrimitiveInfo *) NULL)
2630
276
        {
2631
          /* PrimitiveInfo contains a pointer to "text" which may need to be freed! */
2632
276
          PrimitiveInfo *primitive_info;
2633
276
          size_t i, AllocCount;
2634
276
          primitive_info=*p_PIMgr->pp_PrimitiveInfo;
2635
276
          AllocCount=*p_PIMgr->p_AllocCount;
2636
136M
          for (i = 0; i < AllocCount; i++)
2637
136M
            MagickFreeMemory(primitive_info[i].text);
2638
276
          MagickFreeResourceLimitedMemory(PrimitiveInfo *, *p_PIMgr->pp_PrimitiveInfo);
2639
276
          ThrowException3(p_PIMgr->p_Exception,ResourceLimitError,MemoryAllocationFailed,UnableToDrawOnImage);
2640
276
          status = MagickFail;
2641
276
        }
2642
161k
      else
2643
161k
        {
2644
161k
          *p_PIMgr->pp_PrimitiveInfo=new_primitive_info;
2645
          /* Save new allocation count */
2646
161k
          *p_PIMgr->p_AllocCount = NeedAllocCount;
2647
161k
        }
2648
162k
    }
2649
2650
808k
  return status;
2651
2652
808k
}/*PrimitiveInfoRealloc*/
2653
2654
2655
MagickExport MagickPassFail
2656
DrawImage(Image *image,const DrawInfo *draw_info)
2657
120k
{
2658
259k
#define RenderImageText "[%s] Render..."
2659
2660
120k
  AffineMatrix
2661
120k
    affine,
2662
120k
    current;
2663
2664
120k
  char
2665
120k
    key[2*MaxTextExtent],
2666
120k
    keyword[MaxTextExtent],
2667
120k
    geometry[MaxTextExtent],
2668
120k
    name[MaxTextExtent],
2669
120k
    pattern[MaxTextExtent],
2670
120k
    *original_primitive = (char *) NULL,
2671
120k
    *primitive = (char *) NULL,
2672
120k
    *q,
2673
120k
    *token = (char *) NULL;
2674
2675
120k
  double
2676
120k
    angle,
2677
120k
    factor,
2678
120k
    points_length;  /* primitive_extent is now a size_t */
2679
2680
120k
  DrawInfo
2681
120k
    **graphic_context = (DrawInfo **) NULL;
2682
2683
120k
  long
2684
120k
    j,
2685
120k
    k,
2686
120k
    n=0;
2687
2688
120k
  PointInfo
2689
120k
    point;
2690
2691
120k
  PixelPacket
2692
120k
    start_color;
2693
2694
120k
  PrimitiveInfo
2695
120k
    *primitive_info = (PrimitiveInfo *) NULL;
2696
2697
120k
  PrimitiveType
2698
120k
    primitive_type;
2699
2700
120k
  register char
2701
120k
    *p;
2702
2703
120k
  size_t
2704
120k
    i;
2705
2706
120k
  register long
2707
120k
    x;
2708
2709
120k
  SegmentInfo
2710
120k
    bounds,
2711
120k
    viewbox;
2712
2713
120k
  size_t
2714
120k
    length,
2715
120k
    token_max_length,
2716
120k
    primitive_extent;
2717
2718
120k
  MagickPassFail
2719
120k
    status = MagickPass;
2720
2721
120k
  size_t
2722
120k
    number_points=0;
2723
2724
120k
  MagickBool
2725
120k
    FillOpacityPending,
2726
120k
    StrokeOpacityPending;
2727
2728
120k
  double
2729
120k
    FillOpacitySaved,
2730
120k
    StrokeOpacitySaved;
2731
2732
120k
  MagickBool
2733
120k
    TextRotationPerformed;  /* see comments below where TextRotationPerformed=MagickFalse */
2734
2735
  /*
2736
    Use defsPushCount to track when we enter/leave <defs> ... </defs> so we
2737
    know not to render any graphical elements defined within (per the SVG spec).
2738
    This is accomplished by turning off the "render" flag in the graphics
2739
    context if defsPushCount > 0.
2740
  */
2741
120k
  int
2742
120k
    defsPushCount;
2743
2744
  /* These variables are used to track the current text position */
2745
120k
  double
2746
120k
    xTextCurrent,
2747
120k
    yTextCurrent;
2748
120k
  MagickBool
2749
120k
    UseCurrentTextPosition;     /* true=>use (possibly modified) current text position */
2750
2751
120k
  PrimitiveInfoMgr
2752
120k
    PIMgr;
2753
2754
  /*
2755
    Ensure the annotation info is valid.
2756
  */
2757
120k
  assert(image != (Image *) NULL);
2758
120k
  assert(image->signature == MagickSignature);
2759
120k
  assert(draw_info != (DrawInfo *) NULL);
2760
120k
  assert(draw_info->signature == MagickSignature);
2761
120k
  assert(draw_info->primitive != (char *) NULL);
2762
2763
  /*
2764
    gdb notes for setting a watchpoint for 'status' variable change
2765
    from MagickPass to MagickFail:
2766
2767
    set $prev_val = status
2768
    watch status if $prev_val == 1 && status == 0 && ($prev_val = status, 1)
2769
  */
2770
2771
120k
  keyword[0]='\0';
2772
2773
120k
  if (*draw_info->primitive == '\0')
2774
12
    return(MagickFail);
2775
2776
  /*
2777
    Check for DrawImage recursion
2778
  */
2779
120k
  if ((status &= DrawImageRecurseIn(image)) == MagickFail) /* Already throws exception */
2780
351
    {
2781
351
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Drawing recursion detected!");
2782
351
      return MagickFail;
2783
351
    }
2784
2785
119k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"begin draw-image");
2786
2787
119k
  if ((status &= SetImageType(image,TrueColorType)) == MagickFail)
2788
0
    {
2789
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2790
0
      goto draw_image_done;
2791
0
    }
2792
2793
  /*
2794
    Read primitive from file if supplied primitive starts with '@' and
2795
    we are not already drawing.
2796
  */
2797
119k
  if ((*draw_info->primitive == '@') &&
2798
5
      (DrawImageGetCurrentRecurseLevel(image) == 1))
2799
2
    {
2800
2
      original_primitive=(char *) FileToBlob(draw_info->primitive+1,&length,&image->exception);
2801
2
    }
2802
119k
  else
2803
119k
    {
2804
119k
      original_primitive=draw_info->primitive;
2805
119k
    }
2806
119k
  if (original_primitive == (char *) NULL)
2807
2
    {
2808
2
      ThrowException3(&image->exception,DrawError,NonconformingDrawingPrimitiveDefinition,UnableToDrawOnImage);
2809
2
      status=MagickFail;
2810
2
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2811
2
      goto draw_image_done;
2812
2
    }
2813
2814
119k
  if (getenv("MAGICK_SKIP_RENDERING") != NULL)
2815
0
    {
2816
0
      status=MagickPass;
2817
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit without error");
2818
0
      goto draw_image_done;
2819
0
    }
2820
2821
119k
  primitive_extent=strlen(original_primitive);
2822
119k
  primitive = MagickAllocateResourceLimitedMemory(char *, primitive_extent+1);
2823
119k
  if (primitive == (char *) NULL)
2824
0
    {
2825
0
      ThrowException3(&image->exception,ResourceLimitError,MemoryAllocationFailed,
2826
0
                      UnableToDrawOnImage);
2827
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2828
0
      status=MagickFail;
2829
0
      goto draw_image_done;
2830
0
    }
2831
119k
  (void) memcpy(primitive, original_primitive, primitive_extent+1);
2832
119k
  if (original_primitive != draw_info->primitive)
2833
0
    MagickFreeMemory(original_primitive);
2834
2835
  /*
2836
    Allocate primitive info memory.
2837
  */
2838
119k
  graphic_context=MagickAllocateResourceLimitedClearedArray(DrawInfo **,n+1,sizeof(DrawInfo *));
2839
119k
  if (graphic_context == (DrawInfo **) NULL)
2840
0
    {
2841
0
      ThrowException3(&image->exception,ResourceLimitError,MemoryAllocationFailed,UnableToDrawOnImage);
2842
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2843
0
      status=MagickFail;
2844
0
      goto draw_image_done;
2845
0
    }
2846
2847
  /* Set up to manage growing the PrimitiveInfo array */
2848
119k
  PIMgr.pp_PrimitiveInfo = &primitive_info;
2849
119k
  PIMgr.p_AllocCount = &number_points;
2850
119k
  PIMgr.StoreStartingAt = 0;
2851
119k
  PIMgr.p_Exception = &image->exception;
2852
2853
119k
  if ((status &= PrimitiveInfoRealloc(&PIMgr,6553)) == MagickFail) /* FIXME: oss-fuzz 499125828 leak */
2854
2
    {
2855
2
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2856
2
      goto draw_image_done;
2857
2
    }
2858
119k
  if ((status &= QueryColorDatabase("black",&start_color,&image->exception)) == MagickFail)
2859
0
    {
2860
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2861
0
      goto draw_image_done;
2862
0
    }
2863
119k
  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
2864
  /* next two lines: don't need copy of primitive, just a buffer of the same size */
2865
119k
  token=MagickAllocateResourceLimitedMemory(char *,primitive_extent+1);
2866
119k
  token_max_length=primitive_extent;
2867
119k
  if (token == (char *) NULL)
2868
1
    {
2869
1
      status=MagickFail;
2870
1
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
2871
1
      goto draw_image_done;
2872
1
    }
2873
119k
  defsPushCount = 0;  /* not inside of <defs> ... </defs> */
2874
119k
  xTextCurrent = yTextCurrent = 0.0;  /* initialize current text position */
2875
  /*
2876
    The purpose of these next four variables is to attempt to handle cases like:
2877
2878
    <rect stroke-opacity="0.5" stroke="green" ... />
2879
2880
    I.e., when the stroke-opacity is set before the stroke color is specified (ditto
2881
    for fill-opacity and fill color).  This creates a problem when the current stroke
2882
    color is "none" (the typical inherited default).  A value of "none" causes the
2883
    stroke color to be set to transparent black, wiping out any current stroke-opacity
2884
    information.  Furthermore, as long as the stroke color is "none", any attempt to
2885
    set the stroke-opacity will be ignored.  I have not tested this, but I believe the
2886
    same condition can be arrived at by setting stroke="black" and stroke-opacity="0".
2887
2888
    The real solution to this problem is to store the stroke="none" state without
2889
    affecting the current stroke-opacity value.  This would require additional data
2890
    structure members and code modifications, so for now we handle this situation
2891
    by remembering when there is a pending stroke-opacity request (same for
2892
    fill-opacity).  When the stroke (fill) color is eventually set, the pending
2893
    opacity value is also set.
2894
2895
    The "pending" flags are reset to false whenever a graphic-context is pushed or
2896
    popped, so this fix may only solve a limited set of cases.  However, these cases
2897
    just happen to be the most common ones.
2898
  */
2899
119k
  FillOpacityPending = MagickFalse;
2900
119k
  StrokeOpacityPending = MagickFalse;
2901
119k
  FillOpacitySaved = 0.0;
2902
119k
  StrokeOpacitySaved = 0.0;
2903
2904
  /*
2905
    When DrawImage() was modified to provide management of the current text position to
2906
    the client, text rotation also had to be modified.  Previously, the client would
2907
    perform text rotation (i.e., next character is to be rotated) by supplying the following
2908
    sequence:
2909
2910
    translate x y (where x, y indicate the current text position)
2911
    rotate angle (where angle is the rotation angle)
2912
2913
    Later, when the actual 'text x y string' is supplied by the client, x and y must both
2914
    be zero since the positioning has already been taken care of by the translate/rotate
2915
    sequence.  The next variable indicates that rotation is being applied so that we can
2916
    use (0,0) instead of the actual current text position.
2917
  */
2918
119k
  TextRotationPerformed = MagickFalse;
2919
2920
2.08M
  for (q=primitive; *q != '\0'; )
2921
2.03M
    {
2922
2.03M
      UseCurrentTextPosition = False;
2923
      /*
2924
        Interpret graphic primitive.
2925
      */
2926
2.03M
      if (MagickGetToken(q,&q,keyword,MaxTextExtent) < 1)
2927
11.4k
        break;
2928
2.02M
      if (*keyword == '\0')
2929
2
        break;
2930
2.02M
      if (*keyword == '#')
2931
154k
        {
2932
          /*
2933
            Comment.
2934
          */
2935
9.78M
          while ((*q != '\n') && (*q != '\0'))
2936
9.63M
            q++;
2937
154k
          continue;
2938
154k
        }
2939
1.86M
      p=q-strlen(keyword); /* FIXME: Make sure that this rule works! */
2940
1.86M
      primitive_type=UndefinedPrimitive;
2941
1.86M
      current=graphic_context[n]->affine;
2942
1.86M
      IdentityAffine(&affine);
2943
1.86M
      token[0]='\0';
2944
1.86M
      switch (*keyword)
2945
1.86M
        {
2946
46.0k
        case ';':
2947
46.0k
          break;
2948
64.1k
        case 'a':
2949
85.9k
        case 'A':
2950
85.9k
          {
2951
85.9k
            if (LocaleCompare("affine",keyword) == 0)
2952
61.6k
              {
2953
61.6k
                MagickBool affine_args_good = MagickFalse;
2954
61.6k
                do
2955
61.6k
                  {
2956
                    /* sx */
2957
61.6k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2958
2
                      break;
2959
61.6k
                    if ((status &= MagickAtoFChk(token,&affine.sx)) == MagickFail)
2960
5
                      break;
2961
                    /* rx */
2962
61.6k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2963
2
                      break;
2964
61.6k
                    if (*token == ',')
2965
272
                      if (MagickGetToken(q,&q,token,token_max_length) < 1)
2966
1
                        break;
2967
61.6k
                    if ((status &= MagickAtoFChk(token,&affine.rx)) == MagickFail)
2968
4
                      break;
2969
                    /* ry */
2970
61.6k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2971
6
                      break;
2972
61.5k
                    if (*token == ',')
2973
83
                      if (MagickGetToken(q,&q,token,token_max_length) < 1)
2974
1
                        break;
2975
61.5k
                    if ((status &= MagickAtoFChk(token,&affine.ry)) == MagickFail)
2976
5
                      break;
2977
                    /* sy */
2978
61.5k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2979
2
                      break;
2980
61.5k
                    if (*token == ',')
2981
253
                      if (MagickGetToken(q,&q,token,token_max_length) < 1)
2982
1
                        break;
2983
61.5k
                    if ((status &= MagickAtoFChk(token,&affine.sy)) == MagickFail)
2984
7
                      break;
2985
                    /* tx */
2986
61.5k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2987
4
                      break;
2988
61.5k
                    if (*token == ',')
2989
211
                      if (MagickGetToken(q,&q,token,token_max_length) < 1)
2990
2
                        break;
2991
61.5k
                    if ((status &= MagickAtoFChk(token,&affine.tx)) == MagickFail)
2992
5
                      break;
2993
                    /* ty */
2994
61.5k
                    if (MagickGetToken(q,&q,token,token_max_length) < 1)
2995
3
                      break;
2996
61.5k
                    if (*token == ',')
2997
209
                      if (MagickGetToken(q,&q,token,token_max_length) < 1)
2998
1
                        break;
2999
61.5k
                    if ((status &= MagickAtoFChk(token,&affine.ty)) == MagickFail)
3000
7
                      break;
3001
61.5k
                    affine_args_good=MagickTrue;
3002
61.5k
                  } while (0);
3003
61.6k
                if (affine_args_good != MagickTrue)
3004
58
                  {
3005
58
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3006
58
                    status=MagickFail;
3007
58
                  }
3008
61.6k
                break;
3009
61.6k
              }
3010
24.3k
            if (LocaleCompare("arc",keyword) == 0)
3011
24.1k
              {
3012
24.1k
                primitive_type=ArcPrimitive;
3013
24.1k
                break;
3014
24.1k
              }
3015
182
            status=MagickFail;
3016
182
            break;
3017
24.3k
          }
3018
223
        case 'b':
3019
393
        case 'B':
3020
393
          {
3021
393
            if (LocaleCompare("bezier",keyword) == 0)
3022
347
              {
3023
347
                primitive_type=BezierPrimitive;
3024
347
                break;
3025
347
              }
3026
46
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3027
46
            status=MagickFail;
3028
46
            break;
3029
393
          }
3030
53.8k
        case 'c':
3031
54.4k
        case 'C':
3032
54.4k
          {
3033
54.4k
            if (LocaleCompare("class",keyword) == 0)
3034
6.74k
              {/*class*/
3035
                /* FIXME: Deal with possible recursion */
3036
6.74k
                q = InsertAttributeIntoInputStream(keyword,q,&primitive,&primitive_extent,
3037
6.74k
                                                   &token,&token_max_length,image,
3038
6.74k
                                                   &status,MagickFalse/*UndefAttrIsError*/);
3039
6.74k
                break;
3040
6.74k
              }/*class*/
3041
47.7k
            if (LocaleCompare("clip-path",keyword) == 0)
3042
19.1k
              {
3043
                /*
3044
                  Create clip mask.
3045
                */
3046
19.1k
                if (MagickGetToken(q,&q,token,token_max_length) < 1)
3047
8
                  {
3048
8
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3049
8
                    status=MagickFail;
3050
8
                    break;
3051
8
                  }
3052
19.1k
                if ((status &= CloneString(&graphic_context[n]->extra->clip_path,token)) == MagickFail)
3053
0
                  {
3054
0
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3055
0
                    break;
3056
0
                  }
3057
19.1k
                if ((status &= DrawClipPath(image,graphic_context[n],
3058
19.1k
                                            graphic_context[n]->extra->clip_path)) == MagickFail)
3059
8.86k
                  {
3060
8.86k
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3061
8.86k
                  }
3062
19.1k
                break;
3063
19.1k
              }
3064
28.5k
            if (LocaleCompare("clip-rule",keyword) == 0)
3065
416
              {
3066
416
                MagickGetToken(q,&q,token,token_max_length);
3067
416
                if (LocaleCompare("evenodd",token) == 0)
3068
66
                  {
3069
66
                    graphic_context[n]->fill_rule=EvenOddRule;
3070
66
                    break;
3071
66
                  }
3072
350
                if (LocaleCompare("nonzero",token) == 0)
3073
325
                  {
3074
325
                    graphic_context[n]->fill_rule=NonZeroRule;
3075
325
                    break;
3076
325
                  }
3077
25
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3078
25
                status=MagickFail;
3079
25
                break;
3080
350
              }
3081
28.1k
            if (LocaleCompare("clip-units",keyword) == 0)
3082
363
              {
3083
363
                MagickGetToken(q,&q,token,token_max_length);
3084
363
                if (LocaleCompare("userSpace",token) == 0)
3085
18
                  {
3086
18
                    graphic_context[n]->clip_units=UserSpace;
3087
18
                    break;
3088
18
                  }
3089
345
                if (LocaleCompare("userSpaceOnUse",token) == 0)
3090
312
                  {
3091
312
                    graphic_context[n]->clip_units=UserSpaceOnUse;
3092
312
                    break;
3093
312
                  }
3094
33
                if (LocaleCompare("objectBoundingBox",token) == 0)
3095
18
                  {
3096
18
                    graphic_context[n]->clip_units=ObjectBoundingBox;
3097
18
                    IdentityAffine(&current);
3098
18
                    affine.sx=draw_info->bounds.x2;
3099
18
                    affine.sy=draw_info->bounds.y2;
3100
18
                    affine.tx=draw_info->bounds.x1;
3101
18
                    affine.ty=draw_info->bounds.y1;
3102
18
                    break;
3103
18
                  }
3104
15
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3105
15
                status=MagickFail;
3106
15
                break;
3107
33
              }
3108
27.7k
            if (LocaleCompare("circle",keyword) == 0)
3109
17.1k
              {
3110
17.1k
                primitive_type=CirclePrimitive;
3111
17.1k
                break;
3112
17.1k
              }
3113
10.6k
            if (LocaleCompare("color",keyword) == 0)
3114
10.5k
              {
3115
10.5k
                primitive_type=ColorPrimitive;
3116
10.5k
                break;
3117
10.5k
              }
3118
91
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3119
91
            status=MagickFail;
3120
91
            break;
3121
10.6k
          }
3122
772
        case 'd':
3123
779
        case 'D':
3124
779
          {
3125
779
            if (LocaleCompare("decorate",keyword) == 0)
3126
744
              {
3127
744
                MagickGetToken(q,&q,token,token_max_length);
3128
744
                if (LocaleCompare("none",token) == 0)
3129
68
                  {
3130
68
                    graphic_context[n]->decorate=NoDecoration;
3131
68
                    break;
3132
68
                  }
3133
676
                if (LocaleCompare("underline",token) == 0)
3134
5
                  {
3135
5
                    graphic_context[n]->decorate=UnderlineDecoration;
3136
5
                    break;
3137
5
                  }
3138
671
                if (LocaleCompare("overline",token) == 0)
3139
435
                  {
3140
435
                    graphic_context[n]->decorate=OverlineDecoration;
3141
435
                    break;
3142
435
                  }
3143
236
                if (LocaleCompare("line-through",token) == 0)
3144
215
                  {
3145
215
                    graphic_context[n]->decorate=LineThroughDecoration;
3146
215
                    break;
3147
215
                  }
3148
21
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3149
21
                status=MagickFail;
3150
21
                break;
3151
236
              }
3152
35
            status=MagickFail;
3153
35
            break;
3154
779
          }
3155
2.27k
        case 'e':
3156
2.47k
        case 'E':
3157
2.47k
          {
3158
2.47k
            if (LocaleCompare("ellipse",keyword) == 0)
3159
1.99k
              {
3160
1.99k
                primitive_type=EllipsePrimitive;
3161
1.99k
                break;
3162
1.99k
              }
3163
480
            if (LocaleCompare("encoding",keyword) == 0)
3164
407
              {
3165
                /*
3166
                  If the encoding argument is '', it will result in a
3167
                  zero-length token, which is a valid argument. But
3168
                  MagickGetToken() now returns 0 and an empty token.
3169
                */
3170
407
                MagickGetToken(q,&q,token,token_max_length);
3171
407
                if ((status &= CloneString(&graphic_context[n]->encoding,token)) == MagickFail)
3172
0
                  {
3173
0
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3174
0
                    break;
3175
0
                  }
3176
407
                break;
3177
407
              }
3178
73
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3179
73
            status=MagickFail;
3180
73
            break;
3181
480
          }
3182
196k
        case 'f':
3183
217k
        case 'F':
3184
217k
          {
3185
217k
            if (LocaleCompare("fill",keyword) == 0)
3186
99.8k
              {
3187
99.8k
                MagickGetToken(q,&q,token,token_max_length);
3188
99.8k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
3189
4.32k
                  break;    /* if drawing clip path, ignore changes to fill color */
3190
95.4k
                MagickFormatString(pattern,sizeof(pattern),"[MVG:%.1024s]",token);
3191
95.4k
                if (GetImageAttribute(image,pattern) != (ImageAttribute *) NULL)
3192
22.2k
                  {
3193
22.2k
                    if ((status &= DrawPatternPath(image,draw_info,token,
3194
22.2k
                                                   &graphic_context[n]->fill_pattern)) == MagickFail) /* Fails for MasterBathroom7.svg */
3195
64
                      {
3196
64
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3197
64
                        break;
3198
64
                      }
3199
22.2k
                  }
3200
73.2k
                else
3201
73.2k
                  {/*fill color, not pattern*/
3202
3203
                    /* when setting new fill color, try to preserve fill-opacity */
3204
73.2k
                    Quantum FillOpacityOld = graphic_context[n]->fill.opacity;
3205
73.2k
                    if ((status &= QueryColorDatabase(token,&graphic_context[n]->fill,&image->exception)) == MagickFail)
3206
108
                      {
3207
108
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3208
108
                        break;
3209
108
                      }
3210
73.1k
                    if (graphic_context[n]->fill.opacity != TransparentOpacity)
3211
65.8k
                      {/*new fill color != 'none'*/
3212
3213
65.8k
                        if  ( FillOpacityOld != TransparentOpacity )
3214
60.1k
                          graphic_context[n]->fill.opacity = FillOpacityOld;  /* already has group opacity included */
3215
5.68k
                        else  /* combine fill color's opacity with group opacity per SVG spec */
3216
5.68k
                          {
3217
5.68k
                            Quantum FillOpacity = FillOpacityPending ?
3218
5.59k
                              MaxRGB - (Quantum) (MaxRGBDouble * FillOpacitySaved + 0.5) : graphic_context[n]->fill.opacity;
3219
5.68k
                            graphic_context[n]->fill.opacity =
3220
5.68k
                              MaxRGB - (Quantum)((double)(MaxRGB - FillOpacity) *
3221
5.68k
                                                 (double)(MaxRGB - graphic_context[n]->opacity) / MaxRGBDouble + 0.5);
3222
5.68k
                          }
3223
65.8k
                        FillOpacityPending = MagickFalse;
3224
3225
65.8k
                      }/*new fill color != 'none'*/
3226
3227
73.1k
                  }/*fill color, not pattern*/
3228
95.3k
                break;
3229
95.4k
              }
3230
117k
            if (LocaleCompare("fill-rule",keyword) == 0)
3231
46.9k
              {
3232
46.9k
                MagickGetToken(q,&q,token,token_max_length);
3233
46.9k
                if (LocaleCompare("evenodd",token) == 0)
3234
6
                  {
3235
6
                    graphic_context[n]->fill_rule=EvenOddRule;
3236
6
                    break;
3237
6
                  }
3238
46.9k
                if (LocaleCompare("nonzero",token) == 0)
3239
46.9k
                  {
3240
46.9k
                    graphic_context[n]->fill_rule=NonZeroRule;
3241
46.9k
                    break;
3242
46.9k
                  }
3243
20
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3244
20
                status=MagickFail;
3245
20
                break;
3246
46.9k
              }
3247
70.5k
            if (LocaleCompare("fill-opacity",keyword) == 0)
3248
49.3k
              {
3249
49.3k
                double opacity;
3250
49.3k
                MagickGetToken(q,&q,token,token_max_length);
3251
49.3k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
3252
2.78k
                  break;    /* if drawing clip path, ignore changes to fill opacity */
3253
46.6k
                factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3254
46.6k
                if ((status &= MagickAtoFChk(token,&opacity)) == MagickFail)
3255
5
                  {
3256
5
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3257
5
                    break;
3258
5
                  }
3259
46.5k
                if (opacity < 0.0)
3260
2
                  {
3261
2
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3262
2
                    status=MagickFail;
3263
2
                    break;
3264
2
                  }
3265
46.5k
                opacity *= factor;
3266
46.5k
                if  ( opacity <= 0.0 )   /* per SVG spec */
3267
271
                  opacity = 0.0;
3268
46.3k
                else if  ( opacity > 1.0 )
3269
544
                  opacity = 1.0;
3270
46.5k
                FillOpacitySaved = opacity;
3271
46.5k
                if (graphic_context[n]->fill.opacity != TransparentOpacity)
3272
45.6k
                  {
3273
                    /* combine new fill-opacity with group opacity per SVG spec */
3274
45.6k
                    double opacityGroup = MaxRGB - graphic_context[n]->opacity; /* MaxRGB==opaque */
3275
45.6k
                    graphic_context[n]->fill.opacity = MaxRGB - (Quantum)(opacity * opacityGroup + 0.5);
3276
45.6k
                    FillOpacityPending = MagickFalse;
3277
45.6k
                  }
3278
950
                else
3279
950
                  FillOpacityPending = MagickTrue;
3280
46.5k
                break;
3281
46.5k
              }
3282
21.1k
            if (LocaleCompare("font",keyword) == 0)
3283
4.48k
              {
3284
4.48k
                MagickGetToken(q,&q,token,token_max_length);
3285
4.48k
                if ((status &= CloneString(&graphic_context[n]->font,token)) == MagickFail)
3286
0
                  {
3287
0
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3288
0
                  }
3289
4.48k
                if (LocaleCompare("none",token) == 0)
3290
42
                  MagickFreeMemory(graphic_context[n]->font);
3291
4.48k
                break;
3292
4.48k
              }
3293
16.6k
            if (LocaleCompare("font-family",keyword) == 0)
3294
9.79k
              {
3295
9.79k
                MagickGetToken(q,&q,token,token_max_length);
3296
9.79k
                if ((status &= CloneString(&graphic_context[n]->family,token)) == MagickFail)
3297
0
                  {
3298
0
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3299
0
                  }
3300
9.79k
                break;
3301
9.79k
              }
3302
6.84k
            if (LocaleCompare("font-size",keyword) == 0)
3303
823
              {
3304
823
                MagickGetToken(q,&q,token,token_max_length);
3305
823
                if ((status &= MagickAtoFChk(token,&graphic_context[n]->pointsize)) == MagickFail)
3306
3
                  {
3307
3
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3308
3
                  }
3309
823
                break;
3310
823
              }
3311
6.02k
            if (LocaleCompare("font-stretch",keyword) == 0)
3312
2.06k
              {
3313
2.06k
                MagickGetToken(q,&q,token,token_max_length);
3314
2.06k
                if (LocaleCompare(token,"all") == 0)
3315
3
                  graphic_context[n]->stretch=AnyStretch;
3316
2.06k
                if (LocaleCompare(token,"condensed") == 0)
3317
6
                  graphic_context[n]->stretch=CondensedStretch;
3318
2.06k
                if (LocaleCompare(token,"expanded") == 0)
3319
331
                  graphic_context[n]->stretch=ExpandedStretch;
3320
2.06k
                if (LocaleCompare(token,"extra-condensed") == 0)
3321
337
                  graphic_context[n]->stretch=ExtraCondensedStretch;
3322
2.06k
                if (LocaleCompare(token,"extra-expanded") == 0)
3323
199
                  graphic_context[n]->stretch=ExtraExpandedStretch;
3324
2.06k
                if (LocaleCompare(token,"normal") == 0)
3325
3
                  graphic_context[n]->stretch=NormalStretch;
3326
2.06k
                if (LocaleCompare(token,"semi-condensed") == 0)
3327
68
                  graphic_context[n]->stretch=SemiCondensedStretch;
3328
2.06k
                if (LocaleCompare(token,"semi-expanded") == 0)
3329
228
                  graphic_context[n]->stretch=SemiExpandedStretch;
3330
2.06k
                if (LocaleCompare(token,"ultra-condensed") == 0)
3331
1
                  graphic_context[n]->stretch=UltraCondensedStretch;
3332
2.06k
                if (LocaleCompare(token,"ultra-expanded") == 0)
3333
34
                  graphic_context[n]->stretch=UltraExpandedStretch;
3334
2.06k
                break;
3335
2.06k
              }
3336
3.95k
            if (LocaleCompare("font-style",keyword) == 0)
3337
1.59k
              {
3338
1.59k
                MagickGetToken(q,&q,token,token_max_length);
3339
1.59k
                if (LocaleCompare(token,"all") == 0)
3340
18
                  graphic_context[n]->style=AnyStyle;
3341
1.59k
                if (LocaleCompare(token,"italic") == 0)
3342
70
                  graphic_context[n]->style=ItalicStyle;
3343
1.59k
                if (LocaleCompare(token,"normal") == 0)
3344
679
                  graphic_context[n]->style=NormalStyle;
3345
1.59k
                if (LocaleCompare(token,"oblique") == 0)
3346
102
                  graphic_context[n]->style=ObliqueStyle;
3347
1.59k
                break;
3348
1.59k
              }
3349
2.36k
            if (LocaleCompare("font-weight",keyword) == 0)
3350
2.26k
              {
3351
2.26k
                if (MagickGetToken(q,&q,token,token_max_length) < 1)
3352
4
                  {
3353
4
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3354
4
                    status=MagickFail;
3355
4
                    break;
3356
4
                  }
3357
2.26k
                if (LocaleCompare(token,"all") == 0)
3358
10
                  graphic_context[n]->weight=0;
3359
2.25k
                else if (LocaleCompare(token,"bold") == 0)
3360
74
                  graphic_context[n]->weight=700;
3361
2.17k
                else if (LocaleCompare(token,"bolder") == 0)
3362
0
                  {
3363
0
                    if (graphic_context[n]->weight <= 800)
3364
0
                      graphic_context[n]->weight+=100;
3365
0
                  }
3366
2.17k
                else if (LocaleCompare(token,"lighter") == 0)
3367
0
                  {
3368
0
                    if (graphic_context[n]->weight >= 100)
3369
0
                      graphic_context[n]->weight-=100;
3370
0
                  }
3371
2.17k
                else if (LocaleCompare(token,"normal") == 0)
3372
376
                  graphic_context[n]->weight=400;
3373
1.80k
                else if ((status &= MagickAtoULChk(token,&graphic_context[n]->weight)) == MagickFail)
3374
23
                  {
3375
23
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3376
23
                  }
3377
2.26k
                break;
3378
2.26k
              }
3379
105
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3380
105
            status=MagickFail;
3381
105
            break;
3382
2.36k
          }
3383
1.45k
        case 'g':
3384
2.95k
        case 'G':
3385
2.95k
          {
3386
2.95k
            if (LocaleCompare("gradient-units",keyword) == 0)
3387
977
              {
3388
977
                MagickGetToken(q,&q,token,token_max_length);
3389
                /* FIXME: Not implemented! */
3390
977
                break;
3391
977
              }
3392
1.97k
            if (LocaleCompare("gravity",keyword) == 0)
3393
1.92k
              {
3394
1.92k
                MagickGetToken(q,&q,token,token_max_length);
3395
1.92k
                graphic_context[n]->gravity=StringToGravityType(token);
3396
1.92k
                if (ForgetGravity != graphic_context[n]->gravity)
3397
1.89k
                  break;
3398
32
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3399
32
                status=MagickFail;
3400
32
                break;
3401
1.92k
              }
3402
45
            status=MagickFail;
3403
45
            break;
3404
1.97k
          }
3405
33.0k
        case 'i':
3406
33.2k
        case 'I':
3407
33.2k
          {
3408
33.2k
            if (LocaleCompare("image",keyword) == 0)
3409
33.1k
              {
3410
33.1k
                primitive_type=ImagePrimitive;
3411
33.1k
                MagickGetToken(q,&q,token,token_max_length);
3412
33.1k
                graphic_context[n]->compose=StringToCompositeOperator(token);
3413
33.1k
                if (UndefinedCompositeOp == graphic_context[n]->compose)
3414
78
                  {
3415
78
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3416
78
                    status=MagickFail;
3417
78
                  }
3418
33.1k
                break;
3419
33.1k
              }
3420
87
            status=MagickFail;
3421
87
            break;
3422
33.2k
          }
3423
36.0k
        case 'l':
3424
36.0k
        case 'L':
3425
36.0k
          {
3426
36.0k
            if (LocaleCompare("line",keyword) == 0)
3427
35.9k
              {
3428
35.9k
                primitive_type=LinePrimitive;
3429
35.9k
                break;
3430
35.9k
              }
3431
53
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3432
53
            status=MagickFail;
3433
53
            break;
3434
36.0k
          }
3435
42.6k
        case 'm':
3436
43.2k
        case 'M':
3437
43.2k
          {
3438
43.2k
            if (LocaleCompare("mask",keyword) == 0)   /* added mask */
3439
37.5k
              {
3440
                /*
3441
                  Create mask.
3442
                */
3443
37.5k
                if (MagickGetToken(q,&q,token,token_max_length) < 1)
3444
1
                  {
3445
1
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3446
1
                    status=MagickFail;
3447
1
                    break;
3448
1
                  }
3449
37.5k
                if ((status &= CloneString(&graphic_context[n]->extra->composite_path,token)) == MagickFail)
3450
0
                  {
3451
0
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3452
0
                    break;
3453
0
                  }
3454
37.5k
                if ((status &= DrawCompositeMask(image,graphic_context[n],
3455
37.5k
                                                 graphic_context[n]->extra->composite_path)) == MagickFail)
3456
29.9k
                  {
3457
29.9k
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3458
29.9k
                  }
3459
37.5k
                break;
3460
37.5k
              }
3461
5.62k
            if (LocaleCompare("matte",keyword) == 0)
3462
5.57k
              {
3463
5.57k
                primitive_type=MattePrimitive;
3464
5.57k
                break;
3465
5.57k
              }
3466
48
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3467
48
            status=MagickFail;
3468
48
            break;
3469
5.62k
          }
3470
5.95k
        case 'o':
3471
6.77k
        case 'O':
3472
6.77k
          {
3473
6.77k
            if (LocaleCompare("offset",keyword) == 0)
3474
664
              {
3475
664
                MagickGetToken(q,&q,token,token_max_length);
3476
664
                break;
3477
664
              }
3478
6.11k
            if (LocaleCompare("opacity",keyword) == 0)
3479
6.04k
              {
3480
6.04k
                double opacity,opacityGroupOld,opacityGroupNew;
3481
6.04k
                MagickGetToken(q,&q,token,token_max_length);
3482
6.04k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
3483
180
                  break;    /* if drawing clip path, ignore changes to group/global opacity */
3484
5.86k
                factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
3485
5.86k
                if ((status &= MagickAtoFChk(token,&opacity)) == MagickFail)
3486
8
                  {
3487
8
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3488
8
                    break;
3489
8
                  }
3490
5.85k
                opacity *= factor;
3491
5.85k
                if  ( opacity <= 0.0 )  /* per SVG spec */
3492
1.16k
                  opacity = 0.0;
3493
4.69k
                else if ( opacity > 1.0 )
3494
1.44k
                  opacity = 1.0;
3495
5.85k
                opacityGroupOld = MaxRGB - graphic_context[n]->opacity; /* MaxRGB==opaque */
3496
5.85k
                opacityGroupNew = opacityGroupOld * opacity; /* MaxRGB==opaque */
3497
5.85k
                graphic_context[n]->opacity = MaxRGB - (Quantum)(opacityGroupNew + /*round*/0.5);
3498
5.85k
                if (graphic_context[n]->fill.opacity != TransparentOpacity)
3499
4.48k
                  {/*fill color != 'none'*/
3500
3501
4.48k
                    if  ( opacityGroupOld == 0.0 )  /* can't back out old group opacity */
3502
99
                      graphic_context[n]->fill.opacity = graphic_context[n]->opacity; /* reasonable alternative */
3503
4.39k
                    else
3504
4.39k
                      {
3505
                        /* back out old group opacity value, include new group opacity value */
3506
4.39k
                        double opacityFill = MaxRGB - graphic_context[n]->fill.opacity; /* MaxRGB==opaque */
3507
4.39k
                        opacityFill = opacityFill * (opacityGroupNew / opacityGroupOld);
3508
4.39k
                        graphic_context[n]->fill.opacity =
3509
4.39k
                          (opacityFill < MaxRGBDouble) ? MaxRGB - (Quantum)(opacityFill + /*round*/0.5) : 0;
3510
4.39k
                      }
3511
3512
4.48k
                  }/*fill color != 'none'*/
3513
3514
5.85k
                if (graphic_context[n]->stroke.opacity != TransparentOpacity)
3515
2.47k
                  {/*stroke color != 'none'*/
3516
3517
2.47k
                    if  ( opacityGroupOld == 0.0 ) /* can't back out old group opacity */
3518
0
                      graphic_context[n]->stroke.opacity = graphic_context[n]->opacity; /* reasonable alternative */
3519
2.47k
                    else
3520
2.47k
                      {
3521
                        /* back out old group opacity value, include new group opacity value */
3522
2.47k
                        double opacityStroke = MaxRGB - graphic_context[n]->stroke.opacity; /* MaxRGB==opaque */
3523
2.47k
                        opacityStroke = opacityStroke * (opacityGroupNew / opacityGroupOld);
3524
2.47k
                        graphic_context[n]->stroke.opacity =
3525
2.47k
                          (opacityStroke < MaxRGBDouble) ? MaxRGB - (Quantum)(opacityStroke + /*round*/0.5) : 0;
3526
2.47k
                      }
3527
3528
2.47k
                  }/*stroke color != 'none'*/
3529
5.85k
                break;
3530
5.86k
              }
3531
72
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3532
72
            status=MagickFail;
3533
72
            break;
3534
6.11k
          }
3535
587k
        case 'p':
3536
588k
        case 'P':
3537
588k
          {
3538
588k
            if (LocaleCompare("path",keyword) == 0)
3539
71.9k
              {
3540
71.9k
                primitive_type=PathPrimitive;
3541
71.9k
                break;
3542
71.9k
              }
3543
516k
            if (LocaleCompare("point",keyword) == 0)
3544
2.78k
              {
3545
2.78k
                primitive_type=PointPrimitive;
3546
2.78k
                break;
3547
2.78k
              }
3548
513k
            if (LocaleCompare("polyline",keyword) == 0)
3549
161
              {
3550
161
                primitive_type=PolylinePrimitive;
3551
161
                break;
3552
161
              }
3553
513k
            if (LocaleCompare("polygon",keyword) == 0)
3554
280
              {
3555
280
                primitive_type=PolygonPrimitive;
3556
280
                break;
3557
280
              }
3558
513k
            if (LocaleCompare("pop",keyword) == 0)
3559
165k
              {
3560
165k
                MagickGetToken(q,&q,token,token_max_length);
3561
165k
                if (LocaleCompare("class",token) == 0)  /* added "pop class" to support "defs" */
3562
216
                  break;
3563
165k
                if (LocaleCompare("clip-path",token) == 0)
3564
855
                  break;
3565
164k
                if (LocaleCompare("defs",token) == 0)
3566
5.03k
                  {
3567
                    /* do not render graphic elements if inside <defs> ... </defs> */
3568
5.03k
                    defsPushCount--;
3569
5.03k
                    graphic_context[n]->render = (defsPushCount > 0) ? 0 : 1;
3570
5.03k
                    break;
3571
5.03k
                  }
3572
159k
                if (LocaleCompare("gradient",token) == 0)
3573
1.14k
                  break;
3574
158k
                if (LocaleCompare("graphic-context",token) == 0)
3575
146k
                  {
3576
146k
                    if (n <= 0)
3577
7
                      {
3578
7
                        ThrowException(&image->exception,DrawError,UnbalancedGraphicContextPushPop,token);
3579
7
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3580
7
                        status=MagickFail;
3581
7
                        break;
3582
7
                      }
3583
146k
                    if (graphic_context[n]->extra->clip_path != (char *) NULL)
3584
10.6k
                      if (LocaleCompare(graphic_context[n]->extra->clip_path,
3585
10.6k
                                        graphic_context[n-1]->extra->clip_path) != 0)
3586
1.98k
                        (void) SetImageClipMask(image,(Image *) NULL);
3587
146k
                    if (graphic_context[n]->extra->composite_path != (char *) NULL)
3588
1.82k
                      {
3589
                        /* clean up composite mask if different from parent */
3590
1.82k
                        if (LocaleCompare(graphic_context[n]->extra->composite_path,
3591
1.82k
                                          graphic_context[n-1]->extra->composite_path) != 0)
3592
384
                          (void) SetImageCompositeMask(image,(Image *) NULL);
3593
1.82k
                      }
3594
146k
                    DestroyDrawInfo(graphic_context[n]);
3595
146k
                    n--;
3596
146k
                    FillOpacityPending = StrokeOpacityPending = MagickFalse;
3597
146k
                    break;
3598
146k
                  }
3599
12.1k
                if (LocaleCompare("id",token) == 0)   /* added "pop id" (to support "defs") */
3600
94
                  break;
3601
12.0k
                if (LocaleCompare("mask",token) == 0)   /* added mask */
3602
4.94k
                  break;
3603
7.06k
                if (LocaleCompare("pattern",token) == 0)
3604
6.99k
                  break;
3605
72
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3606
72
                status=MagickFail;
3607
72
                break;
3608
7.06k
              }
3609
347k
            if (LocaleCompare("push",keyword) == 0)
3610
347k
              {
3611
347k
                MagickGetToken(q,&q,token,token_max_length);
3612
347k
                if (LocaleCompare("class",token) == 0)  /* added "push class" to support "defs" */
3613
6.78k
                  {
3614
6.78k
                    char *nq = ExtractTokensBetweenPushPop(q,token,token_max_length,"class",image,0);
3615
6.78k
                    if (nq != NULL)
3616
6.78k
                      q=nq;
3617
2
                    else
3618
2
                      {
3619
2
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3620
2
                        status=MagickFail;
3621
2
                      }
3622
6.78k
                    break;
3623
6.78k
                  }
3624
340k
                if (LocaleCompare("clip-path",token) == 0)
3625
3.23k
                  {
3626
                    /*
3627
                      Code that extracted tokens between push/pop clip-path has been refactored
3628
                      into new function ExtractTokensBetweenPushPop().
3629
                    */
3630
3.23k
                    size_t ExtractedLength;
3631
3.23k
                    char *nq = ExtractTokensBetweenPushPop(q,token,token_max_length,"clip-path",image,&ExtractedLength); /* NULLED */
3632
3.23k
                    if  ( (ExtractedLength == 0) || (nq == NULL) )
3633
87
                      {
3634
87
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3635
87
                        status=MagickFail;
3636
87
                      }
3637
3.15k
                    else
3638
3.15k
                      q=nq;
3639
3.23k
                    break;
3640
3.23k
                  }
3641
337k
                if (LocaleCompare("gradient",token) == 0)
3642
6.03k
                  {
3643
6.03k
                    char
3644
6.03k
                      key[2*MaxTextExtent],
3645
6.03k
                      name[MaxTextExtent],
3646
6.03k
                      type[MaxTextExtent];
3647
3648
6.03k
                    SegmentInfo
3649
6.03k
                      segment;
3650
3651
6.03k
                    double
3652
6.03k
                      gradient_width,
3653
6.03k
                      gradient_height;
3654
3655
6.03k
                    MagickGetToken(q,&q,token,token_max_length);
3656
6.03k
                    (void) strlcpy(name,token,MaxTextExtent);
3657
6.03k
                    MagickGetToken(q,&q,token,token_max_length);
3658
6.03k
                    (void) strlcpy(type,token,MaxTextExtent);
3659
6.03k
                    MagickGetToken(q,&q,token,token_max_length);
3660
6.03k
                    if ((status &= MagickAtoFChk(token,&segment.x1)) == MagickFail)
3661
4
                      {
3662
4
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3663
4
                        break;
3664
4
                      }
3665
6.02k
                    MagickGetToken(q,&q,token,token_max_length);
3666
3667
6.02k
                    if (*token == ',')
3668
2.92k
                      MagickGetToken(q,&q,token,token_max_length);
3669
6.02k
                    if ((status &= MagickAtoFChk(token,&segment.y1)) == MagickFail)
3670
6
                      {
3671
6
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3672
6
                        break;
3673
6
                      }
3674
6.02k
                    MagickGetToken(q,&q,token,token_max_length);
3675
3676
6.02k
                    if (*token == ',')
3677
274
                      MagickGetToken(q,&q,token,token_max_length);
3678
6.02k
                    if ((status &= MagickAtoFChk(token,&segment.x2)) == MagickFail)
3679
9
                      {
3680
9
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3681
9
                        break;
3682
9
                      }
3683
6.01k
                    MagickGetToken(q,&q,token,token_max_length);
3684
3685
6.01k
                    if (*token == ',')
3686
3.18k
                      MagickGetToken(q,&q,token,token_max_length);
3687
6.01k
                    if ((status &= MagickAtoFChk(token,&segment.y2)) == MagickFail)
3688
6
                      {
3689
6
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3690
6
                        break;
3691
6
                      }
3692
6.00k
                    if (LocaleCompare(type,"radial") == 0)
3693
830
                      {
3694
830
                        MagickGetToken(q,&q,token,token_max_length);
3695
830
                        if (*token == ',')
3696
210
                          MagickGetToken(q,&q,token,token_max_length);
3697
830
                      }
3698
200k
                    for (p=q; *q != '\0'; )
3699
199k
                      {
3700
199k
                        MagickGetToken(q,&q,token,token_max_length);
3701
199k
                        if (LocaleCompare(token,"pop") != 0)
3702
191k
                          continue;
3703
7.17k
                        MagickGetToken(q,(char **) NULL,token,token_max_length);
3704
7.17k
                        if (LocaleCompare(token,"gradient") != 0)
3705
2.33k
                          continue;
3706
4.84k
                        break;
3707
7.17k
                      }
3708
6.00k
                    if ((q == NULL) || (p == NULL) || (q-4 < p))
3709
20
                      {
3710
20
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3711
20
                        status=MagickFail;
3712
20
                        break;
3713
20
                      }
3714
5.98k
                    (void) strncpy(token,p,q-p-4);
3715
5.98k
                    token[q-p-4]='\0';
3716
5.98k
                    bounds.x1=graphic_context[n]->affine.sx*segment.x1+
3717
5.98k
                      graphic_context[n]->affine.ry*segment.y1+
3718
5.98k
                      graphic_context[n]->affine.tx;
3719
5.98k
                    bounds.y1=graphic_context[n]->affine.rx*segment.x1+
3720
5.98k
                      graphic_context[n]->affine.sy*segment.y1+
3721
5.98k
                      graphic_context[n]->affine.ty;
3722
5.98k
                    bounds.x2=graphic_context[n]->affine.sx*segment.x2+
3723
5.98k
                      graphic_context[n]->affine.ry*segment.y2+
3724
5.98k
                      graphic_context[n]->affine.tx;
3725
5.98k
                    bounds.y2=graphic_context[n]->affine.rx*segment.x2+
3726
5.98k
                      graphic_context[n]->affine.sy*segment.y2+
3727
5.98k
                      graphic_context[n]->affine.ty;
3728
                    /*
3729
                      Validate gradient image size
3730
                    */
3731
5.98k
                    gradient_width=Max(AbsoluteValue(bounds.x2-bounds.x1+1),1);
3732
5.98k
                    gradient_height=Max(AbsoluteValue(bounds.y2-bounds.y1+1),1);
3733
5.98k
                    if ((status &= AcquireMagickResource(WidthResource,gradient_width)) == MagickFail)
3734
5
                      {
3735
5
                        char resource_str[MaxTextExtent];
3736
5
                        const magick_int64_t width_resource_limit = GetMagickResourceLimit(WidthResource);
3737
5
                        MagickFormatString(resource_str,sizeof(resource_str),
3738
5
                                           "%" MAGICK_INTMAX_F "d", (magick_intmax_t) width_resource_limit);
3739
5
                        ThrowException(&image->exception,ResourceLimitError,
3740
5
                                       ImagePixelWidthLimitExceeded,resource_str);
3741
5
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3742
5
                        break;
3743
5
                      }
3744
5.98k
                    if ((status &= AcquireMagickResource(HeightResource,gradient_height)) == MagickFail)
3745
9
                      {
3746
9
                        char resource_str[MaxTextExtent];
3747
9
                        const magick_int64_t hight_resource_limit = GetMagickResourceLimit(HeightResource);
3748
9
                        MagickFormatString(resource_str,sizeof(resource_str),
3749
9
                                           "%" MAGICK_INTMAX_F "d", (magick_intmax_t) hight_resource_limit);
3750
9
                        ThrowException(&image->exception,ResourceLimitError,
3751
9
                                       ImagePixelHeightLimitExceeded,resource_str);
3752
9
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3753
9
                        break;
3754
9
                      }
3755
5.97k
                    if ((status &= AcquireMagickResource(PixelsResource,(magick_uint64_t) gradient_width*gradient_height))
3756
5.97k
                        == MagickFail)
3757
0
                      {
3758
0
                        char resource_str[MaxTextExtent];
3759
0
                        const magick_int64_t pixels_resource_limit = GetMagickResourceLimit(PixelsResource);
3760
0
                        MagickFormatString(resource_str,sizeof(resource_str),
3761
0
                                           "%" MAGICK_INTMAX_F "d", (magick_intmax_t) pixels_resource_limit);
3762
0
                        ThrowException(&image->exception,ResourceLimitError,
3763
0
                                       ImagePixelLimitExceeded,resource_str);
3764
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3765
0
                        break;
3766
0
                      }
3767
                    /*
3768
                      Apply an arbitrary limit to gradient size requests
3769
                      since gradient images can take a lot of memory.
3770
                      Some tiny SVGs request huge gradients.  This is here
3771
                      to avoid denial of service.
3772
                    */
3773
5.97k
                    if (gradient_width*gradient_height > (double) 5000*5000 /*10000*10000*/)
3774
0
                      {
3775
0
                        char gradient_size_str[MaxTextExtent];
3776
0
                        MagickFormatString(gradient_size_str,sizeof(gradient_size_str),"%gx%g",
3777
0
                                           gradient_width,gradient_height);
3778
0
                        ThrowException(&image->exception,DrawError,
3779
0
                                       UnreasonableGradientSize,gradient_size_str);
3780
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3781
0
                        status=MagickFail;
3782
0
                        break;
3783
0
                      }
3784
5.97k
                    MagickFormatString(key,sizeof(key),"[MVG:%.1024s]",name);
3785
                    /* SetImageAttribute concatenates values! Delete with NULL */
3786
5.97k
                    (void) SetImageAttribute(image,key,NULL);
3787
5.97k
                    (void) SetImageAttribute(image,key,token);
3788
5.97k
                    MagickFormatString(key,sizeof(key),"[MVG:%.1024s-geometry]",name);
3789
5.97k
                    MagickFormatString(geometry,sizeof(geometry),"%gx%g%+g%+g",
3790
5.97k
                                       Max(AbsoluteValue(bounds.x2-bounds.x1+1),1),
3791
5.97k
                                       Max(AbsoluteValue(bounds.y2-bounds.y1+1),1),
3792
5.97k
                                       bounds.x1,bounds.y1);
3793
                    /* SetImageAttribute concatenates values! Delete with NULL */
3794
5.97k
                    (void) SetImageAttribute(image,key,NULL);
3795
5.97k
                    (void) SetImageAttribute(image,key,geometry);
3796
5.97k
                    MagickGetToken(q,&q,token,token_max_length);
3797
5.97k
                    break;
3798
5.97k
                  }
3799
331k
                if (LocaleCompare("id",token) == 0)   /* added "push id" (to support "defs") */
3800
542
                  {
3801
542
                    if  ( defsPushCount > 0 )
3802
434
                      {
3803
434
                        char *nq = ExtractTokensBetweenPushPop(q,token,token_max_length,"id",image,0);
3804
434
                        if (nq == NULL)
3805
0
                          {
3806
0
                            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3807
0
                            status=MagickFail;
3808
0
                          }
3809
434
                        else
3810
434
                          q=nq;
3811
434
                        break;
3812
434
                      }
3813
108
                    else    /* extract <identifier> from "push id <identifier>" */
3814
108
                      {
3815
108
                        MagickGetToken(q,&q,token,token_max_length);
3816
108
                      }
3817
108
                    break;
3818
542
                  }
3819
330k
                if (LocaleCompare("mask",token) == 0)   /* added mask */
3820
2.72k
                  {
3821
2.72k
                    size_t ExtractedLength;
3822
2.72k
                    char *nq = ExtractTokensBetweenPushPop(q,token,token_max_length,"mask",image,&ExtractedLength);
3823
2.72k
                    if  ( (ExtractedLength == 0) || (nq == NULL) )
3824
33
                      {
3825
33
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3826
33
                        status=MagickFail;
3827
33
                      }
3828
2.68k
                    else
3829
2.68k
                      q=nq;
3830
2.72k
                    break;
3831
2.72k
                  }
3832
327k
                if (LocaleCompare("pattern",token) == 0)
3833
31.3k
                  {
3834
31.3k
                    double
3835
31.3k
                      ordinate,
3836
31.3k
                      dval;
3837
3838
31.3k
                    RectangleInfo
3839
31.3k
                      bounds;
3840
3841
31.3k
                    MagickGetToken(q,&q,token,token_max_length);
3842
31.3k
                    (void) strlcpy(name,token,MaxTextExtent);
3843
31.3k
                    MagickGetToken(q,&q,token,token_max_length);
3844
31.3k
                    if ((status &= MagickAtoFChk(token,&ordinate)) == MagickFail)
3845
3
                      {
3846
3
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3847
3
                        break;
3848
3
                      }
3849
31.3k
                    dval=ceil(ordinate-0.5);
3850
31.3k
                    if ((dval < DTOLONG_MIN) || (dval > DTOLONG_MAX) ||
3851
31.3k
                        ((status &= AcquireMagickResource(WidthResource,AbsoluteValue((magick_int64_t) dval))) == MagickFail))
3852
117
                      {
3853
117
                        char resource_str[MaxTextExtent];
3854
117
                        MagickFormatString(resource_str,sizeof(resource_str),"pattern x ordinate (%g)", ordinate);
3855
117
                        ThrowException(&image->exception,ResourceLimitError,
3856
117
                                       ImagePixelWidthLimitExceeded,resource_str);
3857
117
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3858
117
                        break;
3859
117
                      }
3860
31.2k
                    bounds.x=(long) dval;
3861
31.2k
                    MagickGetToken(q,&q,token,token_max_length);
3862
31.2k
                    if (*token == ',')
3863
8.73k
                      MagickGetToken(q,&q,token,token_max_length);
3864
31.2k
                    if ((status &= MagickAtoFChk(token,&ordinate)) == MagickFail)
3865
13
                      {
3866
13
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3867
13
                        break;
3868
13
                      }
3869
31.2k
                    dval=ceil(ordinate-0.5);
3870
31.2k
                    if ((dval < DTOLONG_MIN) || (dval > DTOLONG_MAX) ||
3871
31.2k
                        ((status &= AcquireMagickResource(WidthResource,AbsoluteValue((magick_int64_t) dval))) == MagickFail))
3872
122
                      {
3873
122
                        char resource_str[MaxTextExtent];
3874
122
                        MagickFormatString(resource_str,sizeof(resource_str),"pattern x ordinate (%g)", ordinate);
3875
122
                        ThrowException(&image->exception,ResourceLimitError,
3876
122
                                       ImagePixelHeightLimitExceeded,resource_str);
3877
122
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3878
122
                        break;
3879
122
                      }
3880
31.1k
                    bounds.y=(long) dval;
3881
31.1k
                    MagickGetToken(q,&q,token,token_max_length);
3882
31.1k
                    if (*token == ',')
3883
386
                      MagickGetToken(q,&q,token,token_max_length);
3884
31.1k
                    if ((status &= MagickAtoFChk(token,&ordinate)) == MagickFail)
3885
19
                      {
3886
19
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3887
19
                        break;
3888
19
                      }
3889
31.0k
                    if (ordinate < 0.0)
3890
3
                      {
3891
3
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3892
3
                        status=MagickFail;
3893
3
                        break;
3894
3
                      }
3895
31.0k
                    dval=floor(ordinate+0.5);
3896
31.0k
                    if ((dval > DTOLONG_MAX) ||
3897
31.0k
                        ((status &= AcquireMagickResource(WidthResource,(magick_int64_t) dval)) == MagickFail))
3898
11
                      {
3899
11
                        char resource_str[MaxTextExtent];
3900
11
                        MagickFormatString(resource_str,sizeof(resource_str),"pattern width (%g)", ordinate);
3901
11
                        ThrowException(&image->exception,ResourceLimitError,
3902
11
                                       ImagePixelWidthLimitExceeded,resource_str);
3903
11
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3904
11
                        status=MagickFail;
3905
11
                        break;
3906
11
                      }
3907
31.0k
                    bounds.width=(unsigned long) dval;
3908
31.0k
                    MagickGetToken(q,&q,token,token_max_length);
3909
31.0k
                    if (*token == ',')
3910
8.91k
                      MagickGetToken(q,&q,token,token_max_length);
3911
31.0k
                    if ((status &= MagickAtoFChk(token,&ordinate)) == MagickFail)
3912
8
                      {
3913
8
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3914
8
                        break;
3915
8
                      }
3916
31.0k
                    if (ordinate < 0.0)
3917
1
                      {
3918
1
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3919
1
                        status=MagickFail;
3920
1
                        break;
3921
1
                      }
3922
31.0k
                    dval=floor(ordinate+0.5);
3923
31.0k
                    if ((dval > DTOLONG_MAX) ||
3924
31.0k
                        ((status &= AcquireMagickResource(WidthResource,(magick_int64_t) dval)) == MagickFail))
3925
10
                      {
3926
10
                        char resource_str[MaxTextExtent];
3927
10
                        MagickFormatString(resource_str,sizeof(resource_str),"pattern height (%g)", ordinate);
3928
10
                        ThrowException(&image->exception,ResourceLimitError,
3929
10
                                       ImagePixelHeightLimitExceeded,resource_str);
3930
10
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3931
10
                        status=MagickFail;
3932
10
                        break;
3933
10
                      }
3934
31.0k
                    bounds.height=(unsigned long) dval;
3935
31.0k
                    if ((status &= AcquireMagickResource(PixelsResource,(magick_uint64_t) bounds.width*bounds.height))
3936
31.0k
                        == MagickFail)
3937
0
                      {
3938
0
                        char resource_str[MaxTextExtent];
3939
0
                        MagickFormatString(resource_str,sizeof(resource_str),
3940
0
                                           "pattern dimensions %lux%lu", bounds.width,bounds.height);
3941
0
                        ThrowException(&image->exception,ResourceLimitError,
3942
0
                                       ImagePixelLimitExceeded,resource_str);
3943
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3944
0
                        break;
3945
0
                      }
3946
31.0k
                    if ((bounds.width == 0) || (bounds.height == 0))
3947
14
                      {
3948
14
                        char resource_str[MaxTextExtent];
3949
14
                        MagickFormatString(resource_str,sizeof(resource_str),
3950
14
                                           "pattern dimensions %lux%lu", bounds.width,bounds.height);
3951
14
                        ThrowException(&image->exception,DrawError,
3952
14
                                       InvalidPrimitiveArgument,resource_str);
3953
14
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3954
14
                        status=MagickFail;
3955
14
                        break;
3956
14
                      }
3957
961k
                    for (p=q; *q != '\0'; )
3958
939k
                      {
3959
939k
                        MagickGetToken(q,&q,token,token_max_length);
3960
939k
                        if (LocaleCompare(token,"pop") != 0)
3961
918k
                          continue;
3962
20.6k
                        MagickGetToken(q,(char **) NULL,token,token_max_length);
3963
20.6k
                        if (LocaleCompare(token,"pattern") != 0)
3964
11.8k
                          continue;
3965
8.77k
                        break;
3966
20.6k
                      }
3967
31.0k
                    if ((q == NULL) || (p == NULL) || (q-4 < p))
3968
48
                      {
3969
48
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
3970
48
                        status=MagickFail;
3971
48
                        break;
3972
48
                      }
3973
31.0k
                    (void) strncpy(token,p,q-p-4);
3974
31.0k
                    token[q-p-4]='\0';
3975
31.0k
                    MagickFormatString(key,sizeof(key),"[MVG:%.1024s]",name);
3976
                    /* SetImageAttribute concatenates values! Delete with NULL */
3977
31.0k
                    (void) SetImageAttribute(image,key,NULL);
3978
31.0k
                    (void) SetImageAttribute(image,key,token);
3979
31.0k
                    MagickFormatString(key,sizeof(key),"[MVG:%.1024s-geometry]",name);
3980
31.0k
                    MagickFormatString(geometry,sizeof(geometry),"%lux%lu%+ld%+ld",bounds.width,
3981
31.0k
                                       bounds.height,bounds.x,bounds.y);
3982
                    /* SetImageAttribute concatenates values! Delete with NULL */
3983
31.0k
                    (void) SetImageAttribute(image,key,NULL);
3984
31.0k
                    (void) SetImageAttribute(image,key,geometry);
3985
31.0k
                    MagickGetToken(q,&q,token,token_max_length);
3986
31.0k
                    break;
3987
31.0k
                  }
3988
296k
                if (LocaleCompare("graphic-context",token) == 0)
3989
285k
                  {
3990
285k
                    DrawInfo** new_graphic_context;
3991
285k
                    n++; /* Yes, this increment before alloc must be here! */
3992
285k
                    new_graphic_context=
3993
285k
                      MagickReallocateResourceLimitedClearedArray(DrawInfo**,graphic_context,
3994
285k
                                                                  ((size_t) n+1),sizeof(DrawInfo *));
3995
285k
                    if (new_graphic_context == (DrawInfo **) NULL)
3996
0
                      {
3997
0
                        ThrowException3(&image->exception,ResourceLimitError,
3998
0
                                        MemoryAllocationFailed,UnableToDrawOnImage);
3999
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4000
0
                        status=MagickFail;
4001
0
                        break;
4002
0
                      }
4003
285k
                    graphic_context=new_graphic_context;
4004
285k
                    graphic_context[n]=
4005
285k
                      CloneDrawInfo((ImageInfo *) NULL,graphic_context[n-1]);
4006
285k
                    if (graphic_context[n] == (DrawInfo *) NULL)
4007
0
                      {
4008
0
                        ThrowException3(&image->exception,ResourceLimitError,
4009
0
                                        MemoryAllocationFailed,UnableToDrawOnImage);
4010
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4011
0
                        status=MagickFail;
4012
0
                        break;
4013
0
                      }
4014
285k
                    FillOpacityPending = StrokeOpacityPending = MagickFalse;
4015
285k
                    break;
4016
285k
                  }
4017
11.0k
                if (LocaleCompare("defs",token) == 0)
4018
11.0k
                  {
4019
                    /* do not render graphic elements if inside <defs> ... </defs> */
4020
11.0k
                    defsPushCount++;
4021
11.0k
                    graphic_context[n]->render = (defsPushCount > 0) ? 0 : 1;
4022
11.0k
                    break;
4023
11.0k
                  }
4024
48
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4025
48
                status=MagickFail;
4026
48
                break;
4027
11.0k
              }
4028
136
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4029
136
            status=MagickFail;
4030
136
            break;
4031
347k
          }
4032
36.1k
        case 'r':
4033
36.2k
        case 'R':
4034
36.2k
          {
4035
36.2k
            if (LocaleCompare("rectangle",keyword) == 0)
4036
22.4k
              {
4037
22.4k
                primitive_type=RectanglePrimitive;
4038
22.4k
                break;
4039
22.4k
              }
4040
13.8k
            if (LocaleCompare("rotate",keyword) == 0)
4041
5.33k
              {
4042
5.33k
                MagickGetToken(q,&q,token,token_max_length);
4043
5.33k
                if ((status &= MagickAtoFChk(token,&angle)) == MagickFail)
4044
3
                  {
4045
3
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4046
3
                    break;
4047
3
                  }
4048
5.32k
                affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
4049
5.32k
                affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
4050
5.32k
                affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
4051
5.32k
                affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
4052
5.32k
                break;
4053
5.33k
              }
4054
8.53k
            if (LocaleCompare("roundRectangle",keyword) == 0)
4055
8.45k
              {
4056
8.45k
                primitive_type=RoundRectanglePrimitive;
4057
8.45k
                break;
4058
8.45k
              }
4059
80
            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4060
80
            status=MagickFail;
4061
80
            break;
4062
8.53k
          }
4063
429k
        case 's':
4064
431k
        case 'S':
4065
431k
          {
4066
431k
            if (LocaleCompare("scale",keyword) == 0)
4067
13.3k
              {
4068
13.3k
                MagickGetToken(q,&q,token,token_max_length);
4069
13.3k
                if ((status &= MagickAtoFChk(token,&affine.sx)) == MagickFail)
4070
7
                  {
4071
7
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4072
7
                    break;
4073
7
                  }
4074
13.3k
                MagickGetToken(q,&q,token,token_max_length);
4075
13.3k
                if (*token == ',')
4076
5.07k
                  MagickGetToken(q,&q,token,token_max_length);
4077
13.3k
                if ((status &= MagickAtoFChk(token,&affine.sy)) == MagickFail)
4078
13
                  {
4079
13
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4080
13
                  }
4081
13.3k
                break;
4082
13.3k
              }
4083
418k
            if (LocaleCompare("skewX",keyword) == 0)
4084
378
              {
4085
378
                MagickGetToken(q,&q,token,token_max_length);
4086
378
                if ((status &= MagickAtoFChk(token,&angle)) == MagickFail)
4087
4
                  {
4088
4
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4089
4
                    break;
4090
4
                  }
4091
374
                affine.ry=tan(DegreesToRadians(fmod(angle,360.0)));
4092
374
                break;
4093
378
              }
4094
418k
            if (LocaleCompare("skewY",keyword) == 0)
4095
141
              {
4096
141
                MagickGetToken(q,&q,token,token_max_length);
4097
141
                if ((status &= MagickAtoFChk(token,&angle)) == MagickFail)
4098
3
                  {
4099
3
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4100
3
                    break;
4101
3
                  }
4102
138
                affine.rx=tan(DegreesToRadians(fmod(angle,360.0)));
4103
138
                break;
4104
141
              }
4105
417k
            if (LocaleCompare("stop-color",keyword) == 0)
4106
67.9k
              {
4107
67.9k
                PixelPacket
4108
67.9k
                  stop_color;
4109
4110
67.9k
                MagickGetToken(q,&q,token,token_max_length);
4111
67.9k
                if ((status &= QueryColorDatabase(token,&stop_color,&image->exception)) == MagickFail)
4112
52
                  {
4113
52
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4114
52
                    break;
4115
52
                  }
4116
67.9k
                (void) GradientImage(image,&start_color,&stop_color);
4117
67.9k
                start_color=stop_color;
4118
67.9k
                MagickGetToken(q,&q,token,token_max_length);
4119
67.9k
                break;
4120
67.9k
              }
4121
349k
            if (LocaleCompare("stroke",keyword) == 0)
4122
129k
              {
4123
129k
                MagickGetToken(q,&q,token,token_max_length);
4124
129k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
4125
2.47k
                  break;    /* if drawing clip path, ignore changes to stroke color */
4126
127k
                MagickFormatString(pattern,sizeof(pattern),"[MVG:%.1024s]",token);
4127
127k
                if (GetImageAttribute(image,pattern) != (ImageAttribute *) NULL)
4128
8.22k
                  {
4129
8.22k
                    if ((status &= DrawPatternPath(image,draw_info,token,
4130
8.22k
                                                   &graphic_context[n]->stroke_pattern)) == MagickFail)
4131
11
                      {
4132
11
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4133
11
                        break;
4134
11
                      }
4135
8.22k
                  }
4136
119k
                else
4137
119k
                  {/*stroke color, not pattern*/
4138
4139
                    /* when setting new stroke color, try to preserve stroke-opacity */
4140
119k
                    Quantum StrokeOpacityOld = graphic_context[n]->stroke.opacity;
4141
119k
                    if ((status &= QueryColorDatabase(token,&graphic_context[n]->stroke,&image->exception)) == MagickFail)
4142
42
                      {
4143
42
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4144
42
                        break;
4145
42
                      }
4146
119k
                    if (graphic_context[n]->stroke.opacity != TransparentOpacity)
4147
73.9k
                      {/*stroke color != 'none'*/
4148
4149
73.9k
                        if  ( StrokeOpacityOld != TransparentOpacity )
4150
41.0k
                          graphic_context[n]->stroke.opacity = StrokeOpacityOld;  /* already has group opacity included */
4151
32.8k
                        else  /* combine stroke color's opacity with group opacity per SVG spec */
4152
32.8k
                          {
4153
32.8k
                            Quantum StrokeOpacity = StrokeOpacityPending ? MaxRGB - (Quantum) (MaxRGBDouble * StrokeOpacitySaved + 0.5) : graphic_context[n]->stroke.opacity;
4154
32.8k
                            graphic_context[n]->stroke.opacity = MaxRGB - (Quantum)((double)(MaxRGB - StrokeOpacity) * (double)(MaxRGB - graphic_context[n]->opacity) / MaxRGBDouble + 0.5);
4155
32.8k
                          }
4156
73.9k
                        StrokeOpacityPending = MagickFalse;
4157
4158
73.9k
                      }/*stroke color != 'none'*/
4159
4160
119k
                  }/*stroke color, not pattern*/
4161
127k
                break;
4162
127k
              }
4163
219k
            if (LocaleCompare("stroke-antialias",keyword) == 0)
4164
3.53k
              {
4165
3.53k
                MagickGetToken(q,&q,token,token_max_length);
4166
3.53k
                if ((status &= MagickAtoUIChk(token,&graphic_context[n]->stroke_antialias)) == MagickFail)
4167
3
                  {
4168
3
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4169
3
                  }
4170
3.53k
                break;
4171
3.53k
              }
4172
216k
            if (LocaleCompare("stroke-dasharray",keyword) == 0)
4173
19.5k
              {
4174
19.5k
                MagickFreeMemory(graphic_context[n]->dash_pattern);
4175
19.5k
                if (IsPoint(q))
4176
17.2k
                  {
4177
17.2k
                    char
4178
17.2k
                      *p;
4179
4180
17.2k
                    p=q;
4181
17.2k
                    MagickGetToken(p,&p,token,token_max_length);
4182
17.2k
                    if (*token == ',')
4183
0
                      MagickGetToken(p,&p,token,token_max_length);
4184
85.4k
                    for (x=0; IsPoint(token); x++)
4185
68.1k
                      {
4186
68.1k
                        MagickGetToken(p,&p,token,token_max_length);
4187
68.1k
                        if (*token == ',')
4188
18.3k
                          MagickGetToken(p,&p,token,token_max_length);
4189
68.1k
                      }
4190
17.2k
                    graphic_context[n]->dash_pattern=
4191
17.2k
                      MagickAllocateClearedArray(double *,((size_t) 2*x+2),sizeof(double));
4192
17.2k
                    if (graphic_context[n]->dash_pattern == (double *) NULL)
4193
0
                      {
4194
0
                        (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4195
0
                        status=MagickFail;
4196
0
                        ThrowException3(&image->exception,ResourceLimitError,
4197
0
                                        MemoryAllocationFailed,UnableToDrawOnImage);
4198
0
                        break;
4199
0
                      }
4200
85.4k
                    for (j=0; j < x; j++)
4201
68.1k
                      {
4202
68.1k
                        MagickGetToken(q,&q,token,token_max_length);
4203
68.1k
                        if (*token == ',')
4204
18.3k
                          MagickGetToken(q,&q,token,token_max_length);
4205
68.1k
                        if ((status &= MagickAtoFChk(token,&graphic_context[n]->dash_pattern[j])) == MagickFail)
4206
29.4k
                          {
4207
29.4k
                            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4208
29.4k
                          }
4209
68.1k
                        if (graphic_context[n]->dash_pattern[j] < 0.0)
4210
970
                          {
4211
970
                            (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4212
970
                            status=MagickFail;
4213
970
                          }
4214
68.1k
                      }
4215
17.2k
                    if (status == MagickFail)
4216
70
                      {
4217
70
                        MagickFreeMemory(graphic_context[n]->dash_pattern);
4218
70
                        break;
4219
70
                      }
4220
17.1k
                    if (x & 0x01)
4221
24.4k
                      for ( ; j < (2*x); j++)
4222
16.1k
                        graphic_context[n]->dash_pattern[j]=
4223
16.1k
                          graphic_context[n]->dash_pattern[j-x];
4224
17.1k
                    graphic_context[n]->dash_pattern[j]=0.0;
4225
17.1k
                    break;
4226
17.2k
                  }
4227
2.30k
                MagickGetToken(q,&q,token,token_max_length);
4228
2.30k
                break;
4229
19.5k
              }
4230
196k
            if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4231
4.68k
              {
4232
4.68k
                MagickGetToken(q,&q,token,token_max_length);
4233
4.68k
                if ((status &= MagickAtoFChk(token,&graphic_context[n]->dash_offset)) == MagickFail)
4234
3
                  {
4235
3
                    (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4236
3
                  }
4237
4.68k
                break;
4238
4.68k
              }
4239
192k
            if (LocaleCompare("stroke-linecap",keyword) == 0)
4240
14.7k
              {
4241
14.7k
                MagickGetToken(q,&q,token,token_max_length);
4242
14.7k
                if (LocaleCompare("butt",token) == 0)
4243
1.07k
                  {
4244
1.07k
                    graphic_context[n]->linecap=ButtCap;
4245
1.07k
                    break;
4246
1.07k
                  }
4247
13.7k
                if (LocaleCompare("round",token) == 0)
4248
9.94k
                  {
4249
9.94k
                    graphic_context[n]->linecap=RoundCap;
4250
9.94k
                    break;
4251
9.94k
                  }
4252
3.76k
                if (LocaleCompare("square",token) == 0)
4253
3.72k
                  {
4254
3.72k
                    graphic_context[n]->linecap=SquareCap;
4255
3.72k
                    break;
4256
3.72k
                  }
4257
35
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4258
35
                status=MagickFail;
4259
35
                break;
4260
3.76k
              }
4261
177k
            if (LocaleCompare("stroke-linejoin",keyword) == 0)
4262
13.5k
              {
4263
13.5k
                MagickGetToken(q,&q,token,token_max_length);
4264
13.5k
                if (LocaleCompare("bevel",token) == 0)
4265
1.62k
                  {
4266
1.62k
                    graphic_context[n]->linejoin=BevelJoin;
4267
1.62k
                    break;
4268
1.62k
                  }
4269
11.8k
                if (LocaleCompare("miter",token) == 0)
4270
971
                  {
4271
971
                    graphic_context[n]->linejoin=MiterJoin;
4272
971
                    break;
4273
971
                  }
4274
10.9k
                if (LocaleCompare("round",token) == 0)
4275
10.8k
                  {
4276
10.8k
                    graphic_context[n]->linejoin=RoundJoin;
4277
10.8k
                    break;
4278
10.8k
                  }
4279
28
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4280
28
                status=MagickFail;
4281
28
                break;
4282
10.9k
              }
4283
163k
            if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4284
4.03k
              {
4285
4.03k
                MagickGetToken(q,&q,token,token_max_length);
4286
4.03k
                if ((status &= MagickAtoULChk(token,&graphic_context[n]->miterlimit)) == MagickFail)
4287
3
                  {
4288
3
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4289
3
                    break;
4290
3
                  }
4291
4.02k
                if (graphic_context[n]->miterlimit < 1)
4292
1
                  {
4293
1
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4294
1
                    status=MagickFail;
4295
1
                  }
4296
4.02k
                break;
4297
4.03k
              }
4298
159k
            if (LocaleCompare("stroke-opacity",keyword) == 0)
4299
50.2k
              {
4300
50.2k
                double opacity;
4301
50.2k
                MagickGetToken(q,&q,token,token_max_length);
4302
50.2k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
4303
2.25k
                  break;    /* if drawing clip path, ignore changes to stroke opacity */
4304
48.0k
                factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
4305
48.0k
                if ((status &= MagickAtoFChk(token,&opacity)) == MagickFail)
4306
5
                  {
4307
5
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4308
5
                    status=MagickFail;
4309
5
                    break;
4310
5
                  }
4311
48.0k
                if (opacity < 0.0)
4312
3
                  {
4313
3
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4314
3
                    status=MagickFail;
4315
3
                    break;
4316
3
                  }
4317
47.9k
                opacity *= factor;
4318
47.9k
                if  ( opacity <= 0.0 )  /* per SVG spec */
4319
805
                  opacity = 0.0;
4320
47.1k
                else if  ( opacity > 1.0 )
4321
1.88k
                  opacity = 1.0;
4322
47.9k
                StrokeOpacitySaved = opacity;
4323
47.9k
                if (graphic_context[n]->stroke.opacity != TransparentOpacity)
4324
1.35k
                  {
4325
                    /* combine new stroke-opacity with group opacity per SVG spec */
4326
1.35k
                    double opacityGroup = MaxRGB - graphic_context[n]->opacity;
4327
1.35k
                    graphic_context[n]->stroke.opacity = MaxRGB - (Quantum)(opacity * opacityGroup + 0.5);
4328
1.35k
                    StrokeOpacityPending = MagickFalse;
4329
1.35k
                  }
4330
46.6k
                else
4331
46.6k
                  StrokeOpacityPending = MagickTrue;
4332
47.9k
                break;
4333
48.0k
              }
4334
109k
            if (LocaleCompare("stroke-width",keyword) == 0)
4335
61.8k
              {
4336
61.8k
                MagickGetToken(q,&q,token,token_max_length);
4337
61.8k
                if  ( IsDrawInfoSVGCompliantClippingPath(graphic_context[n]) )
4338
2.28k
                  break;    /* if drawing clip path, ignore changes to stroke width */
4339
59.5k
                if ((status &= MagickAtoFChk(token,&graphic_context[n]->stroke_width)) == MagickFail)
4340
6
                  {
4341
6
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4342
6
                    break;
4343
6
                  }
4344
59.5k
                if (graphic_context[n]->stroke_width < 0.0)
4345
1
                  {
4346
1
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4347
1
                    status=MagickFail;
4348
1
                  }
4349
59.5k
                break;
4350
59.5k
              }
4351
47.7k
            if (LocaleCompare("svg-compliant",keyword) == 0)
4352
47.5k
              {
4353
                /* mark the DrawInfo as being drawn SVG compliant or not */
4354
47.5k
                unsigned int SVGCompliant;
4355
47.5k
                MagickGetToken(q,&q,token,token_max_length);
4356
47.5k
                if ((status &= MagickAtoUIChk(token,&SVGCompliant)) == MagickFail)
4357
4
                  {
4358
4
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4359
4
                  }
4360
47.5k
                else
4361
47.5k
                  SetDrawInfoSVGCompliant(graphic_context[n],SVGCompliant?MagickTrue:MagickFalse);
4362
47.5k
                break;
4363
47.5k
              }
4364
179
            LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4365
179
            status=MagickFail;
4366
179
            break;
4367
47.7k
          }
4368
180k
        case 't':
4369
183k
        case 'T':
4370
183k
          {
4371
183k
            if (LocaleCompare("text",keyword) == 0)
4372
1.25k
              {
4373
1.25k
                primitive_type=TextPrimitive;
4374
1.25k
                UseCurrentTextPosition = MagickFalse;   /* use client-supplied text locations */
4375
1.25k
                break;
4376
1.25k
              }
4377
182k
            if (LocaleCompare("textc",keyword) == 0)  /* draw text at current text position */
4378
41.5k
              {
4379
41.5k
                primitive_type=TextPrimitive;
4380
41.5k
                UseCurrentTextPosition = MagickTrue;        /* use internally tracked text location*/
4381
41.5k
                break;
4382
41.5k
              }
4383
141k
            if (LocaleCompare("textdx",keyword) == 0)  /* update current x position for text */
4384
8.43k
              {/*textdx*/
4385
8.43k
                double value;
4386
8.43k
                MagickGetToken(q,&q,token,token_max_length);
4387
8.43k
                if ((status &= MagickAtoFChk(token,&value)) == MagickFail)
4388
5
                  {
4389
5
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4390
5
                    break;
4391
5
                  }
4392
                /* value may be specified using "em" or "ex" units */
4393
8.42k
                if (LocaleNCompare(q,"em",2) == 0)
4394
648
                  {
4395
648
                    value *= graphic_context[n]->pointsize;
4396
648
                    MagickGetToken(q,&q,token,token_max_length);    /* skip over "em" */
4397
648
                  }
4398
7.78k
                else if (LocaleNCompare(q,"ex",2) == 0)
4399
179
                  {
4400
179
                    value *= 0.5 * graphic_context[n]->pointsize;
4401
179
                    MagickGetToken(q,&q,token,token_max_length);    /* skip over "ex" */
4402
179
                  }
4403
8.42k
                xTextCurrent += value;
4404
8.42k
                break;
4405
8.43k
              }/*textdx*/
4406
132k
            if (LocaleCompare("textdy",keyword) == 0)  /* update current y position for text */
4407
3.79k
              {/*textdy*/
4408
3.79k
                double value;
4409
3.79k
                MagickGetToken(q,&q,token,token_max_length);
4410
3.79k
                if ((status &= MagickAtoFChk(token,&value)) == MagickFail)
4411
3
                  {
4412
3
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4413
3
                    break;
4414
3
                  }
4415
                /* value may be specified using "em" or "ex" units */
4416
3.78k
                if (LocaleNCompare(q,"em",2) == 0)
4417
255
                  {
4418
255
                    value *= graphic_context[n]->pointsize;
4419
255
                    MagickGetToken(q,&q,token,token_max_length);    /* skip over "em" */
4420
255
                  }
4421
3.53k
                else if (LocaleNCompare(q,"ex",2) == 0)
4422
1.97k
                  {
4423
1.97k
                    value *= 0.5 * graphic_context[n]->pointsize;
4424
1.97k
                    MagickGetToken(q,&q,token,token_max_length);    /* skip over "ex" */
4425
1.97k
                  }
4426
3.78k
                yTextCurrent += value;
4427
3.78k
                break;
4428
3.79k
              }/*textdy*/
4429
            /*
4430
              When the current text position was managed in SVGStartElement() in svg.c, and a "rotate"
4431
              was encountered (indicating that the text character was to be rotated), the code would
4432
              emit to the MVG file:
4433
4434
              translate x y (where x, y indicate the current text position)
4435
              rotate angle (where angle indicates the rotation angle)
4436
4437
              Now that the current text position is being managed by DrawImage() in render.c, the code
4438
              in SVGStartElement() cannot issue the "translate" because it can't know the current text
4439
              position.  To handle this, "textr" (text rotation) has been implemented here to perform
4440
              the appropriate translation/rotation sequence.
4441
            */
4442
128k
            if (LocaleCompare("textr",keyword) == 0)  /* text rotation */
4443
6.26k
              {/*textr*/
4444
6.26k
                TextRotationPerformed = MagickTrue;
4445
                /* translate x y */
4446
6.26k
                affine.tx = xTextCurrent;
4447
6.26k
                affine.ty = yTextCurrent;
4448
                /* rotation angle */
4449
6.26k
                MagickGetToken(q,&q,token,token_max_length);
4450
6.26k
                if ((status &= MagickAtoFChk(token,&angle)) == MagickFail)
4451
3
                  {
4452
3
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4453
3
                    break;
4454
3
                  }
4455
6.26k
                affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
4456
6.26k
                affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
4457
6.26k
                affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
4458
6.26k
                affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
4459
6.26k
                break;
4460
6.26k
              }/*textr*/
4461
122k
            if (LocaleCompare("textx",keyword) == 0)  /* set current x position for text */
4462
57.4k
              {
4463
57.4k
                double value;
4464
57.4k
                MagickGetToken(q,&q,token,token_max_length);
4465
57.4k
                if ((status &= MagickAtoFChk(token,&value)) == MagickFail)
4466
1
                  {
4467
1
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4468
1
                    break;
4469
1
                  }
4470
57.4k
                xTextCurrent = value;
4471
57.4k
                break;
4472
57.4k
              }
4473
65.1k
            if (LocaleCompare("texty",keyword) == 0)  /* set current y position for text */
4474
53.7k
              {
4475
53.7k
                double value;
4476
53.7k
                MagickGetToken(q,&q,token,token_max_length);
4477
53.7k
                if ((status &= MagickAtoFChk(token,&value)) == MagickFail)
4478
3
                  {
4479
3
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4480
3
                    break;
4481
3
                  }
4482
53.7k
                yTextCurrent = value;
4483
53.7k
                break;
4484
53.7k
              }
4485
11.3k
            if (LocaleCompare("text-align",keyword) == 0)
4486
1.65k
              {
4487
1.65k
                MagickGetToken(q,&q,token,token_max_length);
4488
1.65k
                if (LocaleCompare(token,"left") == 0)
4489
84
                  graphic_context[n]->align=LeftAlign;
4490
1.65k
                if (LocaleCompare(token,"center") == 0)
4491
827
                  graphic_context[n]->align=CenterAlign;
4492
1.65k
                if (LocaleCompare(token,"right") == 0)
4493
73
                  graphic_context[n]->align=RightAlign;
4494
1.65k
                break;
4495
1.65k
              }
4496
9.69k
            if (LocaleCompare("text-anchor",keyword) == 0)
4497
205
              {
4498
205
                MagickGetToken(q,&q,token,token_max_length);
4499
205
                if (LocaleCompare(token,"start") == 0)
4500
0
                  graphic_context[n]->align=LeftAlign;
4501
205
                if (LocaleCompare(token,"middle") == 0)
4502
78
                  graphic_context[n]->align=CenterAlign;
4503
205
                if (LocaleCompare(token,"end") == 0)
4504
0
                  graphic_context[n]->align=RightAlign;
4505
205
                break;
4506
205
              }
4507
9.49k
            if (LocaleCompare("text-antialias",keyword) == 0)
4508
857
              {
4509
857
                unsigned int value;
4510
857
                MagickGetToken(q,&q,token,token_max_length);
4511
857
                if ((status &= MagickAtoUIChk(token,&value)) == MagickFail)
4512
1
                  {
4513
1
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4514
1
                    break;
4515
1
                  }
4516
856
                graphic_context[n]->text_antialias = value;
4517
856
                break;
4518
857
              }
4519
8.63k
            if (LocaleCompare("text-undercolor",keyword) == 0)
4520
3.65k
              {
4521
3.65k
                MagickGetToken(q,&q,token,token_max_length);
4522
3.65k
                if ((status &= QueryColorDatabase(token,&graphic_context[n]->undercolor,
4523
3.65k
                                                  &image->exception)) == MagickFail)
4524
1
                  LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4525
3.65k
                break;
4526
3.65k
              }
4527
4.98k
            if (LocaleCompare("translate",keyword) == 0)
4528
4.80k
              {
4529
4.80k
                MagickGetToken(q,&q,token,token_max_length);
4530
4.80k
                if ((status &= MagickAtoFChk(token,&affine.tx)) == MagickFail)
4531
2
                  {
4532
2
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4533
2
                    break;
4534
2
                  }
4535
4.80k
                MagickGetToken(q,&q,token,token_max_length);
4536
4.80k
                if (*token == ',')
4537
4.27k
                  MagickGetToken(q,&q,token,token_max_length);
4538
4.80k
                if ((status &= MagickAtoFChk(token,&affine.ty)) == MagickFail)
4539
2
                  {
4540
2
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4541
2
                    break;
4542
2
                  }
4543
4.80k
                break;
4544
4.80k
              }
4545
172
            status=MagickFail;
4546
172
            break;
4547
4.98k
          }
4548
866
        case 'u':
4549
875
        case 'U':
4550
875
          {
4551
875
            if (LocaleCompare("use",keyword) == 0)
4552
847
              {
4553
                /* FIXME: nothing prevents infinite recursion of "use" */
4554
847
                q = InsertAttributeIntoInputStream(keyword,q,&primitive,&primitive_extent,
4555
847
                                                   &token,&token_max_length,image,
4556
847
                                                   &status,MagickTrue/*UndefAttrIsError*/);
4557
847
                break;
4558
847
              }
4559
28
            LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4560
28
            status=MagickFail;
4561
28
            break;
4562
875
          }
4563
94.8k
        case 'v':
4564
96.4k
        case 'V':
4565
96.4k
          {
4566
96.4k
            if (LocaleCompare("viewbox",keyword) == 0)
4567
96.2k
              {
4568
                /* This is not currently used for anything */
4569
96.2k
                unsigned long
4570
96.2k
                  viewbox_columns,
4571
96.2k
                  viewbox_rows;
4572
96.2k
                MagickGetToken(q,&q,token,token_max_length);
4573
96.2k
                if ((status &= MagickAtoFChk(token,&viewbox.x1)) == MagickFail)
4574
12
                  {
4575
12
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4576
12
                    break;
4577
12
                  }
4578
96.2k
                MagickGetToken(q,&q,token,token_max_length);
4579
96.2k
                if (*token == ',')
4580
354
                  MagickGetToken(q,&q,token,token_max_length);
4581
96.2k
                if ((status &= MagickAtoFChk(token,&viewbox.y1)) == MagickFail)
4582
22
                  {
4583
22
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4584
22
                    break;
4585
22
                  }
4586
96.1k
                MagickGetToken(q,&q,token,token_max_length);
4587
96.1k
                if (*token == ',')
4588
236
                  MagickGetToken(q,&q,token,token_max_length);
4589
96.1k
                if ((status &= MagickAtoFChk(token,&viewbox.x2)) == MagickFail)
4590
22
                  {
4591
22
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4592
22
                    break;
4593
22
                  }
4594
96.1k
                MagickGetToken(q,&q,token,token_max_length);
4595
96.1k
                if (*token == ',')
4596
1.70k
                  MagickGetToken(q,&q,token,token_max_length);
4597
96.1k
                if ((status &= MagickAtoFChk(token,&viewbox.y2)) == MagickFail)
4598
15
                  {
4599
15
                    LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4600
15
                    break;
4601
15
                  }
4602
96.1k
                viewbox_columns=(unsigned long) (viewbox.x2-viewbox.x1+0.5);
4603
96.1k
                viewbox_rows=(unsigned long) (viewbox.y2-viewbox.y1+0.5);
4604
#if 0
4605
                (void) LogMagickEvent(RenderEvent,GetMagickModule(),
4606
                                      "ViewBox: %g %g %g %g, Geometry: %lux%lu",
4607
                                      viewbox.x1, viewbox.y1, viewbox.x2, viewbox.y2,
4608
                                      viewbox_columns,viewbox_rows);
4609
                /* image->columns=viewbox_columns; */
4610
                /* image->rows=viewbox_rows; */
4611
#else
4612
96.1k
                (void) viewbox_columns;
4613
96.1k
                (void) viewbox_rows;
4614
96.1k
#endif
4615
96.1k
                break;
4616
96.1k
              }
4617
204
            LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4618
204
            status=MagickFail;
4619
204
            break;
4620
96.4k
          }
4621
626
        default:
4622
626
          {
4623
626
            LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4624
626
            status=MagickFail;
4625
626
            break;
4626
96.4k
          }
4627
1.86M
        }
4628
1.86M
      if (status == MagickFail)
4629
42.6k
        {
4630
42.6k
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4631
42.6k
          break;
4632
42.6k
        }
4633
1.82M
      if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
4634
1.78M
          (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
4635
65.5k
        {
4636
65.5k
          graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
4637
65.5k
          graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
4638
65.5k
          graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
4639
65.5k
          graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
4640
65.5k
          graphic_context[n]->affine.tx=
4641
65.5k
            current.sx*affine.tx+current.ry*affine.ty+current.tx;
4642
65.5k
          graphic_context[n]->affine.ty=
4643
65.5k
            current.rx*affine.tx+current.sy*affine.ty+current.ty;
4644
65.5k
        }
4645
1.82M
      if (primitive_type == UndefinedPrimitive)
4646
1.54M
        {
4647
1.54M
          (void) LogMagickEvent(RenderEvent,GetMagickModule(),"  %.*s",
4648
1.54M
                                (int) (q-p),p);
4649
1.54M
          continue;
4650
1.54M
        }
4651
      /*
4652
        Parse the primitive attributes.
4653
      */
4654
277k
      PIMgr.StoreStartingAt = i = 0;
4655
277k
      j=0;
4656
277k
      primitive_info[0].point.x=0.0;
4657
277k
      primitive_info[0].point.y=0.0;
4658
277k
      primitive_info[0].coordinates=0;
4659
277k
      primitive_info[0].method=FloodfillMethod;
4660
277k
      PRIMINF_CLEAR_FLAGS(&primitive_info[0]);
4661
690k
      for (x=0; *q != '\0'; x++)
4662
689k
        {
4663
          /*
4664
            Define points.
4665
          */
4666
689k
          if (!IsPoint(q))
4667
275k
            break;
4668
413k
          MagickGetToken(q,&q,token,token_max_length);
4669
413k
          if ((status &= MagickAtoFChk(token,&point.x)) == MagickFail)
4670
2
            {
4671
2
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4672
2
              break;
4673
2
            }
4674
413k
          MagickGetToken(q,&q,token,token_max_length);
4675
413k
          if (*token == ',')
4676
188k
            MagickGetToken(q,&q,token,token_max_length);
4677
413k
          if ((status &= MagickAtoFChk(token,&point.y)) == MagickFail)
4678
150
            {
4679
150
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4680
150
              break;
4681
150
            }
4682
413k
          MagickGetToken(q,(char **) NULL,token,token_max_length);
4683
413k
          if (*token == ',')
4684
2.18k
            MagickGetToken(q,&q,token,token_max_length);
4685
413k
          assert(i < number_points);
4686
413k
          primitive_info[i].primitive=primitive_type;
4687
413k
          primitive_info[i].point=point;
4688
413k
          primitive_info[i].coordinates=0;
4689
413k
          primitive_info[i].method=FloodfillMethod;
4690
413k
          PRIMINF_CLEAR_FLAGS(&primitive_info[i]);
4691
413k
          i++;
4692
413k
          PIMgr.StoreStartingAt = i;
4693
413k
          if (i < number_points)
4694
413k
            continue;
4695
          /* Array is full; double the array size */
4696
10
          if ((status &= PrimitiveInfoRealloc(&PIMgr,number_points)) == MagickFail)
4697
0
            {
4698
0
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4699
0
              break;
4700
0
            }
4701
10
        }
4702
277k
      if (status == MagickFail)
4703
152
        {
4704
152
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4705
152
          break;
4706
152
        }
4707
      /*
4708
        Special handling when using textc with character rotation; see comments
4709
        above near "textr".
4710
      */
4711
277k
      if  ( (primitive_type == TextPrimitive) && UseCurrentTextPosition && (i == 0) )
4712
41.5k
        {
4713
41.5k
          primitive_info[0].primitive=primitive_type;
4714
41.5k
          PRIMINF_CLEAR_FLAGS(&primitive_info[0]);
4715
41.5k
          if  ( TextRotationPerformed )
4716
1.71k
            {
4717
              /* text positioning has already been performed by translate/rotate sequence */
4718
1.71k
              primitive_info[0].point.x=0;
4719
1.71k
              primitive_info[0].point.y=0;
4720
1.71k
              TextRotationPerformed = MagickFalse;
4721
1.71k
            }
4722
39.8k
          else
4723
39.8k
            {
4724
39.8k
              primitive_info[0].point.x=xTextCurrent;
4725
39.8k
              primitive_info[0].point.y=yTextCurrent;
4726
39.8k
            }
4727
41.5k
          primitive_info[0].coordinates=0;
4728
41.5k
          primitive_info[0].method=FloodfillMethod;
4729
41.5k
          i++;
4730
41.5k
          PIMgr.StoreStartingAt = i;
4731
41.5k
          x++;
4732
41.5k
        }
4733
277k
      assert(j < (long) number_points);
4734
277k
      primitive_info[j].primitive=primitive_type;
4735
277k
      primitive_info[j].coordinates=x;
4736
277k
      primitive_info[j].method=FloodfillMethod;
4737
277k
      PRIMINF_CLEAR_FLAGS(&primitive_info[j]);
4738
277k
      primitive_info[j].text=(char *) NULL;
4739
      /*
4740
        Circumscribe primitive within a circle.
4741
      */
4742
277k
      bounds.x1=primitive_info[j].point.x;
4743
277k
      bounds.y1=primitive_info[j].point.y;
4744
277k
      bounds.x2=primitive_info[j].point.x;
4745
277k
      bounds.y2=primitive_info[j].point.y;
4746
420k
      for (k=1; k < (long) primitive_info[j].coordinates; k++)
4747
142k
        {
4748
142k
          assert(j+k < (long) number_points);
4749
142k
          point=primitive_info[j+k].point;
4750
142k
          if (point.x < bounds.x1)
4751
13.8k
            bounds.x1=point.x;
4752
142k
          if (point.y < bounds.y1)
4753
28.9k
            bounds.y1=point.y;
4754
142k
          if (point.x > bounds.x2)
4755
63.0k
            bounds.x2=point.x;
4756
142k
          if (point.y > bounds.y2)
4757
64.4k
            bounds.y2=point.y;
4758
142k
        }
4759
      /*
4760
        Estimate how many points will be required for the primitive.
4761
      */
4762
277k
      points_length=primitive_info[j].coordinates;
4763
277k
      switch (primitive_type)
4764
277k
        {
4765
22.4k
        case RectanglePrimitive:
4766
22.4k
          {
4767
22.4k
            points_length*=5;
4768
22.4k
            break;
4769
0
          }
4770
8.45k
        case RoundRectanglePrimitive:
4771
8.45k
          {
4772
            /*
4773
              Round rectangle is rectangle plus elipse
4774
            */
4775
8.45k
            double
4776
8.45k
              alpha,
4777
8.45k
              beta,
4778
8.45k
              radius;
4779
4780
8.45k
            alpha=bounds.x2-bounds.x1;
4781
8.45k
            beta=bounds.y2-bounds.y1;
4782
8.45k
            radius=hypot((double) alpha,(double) beta);
4783
8.45k
            points_length*=5;
4784
8.45k
            points_length+=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360;
4785
8.45k
            break;
4786
0
          }
4787
345
        case BezierPrimitive:
4788
345
          {
4789
345
            if (primitive_info[j].coordinates > 107)
4790
12
              (void) ThrowException(&image->exception,DrawError,
4791
345
                                    TooManyCoordinates,token);
4792
345
            points_length=primitive_info[j].coordinates*BezierQuantum;
4793
345
            break;
4794
0
          }
4795
71.9k
        case PathPrimitive:
4796
71.9k
          {
4797
71.9k
            char
4798
71.9k
              *s,
4799
71.9k
              *t;
4800
4801
71.9k
            MagickGetToken(q,&q,token,token_max_length);
4802
71.9k
            points_length=1;
4803
71.9k
            t=token;
4804
19.8M
            for (s=token; *s != '\0'; s=t)
4805
19.8M
              {
4806
19.8M
                double
4807
19.8M
                  value;
4808
4809
19.8M
                value=strtod(s,&t);
4810
19.8M
                (void) value;
4811
19.8M
                if (s == t)
4812
14.1M
                  {
4813
14.1M
                    t++;
4814
14.1M
                    continue;
4815
14.1M
                  }
4816
5.69M
                points_length++;
4817
5.69M
              }
4818
71.9k
            points_length=points_length*BezierQuantum;
4819
71.9k
            break;
4820
0
          }
4821
17.1k
        case CirclePrimitive:
4822
41.1k
        case ArcPrimitive:
4823
43.1k
        case EllipsePrimitive:
4824
43.1k
          {
4825
43.1k
            double
4826
43.1k
              alpha,
4827
43.1k
              beta,
4828
43.1k
              radius;
4829
4830
43.1k
            alpha=bounds.x2-bounds.x1;
4831
43.1k
            beta=bounds.y2-bounds.y1;
4832
43.1k
            radius=hypot(alpha,beta);
4833
43.1k
            points_length=2.0*(ceil(MagickPI*radius))+6.0*BezierQuantum+360.0;
4834
43.1k
            break;
4835
41.1k
          }
4836
131k
        default:
4837
131k
          break;
4838
277k
        }
4839
4840
277k
      if (((size_t) points_length) < points_length)
4841
3
        {
4842
          /* points_length too big to be represented as a size_t */
4843
3
          status=MagickFail;
4844
3
          ThrowException(&image->exception,DrawError,
4845
3
                         PrimitiveArithmeticOverflow,keyword);
4846
3
        }
4847
4848
277k
      if (status == MagickFail)
4849
3
        {
4850
3
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4851
3
          break;
4852
3
        }
4853
4854
277k
      if ((i+points_length) >= number_points)
4855
5.32k
        {
4856
5.32k
          double new_number_points = ceil(number_points+points_length+1);
4857
5.32k
          size_t new_number_points_size_t;
4858
5.32k
          if (new_number_points > (double) PRIMITIVE_INFO_POINTS_MAX)
4859
3
            {
4860
              /* new_number_points too big */
4861
3
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4862
3
              status=MagickFail;
4863
3
              ThrowException3(&image->exception,ResourceLimitError,
4864
3
                              MemoryAllocationFailed,UnableToDrawOnImage);
4865
3
              break;
4866
3
            }
4867
5.32k
          new_number_points_size_t = (size_t) new_number_points;
4868
4869
5.32k
          PIMgr.StoreStartingAt = i;  /* should already be this value; just bein' sure */
4870
5.32k
          if ((status &= PrimitiveInfoRealloc(&PIMgr,new_number_points_size_t-number_points)) == MagickFail)
4871
240
            {
4872
240
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4873
240
              break;
4874
240
            }
4875
5.32k
        }
4876
4877
277k
      assert(j < (long) number_points);
4878
4879
      /*
4880
        The TraceXXX functions that generate a dynamic number of points will automatically
4881
        grow the primitive array as needed.  To make sure that the simpler ones (TracePoint()
4882
        TraceLine(), TraceRectangle(), etc.) have enough space, we make sure there are at
4883
        least 100 elements available.
4884
      */
4885
277k
      if ((status &= PrimitiveInfoRealloc(&PIMgr,100)) == MagickFail)
4886
0
        {
4887
0
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4888
0
          break;
4889
0
        }
4890
4891
      /*
4892
        Trace points
4893
      */
4894
277k
      switch (primitive_type)
4895
277k
        {
4896
2.78k
        case PointPrimitive:
4897
2.78k
        default:
4898
2.78k
          {
4899
2.78k
            if (primitive_info[j].coordinates != 1)
4900
7
              {
4901
7
                status=MagickFail;
4902
7
                break;
4903
7
              }
4904
2.77k
            if ((status &= TracePoint(primitive_info+j,primitive_info[j].point)) == MagickFail)
4905
0
              {
4906
0
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4907
0
                break;
4908
0
              }
4909
2.77k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
4910
2.77k
            break;
4911
2.77k
          }
4912
35.9k
        case LinePrimitive:
4913
35.9k
          {
4914
35.9k
            if (primitive_info[j].coordinates != 2)
4915
13
              {
4916
13
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4917
13
                status=MagickFail;
4918
13
                break;
4919
13
              }
4920
35.9k
            if ((status &= TraceLine(primitive_info+j,primitive_info[j].point,
4921
35.9k
                                     primitive_info[j+1].point)) == MagickFail)
4922
0
              {
4923
0
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4924
0
                break;
4925
0
              }
4926
35.9k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
4927
35.9k
            break;
4928
35.9k
          }
4929
22.4k
        case RectanglePrimitive:
4930
22.4k
          {
4931
            /*
4932
              Rectangle requires 2 primitives.
4933
            */
4934
22.4k
            if (primitive_info[j].coordinates != 2)
4935
6
              {
4936
6
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4937
6
                status=MagickFail;
4938
6
                break;
4939
6
              }
4940
            /*
4941
              Negative width is an error
4942
            */
4943
22.3k
            if ((primitive_info[j+1].point.x - primitive_info[j].point.x) < 0.0)
4944
79
              {
4945
79
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4946
79
                status=MagickFail;
4947
79
                break;
4948
79
              }
4949
            /*
4950
              Negative height is an error
4951
            */
4952
22.3k
            if ((primitive_info[j+1].point.y - primitive_info[j].point.y) < 0.0)
4953
55
              {
4954
55
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4955
55
                status=MagickFail;
4956
55
                break;
4957
55
              }
4958
22.2k
            if ((status &= TraceRectangle(primitive_info+j,
4959
22.2k
                                          /*start*/primitive_info[j].point,
4960
22.2k
                                          /*end*/primitive_info[j+1].point)) == MagickFail)
4961
0
              {
4962
0
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4963
0
                break;
4964
0
              }
4965
22.2k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
4966
22.2k
            break;
4967
22.2k
          }
4968
8.38k
        case RoundRectanglePrimitive:
4969
8.38k
          {
4970
            /*
4971
              Round rectangle requires 3 primitives.
4972
            */
4973
8.38k
            if (primitive_info[j].coordinates != 3)
4974
6
              {
4975
6
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4976
6
                status=MagickFail;
4977
6
                break;
4978
6
              }
4979
            /*
4980
              Negative radius values are an error.
4981
            */
4982
8.37k
            if ((primitive_info[j+2].point.x < 0.0) || (primitive_info[j+2].point.y < 0.0))
4983
8
              {
4984
8
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4985
8
                status=MagickFail;
4986
8
                break;
4987
8
              }
4988
            /*
4989
              Negative width is an error
4990
            */
4991
8.37k
            if ((primitive_info[j+1].point.x - primitive_info[j].point.x) < 0.0)
4992
2
              {
4993
2
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
4994
2
                status=MagickFail;
4995
2
                break;
4996
2
              }
4997
            /*
4998
              Negative height is an error
4999
            */
5000
8.36k
            if ((primitive_info[j+1].point.y - primitive_info[j].point.y) < 0.0)
5001
3
              {
5002
3
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5003
3
                status=MagickFail;
5004
3
                break;
5005
3
              }
5006
8.36k
            PIMgr.StoreStartingAt=j;
5007
8.36k
            if ((status &= TraceRoundRectangle(&PIMgr,
5008
8.36k
                                               /*start*/primitive_info[j].point,
5009
8.36k
                                               /*end*/primitive_info[j+1].point,
5010
8.36k
                                               /*arc*/primitive_info[j+2].point)) == MagickFail)
5011
21
              {
5012
21
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5013
21
                break;
5014
21
              }
5015
8.34k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
5016
8.34k
            break;
5017
8.36k
          }
5018
23.9k
        case ArcPrimitive:
5019
23.9k
          {
5020
23.9k
            if (primitive_info[j].coordinates != 3)
5021
22.0k
              {
5022
22.0k
                primitive_type=UndefinedPrimitive;
5023
22.0k
                break;
5024
22.0k
              }
5025
1.90k
            PIMgr.StoreStartingAt=j;
5026
1.90k
            if ((status &= TraceArc(&PIMgr,primitive_info[j].point,
5027
1.90k
                                    primitive_info[j+1].point,primitive_info[j+2].point)) == MagickFail)
5028
11
              {
5029
11
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5030
11
                break;
5031
11
              }
5032
1.89k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
5033
1.89k
            break;
5034
1.90k
          }
5035
1.99k
        case EllipsePrimitive:
5036
1.99k
          {
5037
1.99k
            if (primitive_info[j].coordinates != 3)
5038
6
              {
5039
6
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5040
6
                status=MagickFail;
5041
6
                break;
5042
6
              }
5043
            /*
5044
              Negative radius values are an error.
5045
            */
5046
1.99k
            if ((primitive_info[j+1].point.x < 0.0) ||
5047
1.98k
                (primitive_info[j+1].point.y < 0.0))
5048
17
              {
5049
17
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5050
17
                status=MagickFail;
5051
17
                break;
5052
17
              }
5053
1.97k
            PIMgr.StoreStartingAt=j;
5054
1.97k
            if ((status &= TraceEllipse(&PIMgr,
5055
1.97k
                                        /*start*/primitive_info[j].point, /*centerX,centerY*/
5056
1.97k
                                        /*stop*/primitive_info[j+1].point, /*radiusX,radiusY*/
5057
1.97k
                                        /*degrees*/primitive_info[j+2].point)) == MagickFail) /*arcStart,arcEnd*/
5058
0
              {
5059
0
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5060
0
                break;
5061
0
              }
5062
1.97k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
5063
1.97k
            break;
5064
1.97k
          }
5065
17.0k
        case CirclePrimitive:
5066
17.0k
          {
5067
17.0k
            if (primitive_info[j].coordinates != 2)
5068
10
              {
5069
10
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5070
10
                status=MagickFail;
5071
10
                break;
5072
10
              }
5073
17.0k
            PIMgr.StoreStartingAt=j;
5074
17.0k
            if ((status &= TraceCircle(&PIMgr,primitive_info[j].point,
5075
17.0k
                                       primitive_info[j+1].point)) == MagickFail)
5076
0
              {
5077
0
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5078
0
                break;
5079
0
              }
5080
17.0k
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
5081
17.0k
            break;
5082
17.0k
          }
5083
160
        case PolylinePrimitive:
5084
160
          {
5085
            /* the SVG spec does not prohibit polylines containing a single point */
5086
160
            if (primitive_info[j].coordinates < 1)
5087
5
              {
5088
5
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5089
5
                status=MagickFail;
5090
5
                break;
5091
5
              }
5092
155
            break;
5093
160
          }
5094
269
        case PolygonPrimitive:
5095
269
          {
5096
            /*
5097
              Polygon requires at least three points.
5098
            */
5099
269
            if (primitive_info[j].coordinates < 3)
5100
11
              {
5101
11
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5102
11
                status=MagickFail;
5103
11
                break;
5104
11
              }
5105
258
            primitive_info[i]=primitive_info[j];
5106
258
            primitive_info[i].coordinates=0;
5107
258
            primitive_info[j].coordinates++;
5108
258
            PRIMINF_SET_IS_CLOSED_SUBPATH(&primitive_info[j],1);
5109
258
            i++;
5110
258
            break;
5111
269
          }
5112
345
        case BezierPrimitive:
5113
345
          {
5114
345
            if (primitive_info[j].coordinates < 3)
5115
8
              {
5116
8
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5117
8
                status=MagickFail;
5118
8
                break;
5119
8
              }
5120
337
            PIMgr.StoreStartingAt=j;
5121
337
            if ((status &= TraceBezier(&PIMgr,primitive_info[j].coordinates)) == MagickFail)
5122
1
              {
5123
1
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5124
1
                break;
5125
1
              }
5126
336
            PIMgr.StoreStartingAt=i=(long) (j+primitive_info[j].coordinates);
5127
336
            break;
5128
337
          }
5129
71.9k
        case PathPrimitive:
5130
71.9k
          {
5131
71.9k
            size_t number_coordinates=0;
5132
71.9k
            PIMgr.StoreStartingAt=j;
5133
71.9k
            if ((status &= TracePath(image,&PIMgr,token, &number_coordinates)) == MagickFail)
5134
1.63k
              {
5135
1.63k
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5136
1.63k
                break;
5137
1.63k
              }
5138
70.2k
            PIMgr.StoreStartingAt=i=(long) (j+number_coordinates);
5139
70.2k
            break;
5140
71.9k
          }
5141
10.5k
        case ColorPrimitive:
5142
16.1k
        case MattePrimitive:
5143
16.1k
          {
5144
16.1k
            if (primitive_info[j].coordinates != 1)
5145
9
              {
5146
9
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5147
9
                status=MagickFail;
5148
9
                break;
5149
9
              }
5150
16.1k
            MagickGetToken(q,&q,token,token_max_length);
5151
16.1k
            if (LocaleCompare("point",token) == 0)
5152
1.24k
              primitive_info[j].method=PointMethod;
5153
16.1k
            if (LocaleCompare("replace",token) == 0)
5154
6.54k
              primitive_info[j].method=ReplaceMethod;
5155
16.1k
            if (LocaleCompare("floodfill",token) == 0)
5156
1
              primitive_info[j].method=FloodfillMethod;
5157
16.1k
            if (LocaleCompare("filltoborder",token) == 0)
5158
701
              primitive_info[j].method=FillToBorderMethod;
5159
16.1k
            if (LocaleCompare("reset",token) == 0)
5160
303
              primitive_info[j].method=ResetMethod;
5161
16.1k
            break;
5162
16.1k
          }
5163
42.7k
        case TextPrimitive:
5164
42.7k
          {
5165
42.7k
            if (primitive_info[j].coordinates != 1)
5166
7
              {
5167
7
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5168
7
                status=MagickFail;
5169
7
                break;
5170
7
              }
5171
42.7k
            if (*token != ',')
5172
42.7k
              MagickGetToken(q,&q,token,token_max_length);
5173
42.7k
            primitive_info[j].text=AllocateString(token);
5174
42.7k
            {/*update current text position for next time*/
5175
              /*
5176
                Clone the DrawInfo, add a blank to the end of
5177
                the text, get the metrics for the concatenated
5178
                string, and use the width to update the text
5179
                current x position.
5180
              */
5181
42.7k
              DrawInfo * clone_info;
5182
42.7k
              TypeMetric  metrics;
5183
42.7k
              clone_info=CloneDrawInfo((ImageInfo *) NULL,graphic_context[n]);
5184
42.7k
              MagickFreeMemory(clone_info->density);        /* density values already converted, don't do again! */
5185
42.7k
              clone_info->render = 0;
5186
42.7k
              clone_info->text=AllocateString(token);
5187
42.7k
              (void) ConcatenateString(&clone_info->text," ");
5188
42.7k
              (void) GetTypeMetrics(image,clone_info,&metrics);
5189
42.7k
              xTextCurrent += metrics.width;
5190
42.7k
              DestroyDrawInfo(clone_info);
5191
42.7k
            }/*update current text position for next time*/
5192
42.7k
            break;
5193
42.7k
          }
5194
33.0k
        case ImagePrimitive:
5195
33.0k
          {
5196
33.0k
            if (primitive_info[j].coordinates != 2)
5197
23
              {
5198
23
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5199
23
                status=MagickFail;
5200
23
                break;
5201
23
              }
5202
33.0k
            MagickGetToken(q,&q,token,token_max_length);
5203
33.0k
            primitive_info[j].text=AllocateString(token);
5204
33.0k
            break;
5205
33.0k
          }
5206
277k
        }
5207
5208
277k
      if (primitive_info == (PrimitiveInfo *) NULL)
5209
34
        break;
5210
277k
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"  %.*s",(int) (q-p),p);
5211
277k
      if (status == MagickFail)
5212
1.90k
        {
5213
1.90k
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5214
1.90k
          break;
5215
1.90k
        }
5216
277k
      assert(i < number_points); /* FIXME: This assertion may be hit (oss-fuzz 459618468) if +100 "headroom" not provided */
5217
275k
      primitive_info[i].primitive=UndefinedPrimitive;
5218
275k
      if (i == 0)
5219
15.2k
        continue;
5220
      /*
5221
        Transform points.
5222
      */
5223
494M
      for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
5224
494M
        {
5225
494M
          assert((unsigned long) i < number_points);
5226
494M
          point=primitive_info[i].point;
5227
494M
          primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
5228
494M
            graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
5229
494M
          primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
5230
494M
            graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
5231
494M
          point=primitive_info[i].point;
5232
494M
          if (point.x < graphic_context[n]->bounds.x1)
5233
42.9M
            graphic_context[n]->bounds.x1=point.x;
5234
494M
          if (point.y < graphic_context[n]->bounds.y1)
5235
43.0M
            graphic_context[n]->bounds.y1=point.y;
5236
494M
          if (point.x > graphic_context[n]->bounds.x2)
5237
23.3M
            graphic_context[n]->bounds.x2=point.x;
5238
494M
          if (point.y > graphic_context[n]->bounds.y2)
5239
84.2M
            graphic_context[n]->bounds.y2=point.y;
5240
494M
          if (primitive_info[i].primitive == ImagePrimitive)
5241
33.0k
            break;
5242
494M
        }
5243
259k
      if (graphic_context[n]->render)
5244
241k
        {
5245
241k
          if ((n != 0) && (graphic_context[n]->extra->clip_path != (char *) NULL) &&
5246
14.6k
              (LocaleCompare(graphic_context[n]->extra->clip_path,
5247
14.6k
                             graphic_context[n-1]->extra->clip_path) != 0))
5248
4.09k
            if ((status &= DrawClipPath(image,graphic_context[n],
5249
4.09k
                                        graphic_context[n]->extra->clip_path)) == MagickFail)
5250
195
              {
5251
195
                LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5252
195
              }
5253
241k
          if ((n != 0) && (graphic_context[n]->extra->composite_path != (char *) NULL) &&
5254
8.24k
              (LocaleCompare(graphic_context[n]->extra->composite_path,
5255
8.24k
                             graphic_context[n-1]->extra->composite_path) != 0))
5256
1.54k
            {
5257
1.54k
              if ((status &= DrawCompositeMask(image,graphic_context[n],
5258
1.54k
                                               graphic_context[n]->extra->composite_path)) == MagickFail)
5259
197
                {
5260
197
                  LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5261
197
                }
5262
1.54k
            }
5263
241k
          if ((status &= DrawPrimitive(image,graphic_context[n],primitive_info)) == MagickFail)
5264
13.1k
            {
5265
13.1k
              LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5266
13.1k
            }
5267
241k
        }
5268
259k
      if (primitive_info->text != (char *) NULL)
5269
75.8k
        MagickFreeMemory(primitive_info->text);
5270
259k
      if ((status &= MagickMonitorFormatted(q-primitive,
5271
259k
                                            (magick_uint64_t) primitive_extent,
5272
259k
                                            &image->exception,
5273
259k
                                            RenderImageText,image->filename)) == MagickFail)
5274
13.1k
        {
5275
13.1k
          LogMagickEvent(RenderEvent,GetMagickModule(),"Quit with error");
5276
13.1k
        }
5277
259k
      if (status == MagickFail)
5278
13.1k
        break;
5279
259k
    }
5280
5281
  /* All function returns past "begin draw-image" pass through here */
5282
119k
 draw_image_done:
5283
5284
119k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"end draw-image");
5285
  /*
5286
    Free resources.
5287
  */
5288
119k
  DrawImageRecurseOut(image);
5289
119k
  MagickFreeResourceLimitedMemory(char *,token);
5290
119k
  MagickFreeResourceLimitedMemory(PrimitiveInfo *,primitive_info);
5291
119k
  if (original_primitive != draw_info->primitive)
5292
2
    MagickFreeResourceLimitedMemory(char *,original_primitive);
5293
119k
  MagickFreeResourceLimitedMemory(char *,primitive);
5294
119k
  if (graphic_context != (DrawInfo **) NULL)
5295
119k
    {
5296
378k
      for ( ; n >= 0; n--)
5297
258k
        DestroyDrawInfo(graphic_context[n]);
5298
119k
      MagickFreeResourceLimitedMemory(DrawInfo **,graphic_context);
5299
119k
    }
5300
119k
  if ((status == MagickFail) &&
5301
58.1k
      (image->exception.severity < ErrorException) &&
5302
4.23k
      (keyword[0] != '\0'))
5303
4.23k
    ThrowBinaryException(DrawError,NonconformingDrawingPrimitiveDefinition,
5304
119k
                         keyword);
5305
115k
  return(status);
5306
119k
}
5307

5308
/*
5309
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5310
%                                                                             %
5311
%                                                                             %
5312
%                                                                             %
5313
%   D r a w P a t t e r n P a t h                                             %
5314
%                                                                             %
5315
%                                                                             %
5316
%                                                                             %
5317
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5318
%
5319
%  DrawPatternPath() draws a pattern.
5320
%
5321
%  The format of the DrawPatternPath method is:
5322
%
5323
%      MagickPassFail DrawPatternPath(Image *image,const DrawInfo *draw_info,
5324
%        const char *name,Image **pattern)
5325
%
5326
%  A description of each parameter follows:
5327
%
5328
%    o image: The image.
5329
%
5330
%    o draw_info: The draw info.
5331
%
5332
%    o name: The pattern name.
5333
%
5334
%    o image: The image.
5335
%
5336
%
5337
*/
5338
MagickExport MagickPassFail
5339
DrawPatternPath(Image *image,const DrawInfo *draw_info,const char *name,
5340
                Image **pattern)
5341
30.4k
{
5342
30.4k
  char
5343
30.4k
    attribute[MaxTextExtent];
5344
5345
30.4k
  const ImageAttribute
5346
30.4k
    *geometry,
5347
30.4k
    *path;
5348
5349
30.4k
  DrawInfo
5350
30.4k
    *clone_info;
5351
5352
30.4k
  ImageInfo
5353
30.4k
    *image_info;
5354
5355
30.4k
  MagickPassFail
5356
30.4k
    status = MagickPass;
5357
5358
30.4k
  assert(image != (Image *) NULL);
5359
30.4k
  assert(image->signature == MagickSignature);
5360
30.4k
  assert(draw_info != (const DrawInfo *) NULL);
5361
30.4k
  assert(name != (const char *) NULL);
5362
30.4k
  MagickFormatString(attribute,sizeof(attribute),"[MVG:%.1024s]",name);
5363
30.4k
  path=GetImageAttribute(image,attribute);
5364
30.4k
  if (path == (ImageAttribute *) NULL)
5365
0
    return(MagickFail);
5366
30.4k
  MagickFormatString(attribute,sizeof(attribute),"[MVG:%.1024s-geometry]",name);
5367
30.4k
  geometry=GetImageAttribute(image,attribute);
5368
30.4k
  if (geometry == (ImageAttribute *) NULL)
5369
2
    return(MagickFail);
5370
30.4k
  if ((*pattern) != (Image *) NULL)
5371
25.9k
    DestroyImage(*pattern);
5372
30.4k
  image_info=CloneImageInfo((ImageInfo *) NULL);
5373
30.4k
  image_info->size=AllocateString(geometry->value);
5374
30.4k
  *pattern=AllocateImage(image_info);
5375
30.4k
  DestroyImageInfo(image_info);
5376
30.4k
  status &= QueryColorDatabase("none",&(*pattern)->background_color,
5377
30.4k
                               &image->exception);
5378
30.4k
  (void) SetImage(*pattern,OpaqueOpacity);
5379
30.4k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),
5380
30.4k
                        "begin pattern-path %.1024s %.1024s",name,geometry->value);
5381
30.4k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
5382
30.4k
  DestroyImage(clone_info->fill_pattern);
5383
30.4k
  clone_info->fill_pattern=(Image *) NULL;
5384
30.4k
  DestroyImage(clone_info->stroke_pattern);
5385
30.4k
  clone_info->stroke_pattern=(Image *) NULL;
5386
30.4k
  (void) CloneString(&clone_info->primitive,path->value);
5387
30.4k
  if ((status &= DrawImage(*pattern,clone_info)) == MagickFail)
5388
73
    {
5389
      /* Copy exception into base image */
5390
73
      if ((*pattern)->exception.severity > image->exception.severity)
5391
61
        CopyException(&image->exception, &(*pattern)->exception);
5392
73
    }
5393
30.4k
  DestroyDrawInfo(clone_info);
5394
30.4k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"end pattern-path");
5395
30.4k
  return(status);
5396
30.4k
}
5397

5398
/*
5399
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5400
%                                                                             %
5401
%                                                                             %
5402
%                                                                             %
5403
+   D r a w P o l y g o n P r i m i t i v e                                   %
5404
%                                                                             %
5405
%                                                                             %
5406
%                                                                             %
5407
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5408
%
5409
%  Method DrawPolygonPrimitive draws a polygon on the image.
5410
%
5411
%  The format of the DrawPolygonPrimitive method is:
5412
%
5413
%      unsigned int DrawPolygonPrimitive(Image *image,const DrawInfo *draw_info,
5414
%        const PrimitiveInfo *primitive_info)
5415
%
5416
%  A description of each parameter follows:
5417
%
5418
%    o image: The image.
5419
%
5420
%    o draw_info: The draw info.
5421
%
5422
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
5423
%
5424
%
5425
*/
5426
5427
static double
5428
GetPixelOpacity(PolygonInfo * restrict polygon_info,const double mid,
5429
                const unsigned int fill,const FillRule fill_rule,const long x,
5430
                const long y,double * restrict stroke_opacity)
5431
1.64G
{
5432
1.64G
  double
5433
1.64G
    alpha,
5434
1.64G
    beta,
5435
1.64G
    distance,
5436
1.64G
    subpath_opacity;
5437
5438
1.64G
  int
5439
1.64G
    winding_number;
5440
5441
1.64G
  register double
5442
1.64G
    dx,
5443
1.64G
    dy;
5444
5445
1.64G
  register EdgeInfo
5446
1.64G
    *p;
5447
5448
1.64G
  register const PointInfo
5449
1.64G
    *q;
5450
5451
1.64G
  register size_t
5452
1.64G
    i;
5453
5454
1.64G
  size_t
5455
1.64G
    j;
5456
5457
  /*
5458
    Compute fill & stroke opacity for this (x,y) point.
5459
  */
5460
1.64G
  *stroke_opacity=0.0;
5461
1.64G
  subpath_opacity=0.0;
5462
1.64G
  p=polygon_info->edges;
5463
6.52G
  for (j=0; j < polygon_info->number_edges; j++)
5464
5.23G
  {
5465
5.23G
    if (y <= (p->bounds.y1-mid-0.5))
5466
361M
      break;
5467
4.87G
    if (y > (p->bounds.y2+mid+0.5))
5468
4.46M
      {
5469
4.46M
        (void) DestroyEdge(polygon_info,j);
5470
4.46M
        p++;
5471
4.46M
        continue;
5472
4.46M
      }
5473
4.86G
    if (x <= (p->bounds.x1-mid-0.5))
5474
590M
      {
5475
590M
        p++;
5476
590M
        continue;
5477
590M
      }
5478
4.27G
    if (x > (p->bounds.x2+mid+0.5))
5479
960M
      {
5480
960M
        p++;
5481
960M
        continue;
5482
960M
      }
5483
12.2G
    for (i=Max(p->highwater,1); i < p->number_points; i++)
5484
10.4G
    {
5485
10.4G
      if (y <= (p->points[i-1].y-mid-0.5))
5486
1.53G
        break;
5487
8.91G
      if (y > (p->points[i].y+mid+0.5))
5488
67.9M
        continue;
5489
8.84G
      if (p->scanline != y)
5490
24.6M
        {
5491
24.6M
          p->scanline=y;
5492
24.6M
          p->highwater=i;
5493
24.6M
        }
5494
      /*
5495
        Compute distance between a point and an edge.
5496
5497
        In the comments below, let seg0, seg1 be the end points of the line
5498
        segment (edge), and let pt be (x,y).  Conceptually, consider the segment
5499
        to be a vector from seg0 to seg1, and pt to be a vector from seg0 to pt.
5500
        Let seglen be the length of the segment vector, and ptlen be the length
5501
        of the pt vector.
5502
5503
        The "if" tests below now test "beta <= 0" instead of "beta < 0", and
5504
        "beta >= alpha" instead of "beta > alpha".  This captures the case of
5505
        a zero-length segment and avoids a divide-by-zero when computing 1/alpha.
5506
      */
5507
8.84G
      q=p->points+i-1;    /* q is seg0, q+1 is seg1 */
5508
8.84G
      dx=(q+1)->x-q->x,   /* seg1.x - seg0.x */
5509
8.84G
      dy=(q+1)->y-q->y;   /* seg1.y - seg0.y */
5510
8.84G
      beta=dx*(x-q->x)+dy*(y-q->y);   /* dot(segvec,ptvec) = seglen * ptlen * costheta */
5511
8.84G
      if (beta <= 0.0)
5512
2.94G
        {/*cosine<=0, pt is closest to seg0*/
5513
2.94G
          dx=x-q->x;
5514
2.94G
          dy=y-q->y;
5515
2.94G
          distance=dx*dx+dy*dy;
5516
2.94G
        }
5517
5.89G
      else
5518
5.89G
        {
5519
5.89G
          alpha=dx*dx+dy*dy;  /*seglen*seglen*/
5520
5.89G
          if (beta >= alpha)
5521
2.99G
            {/*pt is closest to seg1*/
5522
2.99G
              dx=x-(q+1)->x;
5523
2.99G
              dy=y-(q+1)->y;
5524
2.99G
              distance=dx*dx+dy*dy;
5525
2.99G
            }
5526
2.90G
          else
5527
2.90G
            {/*pt is closest to a point between seg0 and seg1*/
5528
2.90G
              alpha=1.0/alpha;  /*don't get here if alpha==0*/
5529
2.90G
              beta=dx*(y-q->y)-dy*(x-q->x);
5530
2.90G
              distance=alpha*beta*beta;
5531
2.90G
            }
5532
5.89G
        }
5533
      /*
5534
        Compute stroke & subpath opacity.
5535
      */
5536
8.84G
      beta=0.0;
5537
8.84G
      if (!p->ghostline)
5538
8.74G
        {
5539
8.74G
          alpha=mid+0.5;
5540
8.74G
          if ((*stroke_opacity < 1.0) &&
5541
8.37G
              (distance <= ((alpha+0.25)*(alpha+0.25))))
5542
128M
            {
5543
128M
              alpha=mid-0.5;
5544
128M
              if (distance <= ((alpha+0.25)*(alpha+0.25)))
5545
78.1M
                *stroke_opacity=1.0;
5546
50.5M
              else
5547
50.5M
                {
5548
50.5M
                  beta=1.0;
5549
50.5M
                  if (distance != 1.0)
5550
48.4M
                    beta=sqrt(distance);
5551
50.5M
                  alpha=beta-mid-0.5;
5552
50.5M
                  if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
5553
32.1M
                    *stroke_opacity=(alpha-0.25)*(alpha-0.25);
5554
50.5M
                }
5555
128M
            }
5556
8.74G
        }
5557
8.84G
      if (!fill || (distance > 1.0) || (subpath_opacity >= 1.0))
5558
8.76G
        continue;
5559
77.6M
      if (distance <= 0.0)
5560
5.99M
        {
5561
5.99M
          subpath_opacity=1.0;
5562
5.99M
          continue;
5563
5.99M
        }
5564
71.6M
      if (distance > 1.0)
5565
0
        continue;
5566
71.6M
      if (beta == 0.0)
5567
31.4M
        {
5568
31.4M
          beta=1.0;
5569
31.4M
          if (distance != 1.0)
5570
29.8M
            beta=sqrt(distance);
5571
31.4M
        }
5572
71.6M
      alpha=beta-1.0;
5573
71.6M
      if (subpath_opacity < (alpha*alpha))
5574
39.8M
        subpath_opacity=alpha*alpha;
5575
71.6M
    }
5576
3.31G
    p++;
5577
3.31G
  }
5578
  /*
5579
    Compute fill opacity.
5580
  */
5581
1.64G
  if (!fill)
5582
29.5M
    return(0.0);
5583
1.62G
  if (subpath_opacity >= 1.0)
5584
6.02M
    return(1.0);
5585
  /*
5586
    Determine winding number.
5587
  */
5588
1.61G
  winding_number=0;
5589
1.61G
  p=polygon_info->edges;
5590
6.24G
  for (j=0; j < polygon_info->number_edges; j++)
5591
5.02G
  {
5592
5.02G
    if (y <= p->bounds.y1)
5593
397M
      break;
5594
4.63G
    if (y > p->bounds.y2)
5595
82.2M
      {
5596
82.2M
        p++;
5597
82.2M
        continue;
5598
82.2M
      }
5599
4.54G
    if (x <= p->bounds.x1)
5600
574M
      {
5601
574M
        p++;
5602
574M
        continue;
5603
574M
      }
5604
3.97G
    if (x > p->bounds.x2)
5605
929M
      {
5606
929M
        winding_number+=p->direction ? 1 : -1;
5607
929M
        p++;
5608
929M
        continue;
5609
929M
      }
5610
5.51G
    for (i=Max(p->highwater,1); i < p->number_points; i++)
5611
5.51G
      if (y <= p->points[i].y)
5612
3.04G
        break;
5613
3.04G
    q=p->points+i-1;
5614
3.04G
    dx=(q+1)->x-q->x;
5615
3.04G
    dy=(q+1)->y-q->y;
5616
3.04G
    if ((dx*(y-q->y)) <= (dy*(x-q->x)))
5617
1.44G
      winding_number+=p->direction ? 1 : -1;
5618
3.04G
    p++;
5619
3.04G
  }
5620
1.61G
  if (fill_rule != NonZeroRule)
5621
1.21G
    {
5622
1.21G
      if (AbsoluteValue(winding_number) & 0x01)
5623
1.07G
        return(1.0);
5624
1.21G
    }
5625
399M
  else
5626
399M
    if (AbsoluteValue(winding_number) > 0)
5627
149M
      return(1.0);
5628
391M
  return(subpath_opacity);
5629
1.61G
}
5630
5631
#if defined(HAVE_OPENMP)
5632
static PolygonInfo* ClonePolygonInfo(const PolygonInfo* orig_polygon_info, ExceptionInfo *exception)
5633
{
5634
  PolygonInfo
5635
    *polygon_info = (PolygonInfo *) NULL;
5636
5637
  size_t
5638
    edge;
5639
5640
  if (orig_polygon_info == (PolygonInfo *) NULL)
5641
    return polygon_info;
5642
5643
  if ((polygon_info=MagickAllocateMemory(PolygonInfo *,sizeof(PolygonInfo))) == (PolygonInfo *) NULL)
5644
    {
5645
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
5646
                      UnableToDrawOnImage);
5647
      return polygon_info;
5648
    }
5649
5650
  polygon_info->number_edges=0;
5651
5652
  if ((polygon_info->edges=
5653
       MagickAllocateResourceLimitedArray(EdgeInfo *,
5654
                                          orig_polygon_info->number_edges,
5655
                                          sizeof(EdgeInfo))) == (EdgeInfo *) NULL)
5656
    {
5657
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
5658
                      UnableToDrawOnImage);
5659
      DestroyPolygonInfo(polygon_info);
5660
      polygon_info=(PolygonInfo *) NULL;
5661
      return polygon_info;
5662
    }
5663
5664
  (void) memcpy(polygon_info->edges,orig_polygon_info->edges,
5665
                orig_polygon_info->number_edges*sizeof(EdgeInfo));
5666
5667
  for (edge = 0; edge < orig_polygon_info->number_edges; edge++)
5668
    polygon_info->edges[edge].points = (PointInfo *) NULL;
5669
5670
  polygon_info->number_edges=orig_polygon_info->number_edges;
5671
  for (edge = 0; edge < polygon_info->number_edges; edge++)
5672
    {
5673
      if ((polygon_info->edges[edge].points=
5674
           MagickAllocateResourceLimitedArray(PointInfo *,
5675
                                              polygon_info->edges[edge].number_points,
5676
                                              sizeof(PointInfo))) == (PointInfo *) NULL)
5677
        {
5678
          ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
5679
                          UnableToDrawOnImage);
5680
          DestroyPolygonInfo(polygon_info);
5681
          polygon_info=(PolygonInfo *) NULL;
5682
          return polygon_info;
5683
        }
5684
5685
      (void) memcpy(polygon_info->edges[edge].points,orig_polygon_info->edges[edge].points,
5686
                    polygon_info->edges[edge].number_points*sizeof(PointInfo));
5687
    }
5688
5689
  return polygon_info;
5690
}
5691
#endif /* if defined(HAVE_OPENMP) */
5692
5693
static MagickPassFail
5694
DrawPolygonPrimitive(Image *image,const DrawInfo *draw_info,
5695
                     const PrimitiveInfo *primitive_info)
5696
3.72M
{
5697
3.72M
  double
5698
3.72M
    mid,
5699
3.72M
    stroke_width_limited;
5700
5701
3.72M
  SegmentInfo
5702
3.72M
    bounds;
5703
5704
3.72M
  ThreadViewDataSet
5705
3.72M
    * restrict polygon_set = (ThreadViewDataSet *) NULL;
5706
5707
3.72M
  magick_uint64_t
5708
3.72M
    total_pixels;
5709
5710
3.72M
  MagickPassFail
5711
3.72M
    status = MagickPass;
5712
5713
3.72M
  int
5714
3.72M
    max_threads,
5715
3.72M
    num_threads;
5716
5717
3.72M
  assert(image != (Image *) NULL);
5718
3.72M
  assert(image->signature == MagickSignature);
5719
3.72M
  assert(draw_info != (DrawInfo *) NULL);
5720
3.72M
  assert(draw_info->signature == MagickSignature);
5721
3.72M
  assert(primitive_info != (PrimitiveInfo *) NULL);
5722
  /*
5723
    Nothing to do.
5724
  */
5725
3.72M
  if (primitive_info->coordinates <= 1)   /*single point polygons have zero area; don't draw*/
5726
34.5k
    return(MagickPass);
5727
5728
  /*
5729
    Determine the number of threads to use.
5730
  */
5731
3.69M
  total_pixels = ((magick_uint64_t) image->rows * image->columns);
5732
5733
3.69M
  max_threads=omp_get_max_threads();
5734
3.69M
  num_threads=max_threads;
5735
5736
3.69M
  if (total_pixels < 250000UL)
5737
2.85M
    num_threads=Min(num_threads,1);
5738
841k
  else if (total_pixels < 1000000UL)
5739
807k
    num_threads=Min(num_threads,4);
5740
33.4k
  else if (total_pixels < 9000000UL)
5741
33.4k
    num_threads=Min(num_threads,8);
5742
5743
  /* fprintf(stderr,"Using %d threads\n", num_threads); */
5744
5745
3.69M
  {
5746
    /*
5747
      Allocate and initialize thread-specific polygon sets.
5748
5749
      There are thread-specific sets because GetPixelOpacity modifies
5750
      Edges (via DestroyEdge() and modifies Edge scanline and
5751
      highwater)
5752
    */
5753
3.69M
    PathInfo
5754
3.69M
      * restrict path_info;
5755
5756
3.69M
    unsigned int
5757
3.69M
      index;
5758
5759
3.69M
    path_info=(PathInfo *) NULL;
5760
3.69M
    if ((path_info=ConvertPrimitiveToPath(draw_info,primitive_info,&image->exception))
5761
3.69M
        != (PathInfo *) NULL)
5762
3.69M
      {
5763
3.69M
        if ((polygon_set=AllocateThreadViewDataSet(DestroyPolygonInfo,image,
5764
3.69M
                                                   &image->exception))
5765
3.69M
            != (ThreadViewDataSet *) NULL)
5766
3.69M
          {
5767
3.69M
            unsigned int
5768
3.69M
              allocated_views;
5769
5770
3.69M
            PolygonInfo
5771
3.69M
              *polygon_info;
5772
5773
3.69M
            allocated_views=GetThreadViewDataSetAllocatedViews(polygon_set);
5774
3.69M
            if ((int) allocated_views > num_threads)
5775
0
              allocated_views=num_threads;
5776
5777
            /*
5778
              Assign polygon for each worker thread.
5779
5780
              Only the first polygon set needs to be from
5781
              ConvertPathToPolygon() since they start off the same.
5782
              Otherwise an optimized clone can be used instead.
5783
5784
              To that end, we have now written ClonePolygonInfo()
5785
5786
              FIXME: A better solution would be to figure out how
5787
              threads can share common data while adding minimal
5788
              locking.  The current algorithms do unfriendly things
5789
              such as compacting the structure using memmove().
5790
            */
5791
3.69M
            polygon_info=ConvertPathToPolygon(path_info,&image->exception);
5792
7.39M
            for (index=0; index < allocated_views; index++)
5793
#if defined(HAVE_OPENMP)
5794
            AssignThreadViewData(polygon_set,index,index == 0 ? (void *) polygon_info :
5795
                                 ClonePolygonInfo(polygon_info,&image->exception));
5796
#else
5797
3.69M
            AssignThreadViewData(polygon_set,index,(void *) polygon_info);
5798
3.69M
#endif /* if defined(HAVE_OPENMP) */
5799
5800
            /*
5801
              Verify worker thread allocations.
5802
            */
5803
7.39M
            for (index=0; index < allocated_views; index++)
5804
3.69M
              if (AccessThreadViewDataById(polygon_set,index) == (void *) NULL)
5805
151
                {
5806
151
                  DestroyThreadViewDataSet(polygon_set);
5807
151
                  polygon_set=(ThreadViewDataSet *) NULL;
5808
151
                  break;
5809
151
                }
5810
3.69M
          }
5811
5812
3.69M
        MagickFreeResourceLimitedMemory(PathInfo *,path_info);
5813
3.69M
      }
5814
3.69M
    if (polygon_set == (ThreadViewDataSet *) NULL)
5815
206
      {
5816
206
        ThrowException3(&image->exception,ResourceLimitError,MemoryAllocationFailed,
5817
206
                        UnableToDrawOnImage);
5818
206
        return MagickFail;
5819
206
      }
5820
3.69M
  }
5821
5822
  /*
5823
    Compute bounding box.
5824
  */
5825
3.69M
  {
5826
3.69M
    const PolygonInfo
5827
3.69M
      * restrict polygon_info;
5828
5829
3.69M
    register size_t
5830
3.69M
      i;
5831
5832
3.69M
    polygon_info=(const PolygonInfo *) AccessThreadViewData(polygon_set);
5833
3.69M
    bounds=polygon_info->edges[0].bounds;
5834
5835
3.69M
    if (0) /* DEBUG ??? */
5836
0
      if ((status=DrawBoundingRectangles(image,draw_info,polygon_info)) == MagickFail)
5837
0
        goto draw_polygon_primitive_end;
5838
5839
28.9M
    for (i=1; i < polygon_info->number_edges; i++)
5840
25.2M
      {
5841
25.2M
        register const EdgeInfo
5842
25.2M
          *p;
5843
5844
25.2M
        p=polygon_info->edges+i;
5845
25.2M
        if (p->bounds.x1 < bounds.x1)
5846
1.02M
          bounds.x1=p->bounds.x1;
5847
25.2M
        if (p->bounds.y1 < bounds.y1)
5848
7.84k
          bounds.y1=p->bounds.y1;
5849
25.2M
        if (p->bounds.x2 > bounds.x2)
5850
3.91M
          bounds.x2=p->bounds.x2;
5851
25.2M
        if (p->bounds.y2 > bounds.y2)
5852
2.01M
          bounds.y2=p->bounds.y2;
5853
25.2M
      }
5854
    /*
5855
      Modified clipping below.  Previous code was (similar for y):
5856
5857
        if (x >= columns) x = columns-1;
5858
5859
      Note that if columns == 50 this results in:
5860
5861
        x==49.999 --> x=49.999
5862
        x==50.000 --> x=49.000
5863
5864
      Pretty sure this was not the intention of the original
5865
      author.  Also, this can result in x_start > x_end, which
5866
      can cause the call to GetImagePixelsEx() to blow up due to
5867
      a column count <= 0 (see code below).
5868
5869
      Changed the code to first check for starting bounds beyond
5870
      right/bottom of image, and ending bounds beyond left/top
5871
      of image.  When this is the case, there is nothing to do,
5872
      since the object lies completely outside the image.  If
5873
      not, clip based on columns-1 and rows-1 instead of columns
5874
      and rows.
5875
    */
5876
5877
    /* sanity check for excessively big stroke_width (ticket #515) */
5878
3.69M
    if  ( (stroke_width_limited = STROKE_WIDTH_LIMIT(image)) > draw_info->stroke_width )
5879
3.69M
        stroke_width_limited = draw_info->stroke_width;
5880
5881
3.69M
    mid=ExpandAffine(&draw_info->affine)*stroke_width_limited/2.0;
5882
3.69M
    bounds.x1-=(mid+1.0);
5883
3.69M
    bounds.y1-=(mid+1.0);
5884
3.69M
    bounds.x2+=(mid+1.0);
5885
3.69M
    bounds.y2+=(mid+1.0);
5886
3.69M
    if ( (bounds.x1 >= image->columns) || (bounds.y1 >= image->rows)
5887
1.63M
         || (bounds.x2 <= 0.0) || (bounds.y2 <= 0.0) )
5888
2.62M
      {
5889
        /* object completely outside image */
5890
2.62M
        DestroyThreadViewDataSet(polygon_set);
5891
2.62M
        polygon_set = (ThreadViewDataSet *) NULL;
5892
2.62M
        return(MagickPass);
5893
2.62M
      }
5894
1.07M
    bounds.x1=bounds.x1 <= 0.0 ? 0.0 : bounds.x1 >= image->columns-1 ?
5895
940k
      image->columns-1 : bounds.x1;
5896
1.07M
    bounds.y1=bounds.y1 <= 0.0 ? 0.0 : bounds.y1 >= image->rows-1 ?
5897
918k
      image->rows-1 : bounds.y1;
5898
1.07M
    bounds.x2=bounds.x2 <= 0.0 ? 0.0 : bounds.x2 >= image->columns-1 ?
5899
998k
      image->columns-1 : bounds.x2;
5900
1.07M
    bounds.y2=bounds.y2 <= 0.0 ? 0.0 : bounds.y2 >= image->rows-1 ?
5901
998k
      image->rows-1 : bounds.y2;
5902
1.07M
  }
5903
5904
1.07M
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    begin draw-polygon");
5905
5906
1.07M
  if (primitive_info->coordinates == 1)
5907
0
    {
5908
      /*
5909
        Draw point.
5910
      */
5911
0
      long
5912
0
        x_start,
5913
0
        x_stop,
5914
0
        y_start,
5915
0
        y_stop,
5916
0
        y;
5917
5918
0
      PixelPacket
5919
0
        stroke_color;
5920
5921
0
      stroke_color=draw_info->stroke;
5922
0
      x_start=(long) ceil(bounds.x1-0.5);   /* rounds n.5 to n */
5923
0
      x_stop=(long) floor(bounds.x2+0.5);   /* rounds n.5 to n+1 */
5924
0
      y_start=(long) ceil(bounds.y1-0.5);
5925
0
      y_stop=(long) floor(bounds.y2+0.5);
5926
5927
#if defined(HAVE_OPENMP)
5928
#  if defined(TUNE_OPENMP)
5929
#    pragma omp parallel for schedule(runtime) shared(status)
5930
#  else
5931
#    pragma omp parallel for num_threads(2) schedule(guided) shared(status)
5932
#  endif
5933
#endif
5934
0
      for (y=y_start; y <= y_stop; y++)
5935
0
        {
5936
0
          long
5937
0
            x;
5938
5939
0
          PixelPacket
5940
0
            * restrict q;
5941
5942
0
          MagickPassFail
5943
0
            thread_status;
5944
5945
0
          thread_status=status;
5946
0
          if (thread_status == MagickFail)
5947
0
            continue;
5948
5949
0
          x=x_start;
5950
0
          q=GetImagePixelsEx(image,x,y,x_stop-x+1,1,&image->exception);
5951
0
          if (q == (PixelPacket *) NULL)
5952
0
            thread_status=MagickFail;
5953
0
          if (thread_status != MagickFail)
5954
0
            {
5955
0
              for ( ; x <= x_stop; x++)
5956
0
                {
5957
0
                  if ((x == (long) ceil(primitive_info->point.x-0.5)) &&
5958
0
                      (y == (long) ceil(primitive_info->point.y-0.5)))
5959
0
                    *q=stroke_color;
5960
0
                  q++;
5961
0
                }
5962
0
              if (!SyncImagePixelsEx(image,&image->exception))
5963
0
                thread_status=MagickFail;
5964
0
            }
5965
0
          if (thread_status == MagickFail)
5966
0
            {
5967
0
              status=thread_status;
5968
#if defined(HAVE_OPENMP)
5969
#  pragma omp flush (status)
5970
#endif
5971
0
            }
5972
0
        }
5973
0
    } /* if (primitive_info->coordinates == 1) */
5974
1.07M
  else
5975
1.07M
    {
5976
      /*
5977
        Draw polygon or line.
5978
      */
5979
1.07M
      long
5980
1.07M
        x_start,
5981
1.07M
        x_stop,
5982
1.07M
        y_start,
5983
1.07M
        y_stop,
5984
1.07M
        y;
5985
5986
1.07M
      const Image
5987
1.07M
        *fill_pattern=draw_info->fill_pattern,
5988
1.07M
        *stroke_pattern=draw_info->stroke_pattern;
5989
5990
1.07M
      unsigned int
5991
1.07M
        fill;
5992
5993
1.07M
      fill=(primitive_info->method == FillToBorderMethod) ||
5994
1.05M
        (primitive_info->method == FloodfillMethod);
5995
5996
1.07M
      x_start=(long) ceil(bounds.x1-0.5); /* FIXME: validate */  /* rounds n.5 to n */
5997
1.07M
      x_stop=(long) floor(bounds.x2+0.5); /* FIXME: validate */  /* rounds n.5 to n+1 */
5998
1.07M
      y_start=(long) ceil(bounds.y1-0.5); /* FIXME: validate */
5999
1.07M
      y_stop=(long) floor(bounds.y2+0.5); /* FIXME: validate */
6000
6001
1.07M
#if 1
6002
#if defined(HAVE_OPENMP)
6003
#  if defined(TUNE_OPENMP)
6004
#    pragma omp parallel for schedule(runtime) shared(status)
6005
#  else
6006
#    pragma omp parallel for num_threads(num_threads) schedule(dynamic) shared(status)
6007
#  endif
6008
#endif
6009
1.07M
#endif
6010
12.4M
      for (y=y_start; y <= y_stop; y++)
6011
11.3M
        {
6012
11.3M
          PixelPacket
6013
11.3M
            fill_color,
6014
11.3M
            stroke_color;
6015
6016
11.3M
          double
6017
11.3M
            fill_opacity,
6018
11.3M
            stroke_opacity;
6019
6020
11.3M
          long
6021
11.3M
            x;
6022
6023
11.3M
          PolygonInfo
6024
11.3M
            * restrict polygon_info;
6025
6026
11.3M
          PixelPacket
6027
11.3M
            * restrict q;
6028
6029
11.3M
          MagickPassFail
6030
11.3M
            thread_status;
6031
6032
11.3M
          thread_status=status;
6033
11.3M
          if (thread_status == MagickFail)
6034
93
            continue;
6035
6036
11.3M
          polygon_info=(PolygonInfo *) AccessThreadViewData(polygon_set);
6037
11.3M
          fill_color=draw_info->fill;
6038
11.3M
          stroke_color=draw_info->stroke;
6039
11.3M
          x=x_start;
6040
11.3M
          q=GetImagePixelsEx(image,x,y,x_stop-x+1,1,&image->exception);
6041
11.3M
          if (q == (PixelPacket *) NULL)
6042
9
            thread_status=MagickFail;
6043
6044
11.3M
          if (thread_status != MagickFail)
6045
11.3M
            {
6046
1.66G
              for ( ; x <= x_stop; x++)
6047
1.64G
                {
6048
                  /*
6049
                    Fill and/or stroke.  The fill_opacity returned by GetPixelOpacity()
6050
                    handles partial pixel coverage at the edge of a polygon, where
6051
                    0==no coverage and 1==full coverage
6052
6053
                    GetPixelOpacity() modifies some properties referenced by polygon_info.
6054
                  */
6055
1.64G
                  fill_opacity=GetPixelOpacity(polygon_info,mid,fill,
6056
1.64G
                                               draw_info->fill_rule,
6057
1.64G
                                               x,y,&stroke_opacity);
6058
1.64G
                  if (!draw_info->stroke_antialias)
6059
219M
                    {
6060
                      /* When stroke antialiasing is disabled, only draw for
6061
                         opacities >= 0.99 in order to ensure that lines are not
6062
                         drawn wider than requested. */
6063
219M
                      if (fill_opacity < 0.99)
6064
192M
                        fill_opacity=0.0;
6065
219M
                      if (stroke_opacity < 0.99)
6066
193M
                        stroke_opacity=0.0;
6067
219M
                    }
6068
1.64G
                  if ((fill_pattern != (Image *) NULL) &&
6069
6.34M
                      (fill_pattern->columns != 0) &&
6070
6.34M
                      (fill_pattern->rows != 0))
6071
6.34M
                    {
6072
6.34M
                      (void) AcquireOnePixelByReference
6073
6.34M
                        (fill_pattern,&fill_color,
6074
6.34M
                        (long) (x-fill_pattern->tile_info.x) % fill_pattern->columns,
6075
6.34M
                        (long) (y-fill_pattern->tile_info.y) % fill_pattern->rows,
6076
6.34M
                        &image->exception);
6077
                      /* apply the group opacity value to the pattern pixel */
6078
6.34M
                      fill_color.opacity = MaxRGB-((MaxRGB-fill_color.opacity)*(MaxRGB-draw_info->opacity)+(MaxRGB>>1))/MaxRGB;
6079
6.34M
                    }
6080
                  /* combine fill_opacity with the fill color's opacity */
6081
1.64G
                  fill_opacity=MaxRGBDouble-fill_opacity*
6082
1.64G
                    (MaxRGBDouble-(double) fill_color.opacity);
6083
                  /*
6084
                    Notes on call to AlphaCompositePixel():
6085
6086
                      fill_color: the polygon or pattern fill color, not premultiplied
6087
                        by its opacity value
6088
                      fill_opacity: product of the fill color opacity and opacity due
6089
                        to partial pixel coverage (e.g., at the edge of the polygon)
6090
                      q: (input) the background pixel, (output) the composited pixel;
6091
                        neither is premultiplied by its opacity value
6092
                      q->opacity: the background pixel opacity
6093
6094
                    The previous version of this code substituted "OpaqueOpacity"
6095
                    for q->opacity if q->opacity was transparent.  I think this was
6096
                    originally done to avoid a divide-by-zero in AlphaCompositePixel().
6097
                    However, this substitution results in an incorrect result if the
6098
                    background pixel is completely transparent.  Since the current
6099
                    version of AlphaCompositePixel() has code in it to prevent a
6100
                    divide-by-zero, the code has been fixed to always use q->opacity
6101
                    as the background pixel opacity.
6102
                  */
6103
1.64G
                  AlphaCompositePixel(q,&fill_color,fill_opacity,q,q->opacity);
6104
1.64G
                  if ((stroke_pattern != (Image *) NULL) &&
6105
2.20M
                      (stroke_pattern->columns != 0) &&
6106
2.20M
                      (stroke_pattern->rows != 0))
6107
2.20M
                    {
6108
2.20M
                      (void) AcquireOnePixelByReference
6109
2.20M
                        (stroke_pattern,&stroke_color,
6110
2.20M
                        (long) (x-stroke_pattern->tile_info.x) % stroke_pattern->columns,
6111
2.20M
                        (long) (y-stroke_pattern->tile_info.y) % stroke_pattern->rows,
6112
2.20M
                        &image->exception);
6113
                      /* apply the group opacity value to the pattern pixel */
6114
2.20M
                      stroke_color.opacity = MaxRGB-((MaxRGB-stroke_color.opacity)*(MaxRGB-draw_info->opacity)+(MaxRGB>>1))/MaxRGB;
6115
2.20M
                    }
6116
1.64G
                  stroke_opacity=MaxRGBDouble-stroke_opacity*
6117
1.64G
                    (MaxRGBDouble-(double)stroke_color.opacity);
6118
                  /*
6119
                    In the call to AlphaCompositePixel() below, q->opacity is now always
6120
                    used as the background pixel opacity for the same reason as described
6121
                    in the call to AlphaCompositePixel() above.
6122
                  */
6123
1.64G
                  AlphaCompositePixel(q,&stroke_color,stroke_opacity,q,q->opacity);
6124
1.64G
                  q++;
6125
1.64G
                } /* for ( ; x <= x_stop; x++) */
6126
11.3M
              if (!SyncImagePixelsEx(image,&image->exception))
6127
0
                thread_status=MagickFail;
6128
6129
11.3M
            } /* if (thread_status != MagickFail) */
6130
11.3M
          if (thread_status == MagickFail)
6131
9
            {
6132
9
              status=thread_status;
6133
#if defined(HAVE_OPENMP)
6134
#  pragma omp flush (status)
6135
#endif
6136
9
            }
6137
11.3M
        } /* for (y=y_start */
6138
1.07M
    }
6139
1.07M
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end draw-polygon");
6140
6141
1.07M
 draw_polygon_primitive_end:;
6142
6143
1.07M
  DestroyThreadViewDataSet(polygon_set);
6144
6145
1.07M
  return(status);
6146
1.07M
}
6147

6148
/*
6149
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6150
%                                                                             %
6151
%                                                                             %
6152
%                                                                             %
6153
+   D r a w P r i m i t i v e                                                 %
6154
%                                                                             %
6155
%                                                                             %
6156
%                                                                             %
6157
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6158
%
6159
%  Method DrawPrimitive draws a primitive (line, rectangle, ellipse) on the
6160
%  image.
6161
%
6162
%  The format of the DrawPrimitive method is:
6163
%
6164
%      void DrawPrimitive(Image *image,const DrawInfo *draw_info,
6165
%        PrimitiveInfo *primitive_info)
6166
%
6167
%  A description of each parameter follows:
6168
%
6169
%    o image: The image.
6170
%
6171
%    o draw_info: The draw info.
6172
%
6173
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
6174
%
6175
%
6176
*/
6177
6178
static void
6179
LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
6180
0
{
6181
0
  char
6182
0
    *methods[] =
6183
0
    {
6184
0
      (char *) "point",
6185
0
      (char *) "replace",
6186
0
      (char *) "floodfill",
6187
0
      (char *) "filltoborder",
6188
0
      (char *) "reset",
6189
0
      (char *) "?"
6190
0
    };
6191
6192
0
  long
6193
0
    coordinates,
6194
0
    y;
6195
6196
0
  PointInfo
6197
0
    p={0,0},
6198
0
    q,
6199
0
    point;
6200
6201
0
  register long
6202
0
    i,
6203
0
    x;
6204
6205
0
  x=(long) ceil(primitive_info->point.x-0.5);
6206
0
  y=(long) ceil(primitive_info->point.y-0.5);
6207
0
  switch (primitive_info->primitive)
6208
0
  {
6209
0
    case PointPrimitive:
6210
0
    {
6211
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6212
0
        "PointPrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
6213
0
      return;
6214
0
    }
6215
0
    case ColorPrimitive:
6216
0
    {
6217
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6218
0
        "ColorPrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
6219
0
      return;
6220
0
    }
6221
0
    case MattePrimitive:
6222
0
    {
6223
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6224
0
        "MattePrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
6225
0
      return;
6226
0
    }
6227
0
    case TextPrimitive:
6228
0
    {
6229
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6230
0
        "TextPrimitive %ld,%ld",x,y);
6231
0
      return;
6232
0
    }
6233
0
    case ImagePrimitive:
6234
0
    {
6235
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6236
0
        "ImagePrimitive %ld,%ld",x,y);
6237
0
      return;
6238
0
    }
6239
0
    default:
6240
0
      break;
6241
0
  }
6242
0
  coordinates=0;
6243
0
  q.x=(-1.0);
6244
0
  q.y=(-1.0);
6245
0
  for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
6246
0
  {
6247
0
    point=primitive_info[i].point;
6248
0
    if (coordinates <= 0)
6249
0
      {
6250
0
        coordinates=(long) primitive_info[i].coordinates;
6251
0
        (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6252
0
          "    begin open (%ld)",coordinates);
6253
0
        p=point;
6254
0
      }
6255
0
    point=primitive_info[i].point;
6256
0
    if ((fabs(q.x-point.x) > MagickEpsilon) ||
6257
0
        (fabs(q.y-point.y) > MagickEpsilon))
6258
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"      %ld: %g,%g",
6259
0
        coordinates,point.x,point.y);
6260
0
    else
6261
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6262
0
        "      %ld: %g,%g (duplicate)",coordinates,point.x,point.y);
6263
0
    q=point;
6264
0
    coordinates--;
6265
0
    if (coordinates > 0)
6266
0
      continue;
6267
0
    if ((fabs(p.x-point.x) > MagickEpsilon) ||
6268
0
        (fabs(p.y-point.y) > MagickEpsilon))
6269
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end last (%ld)",
6270
0
        coordinates);
6271
0
    else
6272
0
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"    end open (%ld)",
6273
0
        coordinates);
6274
0
  }
6275
0
}
6276
6277
static MagickPassFail
6278
DrawPrimitive(Image *image,const DrawInfo *draw_info,
6279
              const PrimitiveInfo *primitive_info)
6280
241k
{
6281
241k
  long
6282
241k
    y;
6283
6284
241k
  MonitorHandler
6285
241k
    handler;
6286
6287
241k
  double
6288
241k
    dvalue;
6289
6290
241k
  register long
6291
241k
    i,
6292
241k
    x;
6293
6294
241k
  register PixelPacket
6295
241k
    *q;
6296
6297
241k
  MagickPassFail
6298
241k
    status=MagickPass;
6299
6300
241k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"  begin draw-primitive");
6301
241k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6302
241k
                        "    affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,draw_info->affine.rx,
6303
241k
                        draw_info->affine.ry,draw_info->affine.sy,draw_info->affine.tx,
6304
241k
                        draw_info->affine.ty);
6305
241k
  status &= ModifyCache(image,&image->exception);
6306
241k
  if (status == MagickFail)
6307
590
    {
6308
590
      (void) LogMagickEvent(RenderEvent,GetMagickModule(),"  end draw-primitive");
6309
590
      return status;
6310
590
    }
6311
241k
  dvalue=ceil(primitive_info->point.x-0.5);
6312
241k
  if (MAGICK_ISNAN(dvalue) || ((dvalue < DTOLONG_MIN)) || (dvalue > DTOLONG_MAX))
6313
100
    {
6314
100
      char double_str[MaxTextExtent];
6315
100
      MagickFormatString(double_str,sizeof(double_str),"%g",dvalue);
6316
100
      ThrowException(&image->exception,DrawError,PrimitiveArithmeticOverflow,double_str);
6317
100
      return MagickFail;
6318
100
    }
6319
241k
  x=(long) dvalue;
6320
241k
  dvalue=ceil(primitive_info->point.y-0.5);
6321
241k
  if (MAGICK_ISNAN(dvalue) || ((dvalue < DTOLONG_MIN)) || (dvalue > DTOLONG_MAX))
6322
59
    {
6323
59
      char double_str[MaxTextExtent];
6324
59
      MagickFormatString(double_str,sizeof(double_str),"%g",dvalue);
6325
59
      ThrowException(&image->exception,DrawError,PrimitiveArithmeticOverflow,double_str);
6326
59
      return MagickFail;
6327
59
    }
6328
241k
  y=(long) dvalue;
6329
241k
  switch (primitive_info->primitive)
6330
241k
    {
6331
33.3k
    case PointPrimitive:
6332
33.3k
      {
6333
33.3k
        if ((q=GetImagePixels(image,x,y,1,1)) != (PixelPacket *) NULL)
6334
9.75k
          {
6335
9.75k
            *q=draw_info->fill;
6336
9.75k
            status&=SyncImagePixels(image);
6337
9.75k
          }
6338
33.3k
        break;
6339
0
      }
6340
10.5k
    case ColorPrimitive:
6341
10.5k
      {
6342
10.5k
        switch (primitive_info->method)
6343
10.5k
          {
6344
37
          case PointMethod:
6345
37
          default:
6346
37
            {
6347
37
              if ((q=GetImagePixels(image,x,y,1,1)) != (PixelPacket *) NULL)
6348
19
                {
6349
19
                  *q=draw_info->fill;
6350
19
                  status&=SyncImagePixels(image);
6351
19
                }
6352
37
              break;
6353
37
            }
6354
4.87k
          case ReplaceMethod:
6355
4.87k
            {
6356
4.87k
              Image
6357
4.87k
                *pattern;
6358
6359
4.87k
              PixelPacket
6360
4.87k
                color,
6361
4.87k
                target;
6362
6363
4.87k
              color=draw_info->fill;
6364
4.87k
              if (AcquireOnePixelByReference(image,&target,x,y,
6365
4.87k
                                             &image->exception) == MagickFail)
6366
835
                break;
6367
4.04k
              pattern=draw_info->fill_pattern;
6368
82.0k
              for (y=0; y < (long) image->rows; y++)
6369
77.9k
                {
6370
77.9k
                  q=GetImagePixels(image,0,y,image->columns,1);
6371
77.9k
                  if (q == (PixelPacket *) NULL)
6372
0
                    break;
6373
8.79M
                  for (x=0; x < (long) image->columns; x++)
6374
8.71M
                    {
6375
8.71M
                      if (!FuzzyColorMatch(q,&target,image->fuzz))
6376
197k
                        {
6377
197k
                          q++;
6378
197k
                          continue;
6379
197k
                        }
6380
8.52M
                      if (pattern != (Image *) NULL)
6381
5.58k
                        {
6382
5.58k
                          if (AcquireOnePixelByReference(pattern,&color,
6383
5.58k
                                                         (long) (x-pattern->tile_info.x) % pattern->columns,
6384
5.58k
                                                         (long) (y-pattern->tile_info.y) % pattern->rows,
6385
5.58k
                                                         &image->exception) == MagickFail)
6386
0
                            break;
6387
5.58k
                          if (!pattern->matte)
6388
0
                            color.opacity=OpaqueOpacity;
6389
5.58k
                        }
6390
8.52M
                      if (color.opacity != TransparentOpacity)
6391
8.51M
                        AlphaCompositePixel(q,&color,color.opacity,q,q->opacity);
6392
8.52M
                      q++;
6393
8.52M
                    }
6394
77.9k
                  status&=SyncImagePixels(image);
6395
77.9k
                  if (status == MagickFail)
6396
0
                    break;
6397
77.9k
                }
6398
4.04k
              break;
6399
4.87k
            }
6400
4.90k
          case FloodfillMethod:
6401
5.54k
          case FillToBorderMethod:
6402
5.54k
            {
6403
5.54k
              PixelPacket
6404
5.54k
                border_color,
6405
5.54k
                target;
6406
6407
5.54k
              if (AcquireOnePixelByReference(image,&target,x,y,
6408
5.54k
                                             &image->exception) == MagickFail)
6409
3.03k
                break;
6410
2.51k
              if (primitive_info->method == FillToBorderMethod)
6411
638
                {
6412
638
                  border_color=draw_info->border_color;
6413
638
                  target=border_color;
6414
638
                }
6415
2.51k
              status&=ColorFloodfillImage(image,draw_info,target,x,y,
6416
2.51k
                                          primitive_info->method);
6417
2.51k
              break;
6418
5.54k
            }
6419
76
          case ResetMethod:
6420
76
            {
6421
4.09k
              for (y=0; y < (long) image->rows; y++)
6422
4.01k
                {
6423
4.01k
                  q=GetImagePixels(image,0,y,image->columns,1);
6424
4.01k
                  if (q == (PixelPacket *) NULL)
6425
0
                    break;
6426
13.5k
                  for (x=0; x < (long) image->columns; x++)
6427
9.53k
                    {
6428
9.53k
                      *q=draw_info->fill;
6429
9.53k
                      q++;
6430
9.53k
                    }
6431
4.01k
                  status&=SyncImagePixels(image);
6432
4.01k
                  if (status == MagickFail)
6433
0
                    break;
6434
4.01k
                }
6435
76
              break;
6436
5.54k
            }
6437
10.5k
          }
6438
10.5k
        break;
6439
10.5k
      }
6440
10.5k
    case MattePrimitive:
6441
5.55k
      {
6442
5.55k
        if (!image->matte)
6443
575
          SetImageOpacity(image,OpaqueOpacity);
6444
5.55k
        switch (primitive_info->method)
6445
5.55k
          {
6446
1.20k
          case PointMethod:
6447
1.20k
          default:
6448
1.20k
            {
6449
1.20k
              q=GetImagePixels(image,x,y,1,1);
6450
1.20k
              if (q == (PixelPacket *) NULL)
6451
663
                break;
6452
543
              q->opacity=TransparentOpacity;
6453
543
              status&=SyncImagePixels(image);
6454
543
              break;
6455
1.20k
            }
6456
1.65k
          case ReplaceMethod:
6457
1.65k
            {
6458
1.65k
              PixelPacket
6459
1.65k
                target;
6460
6461
1.65k
              if (AcquireOnePixelByReference(image,&target,x,y,
6462
1.65k
                                             &image->exception) == MagickFail)
6463
333
                break;
6464
1.32k
              status&=TransparentImage(image,target,TransparentOpacity);
6465
1.32k
              break;
6466
1.65k
            }
6467
2.40k
          case FloodfillMethod:
6468
2.46k
          case FillToBorderMethod:
6469
2.46k
            {
6470
2.46k
              PixelPacket
6471
2.46k
                border_color,
6472
2.46k
                target;
6473
6474
2.46k
              if (AcquireOnePixelByReference(image,&target,x,y,
6475
2.46k
                                             &image->exception) == MagickFail)
6476
783
                break;
6477
1.68k
              if (primitive_info->method == FillToBorderMethod)
6478
61
                {
6479
61
                  border_color=draw_info->border_color;
6480
61
                  target=border_color;
6481
61
                }
6482
1.68k
              status&=MatteFloodfillImage(image,target,TransparentOpacity,x,y,
6483
1.68k
                                          primitive_info->method);
6484
1.68k
              break;
6485
2.46k
            }
6486
227
          case ResetMethod:
6487
227
            {
6488
167k
              for (y=0; y < (long) image->rows; y++)
6489
167k
                {
6490
167k
                  q=GetImagePixels(image,0,y,image->columns,1);
6491
167k
                  if (q == (PixelPacket *) NULL)
6492
0
                    break;
6493
1.00M
                  for (x=0; x < (long) image->columns; x++)
6494
835k
                    {
6495
835k
                      q->opacity=draw_info->fill.opacity;
6496
835k
                      q++;
6497
835k
                    }
6498
167k
                  status&=SyncImagePixels(image);
6499
167k
                  if (status == MagickFail)
6500
0
                    break;
6501
167k
                }
6502
227
              break;
6503
2.46k
            }
6504
5.55k
          }
6505
5.55k
        break;
6506
5.55k
      }
6507
25.1k
    case TextPrimitive:
6508
25.1k
      {
6509
25.1k
        char
6510
25.1k
          geometry[MaxTextExtent];
6511
6512
25.1k
        DrawInfo
6513
25.1k
          *clone_info;
6514
6515
25.1k
        if (primitive_info->text == (char *) NULL)
6516
0
          break;
6517
25.1k
        clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6518
25.1k
        (void) CloneString(&clone_info->text,primitive_info->text);
6519
25.1k
        MagickFormatString(geometry,sizeof(geometry),"%+g%+g",primitive_info->point.x,
6520
25.1k
                           primitive_info->point.y);
6521
25.1k
        (void) CloneString(&clone_info->geometry,geometry);
6522
        /*
6523
          FIXME: AnnotateImage sometimes returns error status here
6524
          without throwing exception and under conditions which should
6525
          be ok when rendering (e.g. off-canvas drawing).
6526
6527
          AnnotateImage() recurses back to DrawImage().
6528
          DrawImage--> DrawPrimitive() --> AnnotateImage() --> DrawImage
6529
        */
6530
25.1k
        status&=AnnotateImage(image,clone_info);
6531
25.1k
        DestroyDrawInfo(clone_info);
6532
25.1k
        break;
6533
25.1k
      }
6534
32.8k
    case ImagePrimitive:
6535
32.8k
      {
6536
32.8k
        AffineMatrix
6537
32.8k
          affine;
6538
6539
32.8k
        Image
6540
32.8k
          *composite_image=(Image *) NULL;
6541
6542
32.8k
        ImageInfo
6543
32.8k
          *clone_info;
6544
6545
32.8k
        if (primitive_info->text == (char *) NULL)
6546
0
          break;
6547
32.8k
        clone_info=CloneImageInfo((ImageInfo *) NULL);
6548
32.8k
        clone_info->subimage=0;
6549
32.8k
        clone_info->subrange=1;
6550
32.8k
        if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
6551
32.7k
          {
6552
32.7k
            composite_image=ReadInlineImage(clone_info,primitive_info->text,
6553
32.7k
                                            &image->exception);
6554
32.7k
          }
6555
122
        else
6556
122
          {
6557
            /*
6558
              Sanity check URL/path before passing it to ReadImage()
6559
6560
              This is a temporary fix until suitable flags can be passed
6561
              to keep SetImageInfo() from doing potentially dangerous
6562
              magick things.
6563
            */
6564
732
#define VALID_PREFIX(str,url) (LocaleNCompare(str,url,sizeof(str)-1) == 0)
6565
122
            if (!VALID_PREFIX("http://", primitive_info->text) &&
6566
122
                !VALID_PREFIX("https://", primitive_info->text) &&
6567
122
                !VALID_PREFIX("ftp://", primitive_info->text)  &&
6568
122
                !(IsAccessibleNoLogging(primitive_info->text))
6569
122
                )
6570
97
              {
6571
97
                ThrowException(&image->exception,FileOpenError,UnableToOpenFile,primitive_info->text);
6572
97
                status=MagickFail;
6573
97
              }
6574
25
            else
6575
25
              {
6576
25
                (void) strlcpy(clone_info->filename,primitive_info->text,
6577
25
                               MaxTextExtent);
6578
25
                composite_image=ReadImage(clone_info,&image->exception);
6579
25
              }
6580
122
          }
6581
32.8k
        if (image->exception.severity != UndefinedException)
6582
10.0k
          MagickError2(image->exception.severity,image->exception.reason,
6583
32.8k
                       image->exception.description);
6584
32.8k
        DestroyImageInfo(clone_info);
6585
32.8k
        if (composite_image == (Image *) NULL)
6586
10.0k
          {
6587
10.0k
            status=MagickFail;
6588
10.0k
            break;
6589
10.0k
          }
6590
        /*
6591
          If the requested size is not 0x0 (to be filled in later with
6592
          composite image size) and the requested size is different than
6593
          the composite image size, then transform to the requested
6594
          size.
6595
        */
6596
22.7k
        if ((0 != primitive_info[1].point.x) &&
6597
22.5k
            (0 != primitive_info[1].point.y) &&
6598
22.1k
            (primitive_info[1].point.x != composite_image->columns) &&
6599
22.0k
            (primitive_info[1].point.y != composite_image->rows))
6600
21.6k
          {
6601
21.6k
            char
6602
21.6k
              geometry[MaxTextExtent];
6603
6604
            /*
6605
              Resize image.
6606
            */
6607
21.6k
            MagickFormatString(geometry,sizeof(geometry),"%gx%g!",
6608
21.6k
                               primitive_info[1].point.x,primitive_info[1].point.y);
6609
21.6k
            handler=SetMonitorHandler((MonitorHandler) NULL);
6610
21.6k
            status&=TransformImage(&composite_image,(char *) NULL,geometry);
6611
21.6k
            (void) SetMonitorHandler(handler);
6612
21.6k
            if (status == MagickFail)
6613
455
              {
6614
455
                DestroyImageList(composite_image);
6615
455
                break;
6616
455
              }
6617
21.6k
          }
6618
22.3k
        if (!composite_image->matte)
6619
21.9k
          status&=SetImageOpacity(composite_image,OpaqueOpacity);
6620
22.3k
        if (draw_info->opacity != OpaqueOpacity)
6621
360
          status&=SetImageOpacity(composite_image,draw_info->opacity);
6622
22.3k
        affine=draw_info->affine;
6623
22.3k
        affine.tx=x;
6624
22.3k
        affine.ty=y;
6625
22.3k
        status&=DrawAffineImage(image,composite_image,&affine);
6626
22.3k
        DestroyImageList(composite_image);
6627
22.3k
        break;
6628
22.7k
      }
6629
133k
    default:
6630
133k
      {
6631
133k
        double
6632
133k
          mid,
6633
133k
          stroke_width_limited,
6634
133k
          scale;
6635
6636
133k
        DrawInfo
6637
133k
          *clone_info;
6638
6639
133k
        if (IsEventLogged(RenderEvent))
6640
0
          LogPrimitiveInfo(primitive_info);
6641
133k
        scale=ExpandAffine(&draw_info->affine);
6642
133k
        if ((draw_info->dash_pattern != (double *) NULL) &&
6643
48.3k
            (draw_info->dash_pattern[0] != 0.0) &&
6644
46.5k
            ((scale*draw_info->stroke_width) > MagickEpsilon) &&
6645
41.5k
            (draw_info->stroke.opacity != TransparentOpacity))
6646
28.1k
          {
6647
            /*
6648
              Draw dash polygon.
6649
            */
6650
28.1k
            clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6651
28.1k
            clone_info->stroke_width=0.0;
6652
28.1k
            clone_info->stroke.opacity=TransparentOpacity;
6653
28.1k
            status&=DrawPolygonPrimitive(image,clone_info,primitive_info);
6654
28.1k
            DestroyDrawInfo(clone_info);
6655
28.1k
            status&=DrawDashPolygon(draw_info,primitive_info,image); /* oss-fuzz 455298154 always here while drawing filled 1 pixel circle, due to 'stroke-dasharray 1' */
6656
28.1k
            break;
6657
28.1k
          }
6658
6659
        /* sanity check for excessively big stroke_width (ticket #515) */
6660
105k
        if  ( (stroke_width_limited = STROKE_WIDTH_LIMIT(image)) > draw_info->stroke_width )
6661
104k
          stroke_width_limited = draw_info->stroke_width;
6662
6663
105k
        mid=ExpandAffine(&draw_info->affine)*stroke_width_limited/2.0;
6664
105k
        if ((mid > 1.0) && (draw_info->stroke.opacity != TransparentOpacity))
6665
9.25k
          {
6666
9.25k
            unsigned int
6667
9.25k
              closed_path;
6668
6669
            /*
6670
              Draw strokes while respecting line cap/join attributes.
6671
            */
6672
9.25k
            closed_path=PRIMINF_GET_IS_CLOSED_SUBPATH(&primitive_info[0]);
6673
9.25k
            i=(long) primitive_info[0].coordinates;
6674
9.25k
            if ((((draw_info->linecap == RoundCap) || closed_path) &&
6675
5.54k
                 (draw_info->linejoin == RoundJoin)) ||
6676
6.93k
                (primitive_info[i].primitive != UndefinedPrimitive))
6677
3.14k
              {
6678
3.14k
                status&=DrawPolygonPrimitive(image,draw_info,primitive_info);
6679
3.14k
                break;
6680
3.14k
              }
6681
            /* first fill the polygon by cloning and turning off stroking ... */
6682
6.11k
            clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6683
6.11k
            clone_info->stroke_width=0.0;
6684
6.11k
            clone_info->stroke.opacity=TransparentOpacity;
6685
6.11k
            status&=DrawPolygonPrimitive(image,clone_info,primitive_info);
6686
6.11k
            DestroyDrawInfo(clone_info);
6687
            /* ... and then stroke the polygon */
6688
6.11k
            if (status != MagickFail)
6689
6.10k
              status&=DrawStrokePolygon(image,draw_info,primitive_info);
6690
6.11k
            break;
6691
9.25k
          }
6692
96.1k
        status&=DrawPolygonPrimitive(image,draw_info,primitive_info);
6693
96.1k
        break;
6694
105k
      }
6695
241k
    }
6696
241k
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),"  end draw-primitive");
6697
241k
  return(status);
6698
241k
}
6699

6700
/*
6701
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6702
%                                                                             %
6703
%                                                                             %
6704
%                                                                             %
6705
+   D r a w S t r o k e P o l y g o n                                         %
6706
%                                                                             %
6707
%                                                                             %
6708
%                                                                             %
6709
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6710
%
6711
%  Method DrawStrokePolygon draws a stroked polygon (line, rectangle, ellipse)
6712
%  on the image while respecting the line cap and join attributes.
6713
%
6714
%  The format of the DrawStrokePolygon method is:
6715
%
6716
%      unsigned int DrawStrokePolygon(Image *image,const DrawInfo *draw_info,
6717
%        const PrimitiveInfo *primitive_info)
6718
%
6719
%  A description of each parameter follows:
6720
%
6721
%    o image: The image.
6722
%
6723
%    o draw_info: The draw info.
6724
%
6725
%    o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
6726
%
6727
%
6728
*/
6729
6730
static void
6731
DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
6732
                 const PrimitiveInfo *primitive_info)
6733
593k
{
6734
593k
  PrimitiveInfo
6735
593k
    linecap[5];
6736
6737
593k
  register long
6738
593k
    i;
6739
6740
2.96M
  for (i=0; i < 4; i++)
6741
2.37M
    linecap[i]=(*primitive_info);
6742
593k
  linecap[0].coordinates=4;
6743
593k
  linecap[1].point.x+=10.0*MagickEpsilon;
6744
593k
  linecap[2].point.x+=10.0*MagickEpsilon;
6745
593k
  linecap[2].point.y+=10.0*MagickEpsilon;
6746
593k
  linecap[3].point.y+=10.0*MagickEpsilon;
6747
593k
  linecap[4].primitive=UndefinedPrimitive;
6748
593k
  (void) DrawPolygonPrimitive(image,draw_info,linecap);
6749
593k
}
6750
6751
static MagickPassFail
6752
DrawStrokePolygon(Image *image,const DrawInfo *draw_info,
6753
                  const PrimitiveInfo *primitive_info)
6754
3.00M
{
6755
3.00M
  DrawInfo
6756
3.00M
    *clone_info;
6757
6758
3.00M
  MagickBool
6759
3.00M
    closed_path;
6760
6761
3.00M
  MagickPassFail
6762
3.00M
    status;
6763
6764
3.00M
  PrimitiveInfo
6765
3.00M
    *stroke_polygon;
6766
6767
3.00M
  register const PrimitiveInfo
6768
3.00M
    *p,
6769
3.00M
    *q;
6770
6771
  /*
6772
    Draw stroked polygon.
6773
  */
6774
3.00M
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6775
3.00M
    "    begin draw-stroke-polygon");
6776
3.00M
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
6777
3.00M
  clone_info->fill=draw_info->stroke;
6778
3.00M
  if (clone_info->fill_pattern != (Image *) NULL)
6779
56.7k
    {
6780
56.7k
      DestroyImage(clone_info->fill_pattern);
6781
56.7k
      clone_info->fill_pattern= (Image *) NULL;
6782
56.7k
    }
6783
3.00M
  if (clone_info->stroke_pattern != (Image *) NULL)
6784
29.8k
    clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0,
6785
29.8k
      MagickTrue,&clone_info->stroke_pattern->exception);
6786
3.00M
  clone_info->stroke.opacity=TransparentOpacity;
6787
3.00M
  clone_info->stroke_width=0.0;
6788
3.00M
  clone_info->fill_rule=NonZeroRule;
6789
3.00M
  status=MagickPass;
6790
6.01M
  for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
6791
3.00M
  {
6792
    /*
6793
      Per the SVG spec:  A subpath (see Paths) consisting of a
6794
      single moveto shall not be stroked.
6795
    */
6796
3.00M
    if  ( p->coordinates == 1 )
6797
3.15k
      continue;
6798
    /*
6799
      *** BUG ALERT! ***
6800
6801
      The sequence below has a bug in it somewhere.  "Thin" polygons that are
6802
      stroked with a stroke-width whose magnitude is close to the size of the
6803
      "thinness" of the polygon are rendered incorrectly.  For example, this
6804
      SVG path renders correctly:
6805
6806
      <path fill="none" stroke="rgb(0,255,0)" stroke-width="3" stroke-linecap="round"
6807
        stroke-linejoin="miter" d="M 10 10 110 10 110 10.5 z" />
6808
6809
      But this one does not:
6810
6811
      <path fill="none" stroke="rgb(0,255,0)" stroke-width="3" stroke-linecap="round"
6812
        stroke-linejoin="miter" d="M 10 10 110 10 110 10.4 z" />
6813
6814
      The only difference is the y coordinate of the last path point (10.5 vs. 10.4).
6815
6816
      The "stroke_polygon" produced by TraceStrokePolygon() for the second path is
6817
      significantly different from that for the first path, so my suspicion is that
6818
      that's where the bug is.  However, it could also be in DrawPolygonPrimitive().
6819
    */
6820
3.00M
    stroke_polygon=TraceStrokePolygon(image,draw_info,p,&image->exception);
6821
3.00M
    if (stroke_polygon == (PrimitiveInfo *) NULL)
6822
213
      {
6823
213
        status=MagickFail;
6824
213
        break;
6825
213
      }
6826
3.00M
    status&=DrawPolygonPrimitive(image,clone_info,stroke_polygon);
6827
3.00M
    MagickFreeResourceLimitedMemory(PrimitiveInfo *,stroke_polygon);
6828
3.00M
    if (status == MagickFail)
6829
109
      break;
6830
3.00M
    q=p+p->coordinates-1;
6831
3.00M
    closed_path=PRIMINF_GET_IS_CLOSED_SUBPATH(p);
6832
3.00M
    if ((draw_info->linecap == RoundCap) && !closed_path)
6833
296k
      {
6834
296k
        DrawRoundLinecap(image,draw_info,p);
6835
296k
        DrawRoundLinecap(image,draw_info,q);
6836
296k
      }
6837
3.00M
  }
6838
3.00M
  DestroyDrawInfo(clone_info);
6839
3.00M
  (void) LogMagickEvent(RenderEvent,GetMagickModule(),
6840
3.00M
    "    end draw-stroke-polygon");
6841
3.00M
  return(status);
6842
3.00M
}
6843

6844
/*
6845
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6846
%                                                                             %
6847
%                                                                             %
6848
%                                                                             %
6849
+   G e t D r a w I n f o                                                     %
6850
%                                                                             %
6851
%                                                                             %
6852
%                                                                             %
6853
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6854
%
6855
%  Method GetDrawInfo initializes draw_info to default values.
6856
%
6857
%  The format of the GetDrawInfo method is:
6858
%
6859
%      void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6860
%
6861
%  A description of each parameter follows:
6862
%
6863
%    o image_info: The image info..
6864
%
6865
%    o draw_info: The draw info.
6866
%
6867
%
6868
*/
6869
MagickExport void
6870
GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
6871
4.36M
{
6872
4.36M
  ImageInfo
6873
4.36M
    *clone_info;
6874
6875
4.36M
  DrawInfoExtra
6876
4.36M
    *DIExtra;
6877
6878
  /*
6879
    Initialize draw attributes.
6880
  */
6881
4.36M
  assert(draw_info != (DrawInfo *) NULL);
6882
4.36M
  (void) memset(draw_info,0,sizeof(DrawInfo));
6883
6884
  /* allocate and initialize struct for extra DrawInfo members */
6885
4.36M
  DIExtra = MagickAllocateMemory(DrawInfoExtra *,sizeof(DrawInfoExtra));
6886
4.36M
  if  ( DIExtra == (DrawInfoExtra *) NULL )
6887
4.36M
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,UnableToAllocateDrawInfo);
6888
4.36M
  memset(DIExtra,0,sizeof(*DIExtra));
6889
4.36M
  draw_info->extra = DIExtra;
6890
6891
4.36M
  clone_info=CloneImageInfo(image_info);
6892
4.36M
  IdentityAffine(&draw_info->affine);
6893
4.36M
  draw_info->gravity=NorthWestGravity;
6894
4.36M
  draw_info->opacity=OpaqueOpacity; /* 0UL */
6895
4.36M
  draw_info->fill.opacity=OpaqueOpacity; /* 0UL */
6896
4.36M
  draw_info->stroke.opacity=TransparentOpacity; /* MaxRGB */
6897
4.36M
  draw_info->stroke_antialias=clone_info->antialias;
6898
4.36M
  draw_info->stroke_width=1.0;
6899
4.36M
  draw_info->fill_rule=EvenOddRule;
6900
4.36M
  draw_info->linecap=ButtCap;
6901
4.36M
  draw_info->linejoin=MiterJoin;
6902
4.36M
  draw_info->miterlimit=10;
6903
4.36M
  draw_info->decorate=NoDecoration;
6904
4.36M
  if (clone_info->font != (char *) NULL)
6905
0
    draw_info->font=AllocateString(clone_info->font);
6906
4.36M
  if (clone_info->density != (char *) NULL)
6907
0
    draw_info->density=AllocateString(clone_info->density);
6908
4.36M
  draw_info->text_antialias=clone_info->antialias;
6909
4.36M
  draw_info->pointsize=clone_info->pointsize;
6910
4.36M
  draw_info->undercolor.opacity=TransparentOpacity;
6911
4.36M
  draw_info->border_color=clone_info->border_color;
6912
4.36M
  draw_info->compose=CopyCompositeOp;
6913
4.36M
  if (clone_info->server_name != (char *) NULL)
6914
0
    draw_info->server_name=AllocateString(clone_info->server_name);
6915
4.36M
  draw_info->render=True;
6916
4.36M
  draw_info->signature=MagickSignature;
6917
4.36M
  SetDrawInfoSVGCompliant(draw_info,MagickFalse);
6918
4.36M
  SetDrawInfoClippingPath(draw_info,MagickFalse);
6919
4.36M
  DestroyImageInfo(clone_info);
6920
4.36M
}
6921

6922
/*
6923
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6924
%                                                                             %
6925
%                                                                             %
6926
%                                                                             %
6927
+   P e r m u t a t e                                                         %
6928
%                                                                             %
6929
%                                                                             %
6930
%                                                                             %
6931
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6932
%
6933
%  Method Permutate()
6934
%
6935
%  The format of the Permutate method is:
6936
%
6937
%      void Permutate(long n,long k)
6938
%
6939
%  A description of each parameter follows:
6940
%
6941
%    o n:
6942
%
6943
%    o k:
6944
%
6945
%
6946
*/
6947
static inline double
6948
Permutate(const long n,const long k)
6949
1.17M
{
6950
1.17M
  double
6951
1.17M
    r;
6952
6953
1.17M
  register long
6954
1.17M
    i;
6955
6956
1.17M
  r=1.0;
6957
2.65M
  for (i=k+1; i <= n; i++)
6958
1.48M
    r*=i;
6959
2.65M
  for (i=1; i <= (n-k); i++)
6960
1.48M
    r/=i;
6961
1.17M
  return(r);
6962
1.17M
}
6963

6964
/*
6965
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6966
%                                                                             %
6967
%                                                                             %
6968
%                                                                             %
6969
+   T r a c e P r i m i t i v e                                               %
6970
%                                                                             %
6971
%                                                                             %
6972
%                                                                             %
6973
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6974
%
6975
%  TracePrimitive is a collection of methods for generating graphic
6976
%  primitives such as arcs, ellipses, paths, etc.
6977
%
6978
%
6979
*/
6980
6981
static MagickPassFail
6982
TraceArc(PrimitiveInfoMgr *p_PIMgr,const PointInfo start,
6983
         const PointInfo end,const PointInfo arc)
6984
1.90k
{
6985
1.90k
  PointInfo
6986
1.90k
    center,
6987
1.90k
    radius;
6988
6989
1.90k
  center.x=0.5*(end.x+start.x);
6990
1.90k
  center.y=0.5*(end.y+start.y);
6991
1.90k
  radius.x=fabs(center.x-start.x);
6992
1.90k
  radius.y=fabs(center.y-start.y);
6993
1.90k
  return TraceEllipse(p_PIMgr,center,radius,arc);
6994
1.90k
}
6995
6996
static MagickPassFail
6997
TraceArcPath(PrimitiveInfoMgr *p_PIMgr,const PointInfo start,
6998
             const PointInfo end,const PointInfo arc,const double angle,
6999
             const unsigned int large_arc,const unsigned int sweep)
7000
56.7k
{
7001
56.7k
  double
7002
56.7k
    alpha,
7003
56.7k
    beta,
7004
56.7k
    delta,
7005
56.7k
    factor,
7006
56.7k
    gamma,
7007
56.7k
    theta;
7008
7009
56.7k
  PointInfo
7010
56.7k
    center,
7011
56.7k
    points[3],
7012
56.7k
    radii;
7013
7014
56.7k
  register double
7015
56.7k
    cosine,
7016
56.7k
    sine;
7017
7018
56.7k
  register PrimitiveInfo
7019
56.7k
    *p;
7020
7021
56.7k
  register long
7022
56.7k
    i;
7023
7024
56.7k
  unsigned long
7025
56.7k
    arc_segments;
7026
7027
56.7k
  PrimitiveInfo
7028
56.7k
    **pp_PrimitiveInfo,
7029
56.7k
    *primitive_info;
7030
7031
56.7k
  size_t
7032
56.7k
    InitialStoreStartingAt;
7033
7034
56.7k
  pp_PrimitiveInfo = p_PIMgr->pp_PrimitiveInfo;
7035
56.7k
  InitialStoreStartingAt = p_PIMgr->StoreStartingAt;
7036
56.7k
  primitive_info = *pp_PrimitiveInfo + InitialStoreStartingAt;
7037
7038
56.7k
  primitive_info->coordinates = 0;      /* in case we return without doing anything */
7039
  /*
7040
    Per the SVG spec: If the endpoints (x1, y1) and (x2, y2) are identical, then
7041
    this is equivalent to omitting the elliptical arc segment entirely.
7042
  */
7043
56.7k
  if  ( (fabs(start.x-end.x) < MagickEpsilon) && (fabs(start.y-end.y) < MagickEpsilon) )
7044
4.09k
    {
7045
      /* substitute a lineto command (so zero length arc looks like a zero length segment) */
7046
4.09k
      return (TracePoint(primitive_info,end));
7047
4.09k
    }
7048
52.6k
  radii.x=fabs(arc.x);
7049
52.6k
  radii.y=fabs(arc.y);
7050
52.6k
  if ((radii.x < MagickEpsilon) || (radii.y < MagickEpsilon))
7051
11.8k
    {
7052
      /* Substitute a lineto command */
7053
11.8k
      return (TraceLine(primitive_info,start,end));
7054
11.8k
    }
7055
40.8k
  cosine=cos(DegreesToRadians(fmod(angle,360.0)));
7056
40.8k
  sine=sin(DegreesToRadians(fmod(angle,360.0)));
7057
40.8k
  center.x=cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2;
7058
40.8k
  center.y=cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2;
7059
40.8k
  delta=(center.x*center.x)/(radii.x*radii.x)+
7060
40.8k
    (center.y*center.y)/(radii.y*radii.y);
7061
40.8k
  if (delta > 1.0)
7062
28.8k
    {
7063
28.8k
      radii.x*=sqrt(delta);
7064
28.8k
      radii.y*=sqrt(delta);
7065
28.8k
    }
7066
40.8k
  points[0].x=cosine*start.x/radii.x+sine*start.y/radii.x;
7067
40.8k
  points[0].y=cosine*start.y/radii.y-sine*start.x/radii.y;
7068
40.8k
  points[1].x=cosine*end.x/radii.x+sine*end.y/radii.x;
7069
40.8k
  points[1].y=cosine*end.y/radii.y-sine*end.x/radii.y;
7070
40.8k
  alpha=points[1].x-points[0].x;
7071
40.8k
  beta=points[1].y-points[0].y;
7072
40.8k
  if (fabs(alpha*alpha+beta*beta) < MagickEpsilon)
7073
2.59k
    {
7074
      /* Substitute a lineto command */
7075
2.59k
      return (TraceLine(primitive_info,start,end));
7076
2.59k
    }
7077
38.2k
  factor=1.0/(alpha*alpha+beta*beta)-0.25;
7078
38.2k
  if (factor <= 0.0)
7079
19.2k
    factor=0.0;
7080
18.9k
  else
7081
18.9k
    {
7082
18.9k
      factor=sqrt(factor);
7083
18.9k
      if (sweep == large_arc)
7084
1.78k
        factor=(-factor);
7085
18.9k
    }
7086
38.2k
  center.x=(points[0].x+points[1].x)/2-factor*beta;
7087
38.2k
  center.y=(points[0].y+points[1].y)/2+factor*alpha;
7088
38.2k
  alpha=atan2(points[0].y-center.y,points[0].x-center.x);
7089
38.2k
  theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
7090
38.2k
  if ((theta < 0.0) && sweep)
7091
15.1k
    theta+=2.0*MagickPI;
7092
23.0k
  else
7093
23.0k
    if ((theta > 0.0) && !sweep)
7094
2.10k
      theta-=2.0*MagickPI;
7095
38.2k
  arc_segments=(long) ceil(fabs(theta/(0.5*MagickPI+MagickEpsilon)));
7096
38.2k
  p=primitive_info;
7097
111k
  for (i=0; i < (long) arc_segments; i++)
7098
73.0k
  {
7099
73.0k
    beta=0.5*((alpha+((double) i+1)*theta/arc_segments)-(alpha+(double) i*theta/arc_segments));
7100
73.0k
    gamma=(8.0/3.0)*sin(fmod(0.5*beta,DegreesToRadians(360.0)))*
7101
73.0k
      sin(fmod(0.5*beta,DegreesToRadians(360.0)))/
7102
73.0k
      sin(fmod(beta,DegreesToRadians(360.0)));
7103
73.0k
    points[0].x=center.x+
7104
73.0k
      cos(fmod(alpha+i*theta/arc_segments,DegreesToRadians(360.0)))-gamma*
7105
73.0k
      sin(fmod(alpha+i*theta/arc_segments,DegreesToRadians(360.0)));
7106
73.0k
    points[0].y=center.y+
7107
73.0k
      sin(fmod(alpha+i*theta/arc_segments,DegreesToRadians(360.0)))+gamma*
7108
73.0k
      cos(fmod(alpha+i*theta/arc_segments,DegreesToRadians(360.0)));
7109
73.0k
    points[2].x=center.x+
7110
73.0k
      cos(fmod(alpha+((double) i+1)*theta/arc_segments,DegreesToRadians(360.0)));
7111
73.0k
    points[2].y=center.y+
7112
73.0k
      sin(fmod(alpha+((double) i+1)*theta/arc_segments,DegreesToRadians(360.0)));
7113
73.0k
    points[1].x=points[2].x+gamma*
7114
73.0k
      sin(fmod(alpha+((double) i+1)*theta/arc_segments,DegreesToRadians(360.0)));
7115
73.0k
    points[1].y=points[2].y-gamma*
7116
73.0k
      cos(fmod(alpha+((double) i+1)*theta/arc_segments,DegreesToRadians(360.0)));
7117
73.0k
    p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
7118
73.0k
    p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
7119
73.0k
    (p+1)->point.x=cosine*radii.x*points[0].x-sine*radii.y*points[0].y;
7120
73.0k
    (p+1)->point.y=sine*radii.x*points[0].x+cosine*radii.y*points[0].y;
7121
73.0k
    (p+2)->point.x=cosine*radii.x*points[1].x-sine*radii.y*points[1].y;
7122
73.0k
    (p+2)->point.y=sine*radii.x*points[1].x+cosine*radii.y*points[1].y;
7123
73.0k
    (p+3)->point.x=cosine*radii.x*points[2].x-sine*radii.y*points[2].y;
7124
73.0k
    (p+3)->point.y=sine*radii.x*points[2].x+cosine*radii.y*points[2].y;
7125
73.0k
    if (i == (long) (arc_segments-1))
7126
37.8k
      (p+3)->point=end;
7127
73.0k
    if (TraceBezier(p_PIMgr,4) == MagickFail)
7128
11
      return MagickFail;
7129
73.0k
    p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base addr might have changed */
7130
73.0k
    p_PIMgr->StoreStartingAt+=p->coordinates;
7131
73.0k
    p+=p->coordinates;
7132
73.0k
  }
7133
38.2k
  primitive_info = *pp_PrimitiveInfo + InitialStoreStartingAt;
7134
38.2k
  p_PIMgr->StoreStartingAt = InitialStoreStartingAt;  /* restore original value */
7135
38.2k
  primitive_info->coordinates=p-primitive_info;
7136
38.2k
  PRIMINF_CLEAR_FLAGS(primitive_info);
7137
41.1M
  for (i=0; i < (long) primitive_info->coordinates; i++)
7138
41.1M
  {
7139
41.1M
    p->primitive=primitive_info->primitive;
7140
41.1M
    p--;
7141
41.1M
  }
7142
38.2k
  return MagickPass;
7143
38.2k
}
7144
7145
static MagickPassFail
7146
TraceBezier(PrimitiveInfoMgr *p_PIMgr,
7147
            const size_t number_coordinates)
7148
357k
{
7149
357k
  double
7150
357k
    alpha,
7151
357k
    *coefficients = (double *) NULL,
7152
357k
    weight;
7153
7154
357k
  PointInfo
7155
357k
    end,
7156
357k
    point,
7157
357k
    *points = (PointInfo *) NULL;
7158
7159
357k
  PrimitiveInfo
7160
357k
    *primitive_info,
7161
357k
    **pp_PrimitiveInfo;
7162
7163
357k
  register PrimitiveInfo
7164
357k
    *p;
7165
7166
357k
  register size_t
7167
357k
    i,
7168
357k
    j;
7169
7170
357k
  size_t
7171
357k
    control_points,
7172
357k
    quantum;
7173
7174
357k
  MagickPassFail
7175
357k
    status = MagickPass;
7176
7177
357k
  pp_PrimitiveInfo = p_PIMgr->pp_PrimitiveInfo;
7178
357k
  primitive_info = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7179
7180
  /*
7181
    Allocate coefficients.
7182
  */
7183
357k
  quantum=number_coordinates;
7184
1.53M
  for (i=0; i < number_coordinates; i++)
7185
1.17M
  {
7186
2.65M
    for (j=i+1; j < number_coordinates; j++)
7187
1.48M
    {
7188
1.48M
      alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
7189
1.48M
      if (alpha > (double) INT_MAX)
7190
22
        {
7191
22
          ThrowException3(p_PIMgr->p_Exception,DrawError,ArithmeticOverflow,
7192
22
                          UnableToDrawOnImage);
7193
22
          status=MagickFail;
7194
22
          goto trace_bezier_done;
7195
22
        }
7196
1.48M
      if (alpha > quantum)
7197
460k
        quantum=(size_t) alpha;
7198
1.48M
      alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
7199
1.48M
      if (alpha > (double) INT_MAX)
7200
19
        {
7201
19
          ThrowException3(p_PIMgr->p_Exception,DrawError,ArithmeticOverflow,
7202
19
                          UnableToDrawOnImage);
7203
19
          status=MagickFail;
7204
19
          goto trace_bezier_done;
7205
19
        }
7206
1.48M
      if (alpha > quantum)
7207
321k
        quantum=(size_t) alpha;
7208
1.48M
    }
7209
1.17M
  }
7210
357k
  quantum=Min(quantum/number_coordinates,BezierQuantum);
7211
357k
  control_points=MagickArraySize(quantum,number_coordinates);
7212
357k
  if (control_points == 0)
7213
0
    {
7214
0
      ThrowException3(p_PIMgr->p_Exception,DrawError,ArithmeticOverflow,
7215
0
                      UnableToDrawOnImage);
7216
0
      status=MagickFail;
7217
0
      goto trace_bezier_done;
7218
0
    }
7219
7220
  /* make sure we have enough space */
7221
357k
  if (PrimitiveInfoRealloc(p_PIMgr,control_points+1) == MagickFail)
7222
4
    {
7223
4
      status=MagickFail;
7224
4
      goto trace_bezier_done;
7225
4
    }
7226
357k
  primitive_info = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7227
7228
357k
  coefficients=MagickAllocateResourceLimitedArray(double *,number_coordinates,sizeof(double));
7229
357k
  if (coefficients == (double *) NULL)
7230
1
    {
7231
1
      ThrowException3(p_PIMgr->p_Exception,ResourceLimitError,MemoryAllocationFailed,
7232
1
                      UnableToDrawOnImage);
7233
1
      status=MagickFail;
7234
1
      goto trace_bezier_done;
7235
1
    }
7236
357k
  points=MagickAllocateResourceLimitedArray(PointInfo *,control_points,sizeof(PointInfo));
7237
357k
  if (points == (PointInfo *) NULL)
7238
5
    {
7239
5
      ThrowException3(p_PIMgr->p_Exception,ResourceLimitError,MemoryAllocationFailed,
7240
5
                      UnableToDrawOnImage);
7241
5
      status=MagickFail;
7242
5
      goto trace_bezier_done;
7243
5
    }
7244
  /*
7245
    Compute bezier points.
7246
  */
7247
357k
  end=primitive_info[number_coordinates-1].point;
7248
357k
  weight=0.0;
7249
1.53M
  for (i=0; i < number_coordinates; i++)
7250
1.17M
    coefficients[i]=(long) Permutate((long) (number_coordinates-1),(long) i);
7251
197M
  for (i=0; i < control_points; i++)
7252
197M
  {
7253
197M
    p=primitive_info;
7254
197M
    point.x=0;
7255
197M
    point.y=0;
7256
197M
    alpha=pow((double) (1.0-weight),(double) number_coordinates-1);
7257
843M
    for (j=0; j < number_coordinates; j++)
7258
646M
    {
7259
646M
      point.x+=alpha*coefficients[j]*p->point.x;
7260
646M
      point.y+=alpha*coefficients[j]*p->point.y;
7261
646M
      alpha*=weight/(1.0-weight);
7262
646M
      p++;
7263
646M
    }
7264
197M
    points[i]=point;
7265
197M
    weight+=1.0/quantum/number_coordinates;
7266
197M
  }
7267
  /*
7268
    Bezier curves are just short segmented polys.
7269
  */
7270
357k
  p=primitive_info;
7271
197M
  for (i=0; i < control_points; i++)
7272
197M
  {
7273
197M
    if ((status=TracePoint(p,points[i])) == MagickFail)
7274
0
      goto trace_bezier_done;
7275
197M
    p+=p->coordinates;
7276
197M
  }
7277
357k
  if ((status=TracePoint(p,end)) == MagickFail)
7278
0
    goto trace_bezier_done;
7279
357k
  p+=p->coordinates;
7280
357k
  primitive_info->coordinates=p-primitive_info;
7281
357k
  PRIMINF_CLEAR_FLAGS(primitive_info);
7282
197M
  for (i=0; i < primitive_info->coordinates; i++)
7283
197M
  {
7284
197M
    p->primitive=primitive_info->primitive;
7285
197M
    p--;
7286
197M
  }
7287
357k
 trace_bezier_done:;
7288
357k
  MagickFreeResourceLimitedMemory(PointInfo *,points);
7289
357k
  MagickFreeResourceLimitedMemory(double *,coefficients);
7290
357k
  return status;
7291
357k
}
7292
7293
static MagickPassFail
7294
TraceCircle(PrimitiveInfoMgr *p_PIMgr,const PointInfo start,
7295
            const PointInfo end)
7296
17.0k
{
7297
17.0k
  double
7298
17.0k
    alpha,
7299
17.0k
    beta,
7300
17.0k
    radius;
7301
7302
17.0k
  PointInfo
7303
17.0k
    offset,
7304
17.0k
    degrees;
7305
7306
17.0k
  alpha=end.x-start.x;
7307
17.0k
  beta=end.y-start.y;
7308
17.0k
  radius=sqrt(alpha*alpha+beta*beta);
7309
17.0k
  offset.x=radius;
7310
17.0k
  offset.y=radius;
7311
17.0k
  degrees.x=0.0;
7312
17.0k
  degrees.y=360.0;
7313
17.0k
  return TraceEllipse(p_PIMgr,start,offset,degrees);
7314
17.0k
}
7315
7316
static MagickPassFail
7317
TraceEllipse(PrimitiveInfoMgr *p_PIMgr,const PointInfo start,
7318
             const PointInfo stop,const PointInfo degrees)
7319
47.9k
{
7320
47.9k
  double
7321
47.9k
    delta,
7322
47.9k
    points_length,
7323
47.9k
    step,
7324
47.9k
    y;
7325
7326
47.9k
  PointInfo
7327
47.9k
    angle,
7328
47.9k
    point;
7329
7330
47.9k
  register PrimitiveInfo
7331
47.9k
    *p;
7332
7333
47.9k
  PrimitiveInfo
7334
47.9k
    *primitive_info,
7335
47.9k
    **pp_PrimitiveInfo;
7336
7337
47.9k
  MagickPassFail
7338
47.9k
    status = MagickPass;
7339
7340
  /*
7341
    Ellipses are just short segmented polys.
7342
  */
7343
47.9k
  pp_PrimitiveInfo = p_PIMgr->pp_PrimitiveInfo;
7344
47.9k
  primitive_info = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7345
47.9k
  primitive_info->coordinates = 0;  /* in case we return without doing anything */
7346
47.9k
  if (stop.x == 0.0 || stop.y == 0.0)  /* actually the x and y radii of the corner ellipse */
7347
2.33k
    return MagickPass;
7348
45.5k
  delta=2.0/Max(stop.x,stop.y);
7349
45.5k
  step=MagickPI/8.0;
7350
45.5k
  if (delta < (MagickPI/8.0))
7351
21.0k
    step=(MagickPI/4.0)/ceil(MagickPI/delta/2.0);
7352
45.5k
  angle.x=DegreesToRadians(degrees.x);
7353
45.5k
  y=degrees.y;
7354
50.2k
  while (y < degrees.x)
7355
4.68k
    y+=360.0;
7356
45.5k
  angle.y=DegreesToRadians(y);
7357
7358
  /* FIXME: The number of points could become arbitrarily large.  It
7359
     would be good to add an algorithm which decreases ellipse drawing
7360
     quality when necessary in order to limit the number of points
7361
     required. */
7362
7363
  /* make sure we have enough space */
7364
45.5k
  points_length = ceil(1.0 + ceil((angle.y - angle.x) / step));
7365
45.5k
  if (points_length > (double) PRIMITIVE_INFO_POINTS_MAX)
7366
2
    {
7367
      /* points_length too big */
7368
2
      status=MagickFail;
7369
2
      ThrowException3(p_PIMgr->p_Exception,ResourceLimitError,
7370
2
                     MemoryAllocationFailed,UnableToDrawOnImage);
7371
2
      goto trace_ellipse_done;
7372
2
    }
7373
45.5k
  if ((status=PrimitiveInfoRealloc(p_PIMgr,(size_t) points_length)) == MagickFail)
7374
30
    goto trace_ellipse_done;
7375
45.5k
  primitive_info = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7376
7377
462M
  for (p=primitive_info; angle.x < angle.y; angle.x+=step)
7378
462M
  {
7379
462M
    point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
7380
462M
    point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
7381
462M
    if ((status=TracePoint(p,point)) == MagickFail)
7382
0
      goto trace_ellipse_done;
7383
462M
    p+=p->coordinates;
7384
462M
  }
7385
45.5k
  point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
7386
45.5k
  point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
7387
45.5k
  if ((status=TracePoint(p,point))  == MagickFail)
7388
0
    goto trace_ellipse_done;
7389
45.5k
  p+=p->coordinates;
7390
45.5k
  primitive_info->coordinates=p-primitive_info;
7391
45.5k
  PRIMINF_CLEAR_FLAGS(primitive_info);
7392
45.5k
  if  (
7393
45.5k
    (fabs(primitive_info[0].point.x-primitive_info[primitive_info->coordinates-1].point.x) < MagickEpsilon)
7394
18.3k
    && (fabs(primitive_info[0].point.y-primitive_info[primitive_info->coordinates-1].point.y) < MagickEpsilon)
7395
45.5k
    )
7396
18.0k
    PRIMINF_SET_IS_CLOSED_SUBPATH(primitive_info,1);
7397
  /* NOTE: p pointing just past last traced point! */
7398
462M
  for (p--; p > primitive_info; p--)
7399
462M
    p->primitive=primitive_info->primitive;
7400
45.5k
 trace_ellipse_done:
7401
45.5k
  return status;
7402
45.5k
}
7403
7404
static MagickPassFail
7405
TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
7406
          const PointInfo end)
7407
50.4k
{
7408
50.4k
  MagickPassFail
7409
50.4k
    status = MagickPass;
7410
7411
50.4k
  if ((status=TracePoint(primitive_info,start)) == MagickFail)
7412
0
    goto trace_line_done;
7413
50.4k
  if ((AbsoluteValue(start.x-end.x) <= MagickEpsilon) &&
7414
37.4k
      (AbsoluteValue(start.y-end.y) <= MagickEpsilon))
7415
30.7k
    {
7416
30.7k
      primitive_info->primitive=PointPrimitive;
7417
30.7k
      primitive_info->coordinates=1;
7418
30.7k
      goto trace_line_done;
7419
30.7k
    }
7420
19.6k
  if ((status=TracePoint(primitive_info+1,end)) == MagickFail)
7421
0
    goto trace_line_done;
7422
19.6k
  (primitive_info+1)->primitive=primitive_info->primitive;
7423
19.6k
  primitive_info->coordinates=2;
7424
19.6k
  PRIMINF_CLEAR_FLAGS(primitive_info);
7425
7426
50.4k
 trace_line_done:
7427
7428
50.4k
  return status;
7429
19.6k
}
7430
7431
/*
7432
  Special wrapper macros around strtod(), strtol(), and
7433
  MagickGetToken() to support TracePath() parsing error detection and
7434
  reporting.
7435
*/
7436
#define MagickTracePathAtoF(str,value,status)                   \
7437
2.48M
  do {                                                          \
7438
2.48M
    if ((*(status) &= MagickAtoFChk(str,value)) == MagickFail)     \
7439
2.48M
      {                                                         \
7440
735
        ThrowException(&image->exception,DrawError,FloatValueConversionError,str); \
7441
735
        *(status)=MagickFail;                                        \
7442
735
        return 0;                                               \
7443
735
      }                                                         \
7444
2.48M
  } while(0)
7445
7446
#define MagickTracePathAtoI(str,value,status)                \
7447
  do {                                                       \
7448
    if ((*(status) &= MagickAtoIChk(str,value)) == MagickFail)  \
7449
      {                                                      \
7450
        ThrowException(&image->exception,DrawError,IntegerValueConversionError,str); \
7451
        *(status)=MagickFail;                                        \
7452
        return 0;                                            \
7453
      }                                                      \
7454
  } while(0)
7455
7456
#define MagickTracePathAtoUI(str,value,status)               \
7457
114k
  do {                                                       \
7458
114k
    if ((*(status) &= MagickAtoUIChk(str,value)) == MagickFail) \
7459
114k
      {                                                      \
7460
202
        ThrowException(&image->exception,DrawError,IntegerValueConversionError,str); \
7461
202
        *(status)=MagickFail;                                           \
7462
202
        return 0;                                            \
7463
202
      }                                                      \
7464
114k
  } while(0)
7465
7466
#define MagickGetTracePathToken(p,ep,token,extent,status)       \
7467
2.97M
  do {                                                    \
7468
2.97M
    if (MagickGetToken(p,ep,token,extent) < 1)            \
7469
2.97M
      {                                                   \
7470
646
        ThrowException(&image->exception,DrawError,VectorPathTruncated,p); \
7471
646
        *(status)=MagickFail;                                        \
7472
646
        return 0;                        \
7473
646
      }                                                   \
7474
2.97M
  } while(0)
7475
7476
7477
static MagickPassFail
7478
TracePath(Image *image,PrimitiveInfoMgr *p_PIMgr,const char *path,size_t *number_coordinates)
7479
71.9k
{
7480
71.9k
  char
7481
71.9k
    token[MaxTextExtent];
7482
7483
71.9k
  char
7484
71.9k
    *p;
7485
7486
71.9k
  int
7487
71.9k
    attribute,
7488
71.9k
    last_attribute;
7489
7490
71.9k
  double
7491
71.9k
    x,
7492
71.9k
    y;
7493
7494
71.9k
  PointInfo
7495
71.9k
    end={0,0},
7496
71.9k
    points[4]={{0,0},{0,0},{0,0},{0,0}},
7497
71.9k
    point={0,0},
7498
71.9k
    start={0,0};
7499
7500
71.9k
  PrimitiveType
7501
71.9k
    primitive_type;
7502
7503
71.9k
  PrimitiveInfo
7504
71.9k
    *primitive_info,
7505
71.9k
    **pp_PrimitiveInfo;
7506
7507
71.9k
  register PrimitiveInfo
7508
71.9k
    *q;
7509
7510
71.9k
  register long
7511
71.9k
    i;
7512
7513
71.9k
  unsigned long
7514
71.9k
    z_count;
7515
7516
71.9k
  size_t
7517
71.9k
    SubpathOffset;  /* index to start of current subpath */
7518
7519
71.9k
  MagickPassFail
7520
71.9k
    status = MagickPass;
7521
7522
71.9k
  pp_PrimitiveInfo = p_PIMgr->pp_PrimitiveInfo;
7523
71.9k
  SubpathOffset = p_PIMgr->StoreStartingAt;
7524
71.9k
  primitive_info = *pp_PrimitiveInfo + SubpathOffset;
7525
71.9k
  attribute=0;
7526
71.9k
  *number_coordinates=0;
7527
71.9k
  z_count=0;
7528
71.9k
  primitive_type=primitive_info->primitive;
7529
71.9k
  q=primitive_info;
7530
53.3M
  for (p=(char *) path; *p != '\0'; )
7531
53.2M
  {
7532
53.2M
    while (isspace((int) *p))
7533
2.43M
      p++;
7534
53.2M
    if (*p == '\0')
7535
943
      break;
7536
53.2M
    last_attribute=attribute;
7537
53.2M
    attribute=(*p++);
7538
53.2M
    switch (attribute)
7539
53.2M
    {
7540
      /*
7541
        Elliptical arc
7542
      */
7543
4.51k
      case 'a':
7544
12.3k
      case 'A':
7545
12.3k
      {
7546
12.3k
        MagickBool
7547
12.3k
          large_arc = MagickFalse,
7548
12.3k
          sweep = MagickFalse;
7549
7550
12.3k
        double
7551
12.3k
          angle = 0.0;
7552
7553
12.3k
        PointInfo
7554
12.3k
          arc = {0,0};
7555
7556
        /*
7557
          Compute arc points.
7558
        */
7559
12.3k
        do
7560
57.3k
        {
7561
57.3k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7562
57.3k
          if (*token == ',')
7563
2.32k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7564
57.3k
          MagickTracePathAtoF(token,&arc.x,&status);
7565
57.3k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7566
57.2k
          if (*token == ',')
7567
13.7k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7568
57.2k
          MagickTracePathAtoF(token,&arc.y,&status);
7569
57.2k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7570
57.1k
          if (*token == ',')
7571
18.2k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7572
57.1k
          MagickTracePathAtoF(token,&angle,&status);
7573
57.1k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7574
57.1k
          if (*token == ',')
7575
14.8k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7576
57.1k
          MagickTracePathAtoUI(token,&large_arc,&status);
7577
56.9k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7578
56.9k
          if (*token == ',')
7579
19.7k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7580
56.9k
          MagickTracePathAtoUI(token,&sweep,&status);
7581
56.8k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7582
56.8k
          if (*token == ',')
7583
16.4k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7584
56.8k
          MagickTracePathAtoF(token,&x,&status);
7585
56.8k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7586
56.8k
          if (*token == ',')
7587
16.1k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7588
56.8k
          MagickTracePathAtoF(token,&y,&status);
7589
56.7k
          if (status == MagickFail)
7590
0
            goto trace_path_done;
7591
56.7k
          end.x=attribute == 'A' ? x : point.x+x;
7592
56.7k
          end.y=attribute == 'A' ? y : point.y+y;
7593
56.7k
          if ((status &= TraceArcPath(p_PIMgr,point,end,arc,angle,large_arc,sweep)) == MagickFail)
7594
11
            goto trace_path_done;
7595
56.7k
          q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7596
56.7k
          p_PIMgr->StoreStartingAt += q->coordinates;
7597
56.7k
          q+=q->coordinates;
7598
56.7k
          point=end;
7599
95.0k
          while (isspace((int) ((unsigned char) *p)) != 0)
7600
38.2k
            p++;
7601
56.7k
          if (*p == ',')
7602
16.2k
            p++;
7603
56.7k
        } while (IsPoint(p) != MagickFalse);
7604
11.7k
        break;
7605
12.3k
      }
7606
      /*
7607
        Cubic Bézier curve
7608
      */
7609
11.7k
      case 'c':
7610
2.62k
      case 'C':
7611
2.62k
      {
7612
        /*
7613
          Compute bezier points.
7614
        */
7615
2.62k
        do
7616
11.8k
        {
7617
11.8k
          points[0]=point;
7618
46.9k
          for (i=1; i < 4; i++)
7619
35.3k
          {
7620
35.3k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7621
35.2k
            if (*token == ',')
7622
7.83k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7623
35.2k
            MagickTracePathAtoF(token,&x,&status);
7624
35.2k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7625
35.1k
            if (*token == ',')
7626
15.6k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7627
35.1k
            MagickTracePathAtoF(token,&y,&status);
7628
35.1k
            if (status == MagickFail)
7629
0
              goto trace_path_done;
7630
35.1k
            end.x=attribute == 'C' ? x : point.x+x;
7631
35.1k
            end.y=attribute == 'C' ? y : point.y+y;
7632
35.1k
            points[i]=end;
7633
35.1k
          }
7634
11.6k
          if (status == MagickFail)
7635
0
            break;
7636
58.3k
          for (i=0; i < 4; i++)
7637
46.6k
            (q+i)->point=points[i];
7638
11.6k
          if ((status &= TraceBezier(p_PIMgr,4)) == MagickFail)
7639
4
            goto trace_path_done;
7640
11.6k
          q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7641
11.6k
          p_PIMgr->StoreStartingAt += q->coordinates;
7642
11.6k
          q+=q->coordinates;
7643
11.6k
          point=end;
7644
          /* consume comma if present (as in elliptical arc above) */
7645
18.0k
          while (isspace((int) ((unsigned char) *p)) != 0)
7646
6.39k
            p++;
7647
11.6k
          if (*p == ',')
7648
2.75k
            p++;
7649
11.6k
        } while (IsPoint(p));
7650
2.44k
        break;
7651
2.62k
      }
7652
9.90k
      case 'H':
7653
223k
      case 'h':
7654
223k
      {
7655
223k
        do
7656
300k
        {
7657
300k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7658
300k
          if (*token == ',')
7659
994
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7660
300k
          MagickTracePathAtoF(token,&x,&status);
7661
300k
          if (status == MagickFail)
7662
0
            goto trace_path_done;
7663
300k
          point.x=attribute == 'H' ? x: point.x+x;
7664
          /* make sure we have at least 100 elements available */
7665
300k
          if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
7666
771
            {
7667
771
              if ((status &= PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
7668
0
                goto trace_path_done; /* FIXME: How to return useful error? */
7669
771
              q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7670
771
            }
7671
300k
          if ((status &= TracePoint(q,point)) == MagickFail)
7672
0
            goto trace_path_done;
7673
300k
          p_PIMgr->StoreStartingAt += q->coordinates;
7674
300k
          q+=q->coordinates;
7675
          /* consume comma if present (as in elliptical arc above) */
7676
360k
          while (isspace((int) ((unsigned char) *p)) != 0)
7677
59.3k
            p++;
7678
300k
          if (*p == ',')
7679
17.5k
            p++;
7680
300k
        } while (IsPoint(p));
7681
223k
        break;
7682
223k
      }
7683
      /*
7684
        Line to
7685
      */
7686
223k
      case 'l':
7687
12.2k
      case 'L':
7688
12.2k
      {
7689
12.2k
        do
7690
142k
        {
7691
142k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7692
142k
          if (*token == ',')
7693
1.49k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7694
142k
          MagickTracePathAtoF(token,&x,&status);
7695
142k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7696
142k
          if (*token == ',')
7697
120k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7698
142k
          MagickTracePathAtoF(token,&y,&status);
7699
142k
          if (status == MagickFail)
7700
0
            goto trace_path_done;
7701
142k
          point.x=attribute == 'L' ? x : point.x+x;
7702
142k
          point.y=attribute == 'L' ? y : point.y+y;
7703
          /* make sure we have at least 100 elements available */
7704
142k
          if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
7705
66
          {
7706
66
            if ((status &= PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
7707
0
              goto trace_path_done;
7708
66
            q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7709
66
          }
7710
142k
          if ((status &= TracePoint(q,point)) == MagickFail)
7711
0
            goto trace_path_done;
7712
142k
          p_PIMgr->StoreStartingAt += q->coordinates;
7713
142k
          q+=q->coordinates;
7714
          /* consume comma if present (as in elliptical arc above) */
7715
356k
          while (isspace((int) ((unsigned char) *p)) != 0)
7716
214k
            p++;
7717
142k
          if (*p == ',')
7718
7.21k
            p++;
7719
142k
        } while (IsPoint(p));
7720
12.1k
        break;
7721
12.2k
      }
7722
      /*
7723
        Move to
7724
      */
7725
20.2k
      case 'M':
7726
48.9k
      case 'm':
7727
48.9k
      {
7728
48.9k
        if  ( p_PIMgr->StoreStartingAt != SubpathOffset )
7729
22.0k
          {
7730
22.0k
            primitive_info = *pp_PrimitiveInfo + SubpathOffset;
7731
22.0k
            primitive_info->coordinates=q-primitive_info;
7732
22.0k
            *number_coordinates+=primitive_info->coordinates;
7733
22.0k
            primitive_info=q;
7734
22.0k
            SubpathOffset=p_PIMgr->StoreStartingAt;
7735
22.0k
          }
7736
48.9k
        i=0;
7737
48.9k
        do
7738
198k
        {
7739
198k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7740
198k
          if (*token == ',')
7741
14.4k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7742
198k
          MagickTracePathAtoF(token,&x,&status);
7743
198k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7744
198k
          if (*token == ',')
7745
32.9k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7746
198k
          MagickTracePathAtoF(token,&y,&status);
7747
198k
          if (status == MagickFail)
7748
0
            goto trace_path_done;
7749
198k
          point.x=attribute == 'M' ? x : point.x+x;
7750
198k
          point.y=attribute == 'M' ? y : point.y+y;
7751
198k
          if (i == 0)
7752
48.8k
            start=point; /*otherwise it's an implied lineto command for both 'M' and 'm'*/
7753
198k
          i++;
7754
          /* make sure we have at least 100 elements available */
7755
198k
          if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
7756
104
            {
7757
104
              if ((status &= PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
7758
0
                goto trace_path_done; /* FIXME: How to return useful error? */
7759
104
               q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7760
104
            }
7761
198k
          if ((status &= TracePoint(q,point)) == MagickFail)
7762
0
            goto trace_path_done;
7763
198k
          p_PIMgr->StoreStartingAt += q->coordinates;
7764
198k
          q+=q->coordinates;
7765
7766
          /* there was code here that added the point again, but only if 'M' (?) ... deleted */
7767
7768
          /* consume comma if present (as in elliptical arc above) */
7769
340k
          while (isspace((int) ((unsigned char) *p)) != 0)
7770
141k
            p++;
7771
198k
          if (*p == ',')
7772
18.1k
            p++;
7773
198k
        } while (IsPoint(p));
7774
48.8k
        break;
7775
48.9k
      }
7776
      /*
7777
        Quadratic Bézier curve
7778
      */
7779
48.8k
      case 'q':
7780
3.06k
      case 'Q':
7781
3.06k
      {
7782
        /*
7783
          Compute bezier points.
7784
        */
7785
3.06k
        do
7786
22.5k
        {
7787
22.5k
          points[0]=point;
7788
67.4k
          for (i=1; i < 3; i++)
7789
45.0k
          {
7790
45.0k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7791
44.9k
            if (*token == ',')
7792
970
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7793
44.9k
            MagickTracePathAtoF(token,&x,&status);
7794
44.9k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7795
44.9k
            if (*token == ',')
7796
11.6k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7797
44.9k
            MagickTracePathAtoF(token,&y,&status);
7798
44.8k
            if (status == MagickFail)
7799
0
              goto trace_path_done;
7800
44.8k
            if (*p == ',')
7801
10.5k
              p++;
7802
44.8k
            end.x=attribute == 'Q' ? x : point.x+x;
7803
44.8k
            end.y=attribute == 'Q' ? y : point.y+y;
7804
44.8k
            points[i]=end;
7805
44.8k
          }
7806
89.6k
          for (i=0; i < 3; i++)
7807
67.2k
            (q+i)->point=points[i];
7808
22.4k
          if ((status &= TraceBezier(p_PIMgr,3)) == MagickFail)
7809
7
            goto trace_path_done;
7810
22.3k
          q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7811
22.3k
          p_PIMgr->StoreStartingAt += q->coordinates;
7812
22.3k
          q+=q->coordinates;
7813
22.3k
          point=end;
7814
          /* consume comma if present (as in elliptical arc above) */
7815
43.5k
          while (isspace((int) ((unsigned char) *p)) != 0)
7816
21.1k
            p++;
7817
22.3k
          if (*p == ',')
7818
1.08k
            p++;
7819
22.3k
        } while (IsPoint(p));
7820
2.92k
        break;
7821
3.06k
      }
7822
      /*
7823
        Cubic Bézier curve
7824
      */
7825
2.92k
      case 's':
7826
5.32k
      case 'S':
7827
5.32k
      {
7828
        /*
7829
          Compute bezier points.
7830
        */
7831
        /*
7832
          Handle multiple pairs of cubic Bezier points when the previous path data
7833
          command was not a cubic Bezier (i.e., not 'c' or 's').
7834
        */
7835
5.32k
        int previous_path_data_command = last_attribute;  /* the previous path data command upon entry to this 'case' */
7836
5.32k
        do
7837
15.2k
        {
7838
15.2k
          points[0]=points[3];
7839
15.2k
          points[1].x=2.0*points[3].x-points[2].x;
7840
15.2k
          points[1].y=2.0*points[3].y-points[2].y;
7841
45.3k
          for (i=2; i < 4; i++)
7842
30.2k
          {
7843
30.2k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7844
30.2k
            if (*token == ',')
7845
2.25k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7846
30.2k
            MagickTracePathAtoF(token,&x,&status);
7847
30.1k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7848
30.1k
            if (*token == ',')
7849
9.19k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7850
30.1k
            MagickTracePathAtoF(token,&y,&status);
7851
30.1k
            if (status == MagickFail)
7852
0
              goto trace_path_done;
7853
30.1k
            if (*p == ',')
7854
6.82k
              p++;
7855
30.1k
            end.x=attribute == 'S' ? x : point.x+x;
7856
30.1k
            end.y=attribute == 'S' ? y : point.y+y;
7857
30.1k
            points[i]=end;
7858
30.1k
          }
7859
15.0k
          if (strchr("CcSs",previous_path_data_command) == (char *) NULL) /* check the ACTUAL previous command */
7860
3.83k
            {
7861
3.83k
              points[0]=point;
7862
3.83k
              points[1]=point;
7863
3.83k
            }
7864
75.0k
          for (i=0; i < 4; i++)
7865
60.0k
            (q+i)->point=points[i];
7866
15.0k
          if ((status &= TraceBezier(p_PIMgr,4)) == MagickFail)
7867
5
            goto trace_path_done;
7868
15.0k
          q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7869
15.0k
          p_PIMgr->StoreStartingAt += q->coordinates;
7870
15.0k
          q+=q->coordinates;
7871
15.0k
          point=end;
7872
15.0k
          previous_path_data_command = attribute;   /* current path data command becomes previous for next loop iteration */
7873
          /* consume comma if present (as in elliptical arc above) */
7874
26.0k
          while (isspace((int) ((unsigned char) *p)) != 0)
7875
11.0k
            p++;
7876
15.0k
          if (*p == ',')
7877
560
            p++;
7878
15.0k
        } while (IsPoint(p));
7879
5.13k
        break;
7880
5.32k
      }
7881
      /*
7882
        Quadratic Bézier curve
7883
      */
7884
9.33k
      case 't':
7885
15.1k
      case 'T':
7886
15.1k
      {
7887
        /*
7888
          Compute bezier points.
7889
        */
7890
        /*
7891
          Handle multiple pairs of quadratic Bezier points when the previous path data
7892
          command was not a quadratic Bezier (i.e., not 'q' or 't').
7893
        */
7894
15.1k
        int previous_path_data_command = last_attribute;  /* the previous path data command upon entry to this 'case' */
7895
15.1k
        do
7896
235k
        {
7897
235k
          points[0]=points[2];
7898
235k
          points[1].x=2.0*points[2].x-points[1].x;
7899
235k
          points[1].y=2.0*points[2].y-points[1].y;
7900
470k
          for (i=2; i < 3; i++)
7901
235k
          {
7902
235k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7903
235k
            if (*token == ',')
7904
1.21k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7905
235k
            MagickTracePathAtoF(token,&x,&status);
7906
235k
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7907
235k
            if (*token == ',')
7908
52.7k
              MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7909
235k
            MagickTracePathAtoF(token,&y,&status);
7910
235k
            if (status == MagickFail)
7911
0
              goto trace_path_done;
7912
235k
            end.x=attribute == 'T' ? x : point.x+x;
7913
235k
            end.y=attribute == 'T' ? y : point.y+y;
7914
235k
            points[i]=end;
7915
235k
          }
7916
235k
          if (strchr("QqTt",previous_path_data_command) == (char *) NULL) /* check the ACTUAL previous command */
7917
12.2k
            {
7918
12.2k
              points[0]=point;
7919
12.2k
              points[1]=point;
7920
12.2k
            }
7921
940k
          for (i=0; i < 3; i++)
7922
705k
            (q+i)->point=points[i];
7923
235k
          if ((status &= TraceBezier(p_PIMgr,3)) == MagickFail)
7924
23
            goto trace_path_done;
7925
234k
          q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
7926
234k
          p_PIMgr->StoreStartingAt += q->coordinates;
7927
234k
          q+=q->coordinates;
7928
234k
          point=end;
7929
234k
          previous_path_data_command = attribute;   /* current path data command becomes previous for next loop iteration */
7930
          /* consume comma if present (as in elliptical arc above) */
7931
434k
          while (isspace((int) ((unsigned char) *p)) != 0)
7932
199k
            p++;
7933
234k
          if (*p == ',')
7934
44.8k
            p++;
7935
234k
        } while (IsPoint(p));
7936
15.0k
        break;
7937
15.1k
      }
7938
      /*
7939
        Line to
7940
      */
7941
449k
      case 'v':
7942
464k
      case 'V':
7943
464k
      {
7944
464k
        do
7945
524k
        {
7946
524k
          MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7947
524k
          if (*token == ',')
7948
942
            MagickGetTracePathToken(p,&p,token,MaxTextExtent,&status);
7949
524k
          MagickTracePathAtoF(token,&y,&status);
7950
524k
          if (status == MagickFail)
7951
0
            goto trace_path_done;
7952
524k
          point.y=attribute == 'V' ? y : point.y+y;
7953
          /* make sure we have at least 100 elements available */
7954
524k
          if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
7955
149
            {
7956
149
              if ((status &= PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
7957
0
                goto trace_path_done;
7958
149
              q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7959
149
            }
7960
524k
          if ((status &= TracePoint(q,point)) == MagickFail)
7961
0
            goto trace_path_done;
7962
524k
          p_PIMgr->StoreStartingAt += q->coordinates;
7963
524k
          q+=q->coordinates;
7964
          /* consume comma if present (as in elliptical arc above) */
7965
572k
          while (isspace((int) ((unsigned char) *p)) != 0)
7966
47.5k
            p++;
7967
524k
          if (*p == ',')
7968
9.67k
            p++;
7969
524k
        } while (IsPoint(p));
7970
464k
        break;
7971
464k
      }
7972
      /*
7973
        Close path
7974
      */
7975
3.89M
      case 'z':
7976
8.10M
      case 'Z':
7977
8.10M
      {
7978
8.10M
        point=start;
7979
        /* make sure we have at least 100 elements available */
7980
8.10M
        if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
7981
1.76k
          {
7982
1.76k
            if ((status &= PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
7983
0
              goto trace_path_done;
7984
1.76k
            q = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base address might have changed */
7985
1.76k
          }
7986
8.10M
        if ((status=TracePoint(q,point)) == MagickFail)
7987
0
            goto trace_path_done;
7988
8.10M
        p_PIMgr->StoreStartingAt += q->coordinates;
7989
8.10M
        q+=q->coordinates;
7990
8.10M
        primitive_info = *pp_PrimitiveInfo + SubpathOffset;
7991
8.10M
        primitive_info->coordinates=q-primitive_info;
7992
8.10M
        PRIMINF_SET_IS_CLOSED_SUBPATH(primitive_info,1);
7993
8.10M
        *number_coordinates+=primitive_info->coordinates;
7994
8.10M
        primitive_info=q;
7995
8.10M
        SubpathOffset = p_PIMgr->StoreStartingAt;
7996
8.10M
        z_count++;
7997
8.10M
        break;
7998
8.10M
      }
7999
44.3M
      default:
8000
44.3M
      {
8001
44.3M
        if (isalpha((int) attribute))
8002
1.26M
          (void) fprintf(stderr,"attribute not recognized: %c\n",attribute);
8003
44.3M
        break;
8004
8.10M
      }
8005
53.2M
    }
8006
53.2M
  }
8007
8008
70.2k
  primitive_info = *pp_PrimitiveInfo + SubpathOffset;
8009
70.2k
  primitive_info->coordinates=q-primitive_info;
8010
70.2k
  *number_coordinates+=primitive_info->coordinates;
8011
93.3M
  for (i=0; i < (long) *number_coordinates; i++)
8012
93.3M
  {
8013
93.3M
    q--;
8014
93.3M
    q->primitive=primitive_type;
8015
93.3M
    if (z_count > 1)
8016
53.7M
      q->method=FillToBorderMethod;
8017
93.3M
  }
8018
70.2k
  q=primitive_info;
8019
8020
70.3k
 trace_path_done:;
8021
8022
70.3k
  if (status == MagickFail)
8023
50
    *number_coordinates = 0;
8024
8025
70.3k
  return status;
8026
70.2k
}
8027
8028
static MagickPassFail
8029
TracePoint(PrimitiveInfo *primitive_info,const PointInfo point)
8030
669M
{
8031
669M
  primitive_info->coordinates=1;
8032
669M
  PRIMINF_CLEAR_FLAGS(primitive_info);
8033
669M
  primitive_info->point=point;
8034
669M
  return MagickPass;
8035
669M
}
8036
8037
static MagickPassFail
8038
TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
8039
  const PointInfo end)
8040
22.2k
{
8041
22.2k
  PointInfo
8042
22.2k
    point;
8043
8044
22.2k
  register PrimitiveInfo
8045
22.2k
    *p;
8046
8047
22.2k
  register long
8048
22.2k
    i;
8049
8050
22.2k
  MagickPassFail
8051
22.2k
    status = MagickPass;
8052
8053
  /*
8054
    Per the SVG spec, if the width and/or height are zero, rendering
8055
    the element is disabled.
8056
  */
8057
22.2k
  if ((start.x == end.x) || (start.y == end.y))
8058
2.48k
  {
8059
2.48k
    primitive_info->coordinates = 0;
8060
2.48k
    goto trace_rectangle_done;
8061
2.48k
  }
8062
19.7k
  p=primitive_info;
8063
19.7k
  if ((status = TracePoint(p,start)) == MagickFail)
8064
0
    goto trace_rectangle_done;
8065
19.7k
  p+=p->coordinates;
8066
19.7k
  point.x=start.x;
8067
19.7k
  point.y=end.y;
8068
19.7k
  if ((status = TracePoint(p,point)) == MagickFail)
8069
0
    goto trace_rectangle_done;
8070
19.7k
  p+=p->coordinates;
8071
19.7k
  if ((status = TracePoint(p,end)) == MagickFail)
8072
0
    goto trace_rectangle_done;
8073
19.7k
  p+=p->coordinates;
8074
19.7k
  point.x=end.x;
8075
19.7k
  point.y=start.y;
8076
19.7k
  if ((status = TracePoint(p,point)) == MagickFail)
8077
0
    goto trace_rectangle_done;
8078
19.7k
  p+=p->coordinates;
8079
19.7k
  if ((status = TracePoint(p,start)) == MagickFail)
8080
0
    goto trace_rectangle_done;
8081
19.7k
  p+=p->coordinates;
8082
19.7k
  primitive_info->coordinates=p-primitive_info;
8083
19.7k
  PRIMINF_CLEAR_FLAGS(primitive_info);
8084
19.7k
  PRIMINF_SET_IS_CLOSED_SUBPATH(primitive_info,1);
8085
118k
  for (i=0; i < (long) primitive_info->coordinates; i++)
8086
98.9k
  {
8087
98.9k
    p->primitive=primitive_info->primitive;
8088
98.9k
    p--;
8089
98.9k
  }
8090
8091
22.2k
 trace_rectangle_done:
8092
8093
22.2k
  return status;
8094
19.7k
}
8095
8096
static MagickPassFail
8097
TraceRoundRectangle(PrimitiveInfoMgr *p_PIMgr,
8098
                    const PointInfo start,const PointInfo end,PointInfo arc)
8099
8.36k
{
8100
8.36k
  PointInfo
8101
8.36k
    degrees,
8102
8.36k
    offset,
8103
8.36k
    point;
8104
8105
8.36k
  register PrimitiveInfo
8106
8.36k
    *p;
8107
8108
8.36k
  size_t
8109
8.36k
    InitialStoreStartingAt;
8110
8111
8.36k
  PrimitiveInfo
8112
8.36k
    *primitive_info,
8113
8.36k
    **pp_PrimitiveInfo;
8114
8115
8.36k
  MagickPassFail
8116
8.36k
    status = MagickPass;
8117
8118
8.36k
  pp_PrimitiveInfo = p_PIMgr->pp_PrimitiveInfo;
8119
8.36k
  InitialStoreStartingAt = p_PIMgr->StoreStartingAt;
8120
8121
8.36k
  offset.x=AbsoluteValue(end.x-start.x);  /* rect width */
8122
8.36k
  offset.y=AbsoluteValue(end.y-start.y);  /* rect height */
8123
  /*
8124
    Per the SVG spec, if the width and/or height are zero, rendering
8125
    the element is disabled.
8126
  */
8127
8.36k
  if ((offset.x == 0.0) || (offset.y == 0.0))
8128
1.61k
  {
8129
1.61k
    (*pp_PrimitiveInfo+InitialStoreStartingAt)->coordinates = 0;
8130
1.61k
    goto round_rectangle_done;
8131
1.61k
  }
8132
8133
6.75k
  if (arc.x > (0.5*offset.x))
8134
1.64k
    arc.x=0.5*offset.x;
8135
6.75k
  if (arc.y > (0.5*offset.y))
8136
1.12k
    arc.y=0.5*offset.y;
8137
8138
6.75k
  point.x=start.x+offset.x-arc.x;
8139
6.75k
  point.y=start.y+arc.y;
8140
6.75k
  degrees.x=270.0;
8141
6.75k
  degrees.y=360.0;
8142
6.75k
  if ((status=TraceEllipse(p_PIMgr,point,arc,degrees)) == MagickFail)
8143
6
    goto round_rectangle_done;
8144
6.74k
  p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base addr might have moved */
8145
6.74k
  p_PIMgr->StoreStartingAt += p->coordinates;
8146
8147
6.74k
  point.x=start.x+offset.x-arc.x;
8148
6.74k
  point.y=start.y+offset.y-arc.y;
8149
6.74k
  degrees.x=0.0;
8150
6.74k
  degrees.y=90.0;
8151
6.74k
  if ((status=TraceEllipse(p_PIMgr,point,arc,degrees)) == MagickFail)
8152
6
    goto round_rectangle_done;
8153
6.74k
  p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base addr might have moved */
8154
6.74k
  p_PIMgr->StoreStartingAt += p->coordinates;
8155
8156
6.74k
  point.x=start.x+arc.x;
8157
6.74k
  point.y=start.y+offset.y-arc.y;
8158
6.74k
  degrees.x=90.0;
8159
6.74k
  degrees.y=180.0;
8160
6.74k
  if ((status=TraceEllipse(p_PIMgr,point,arc,degrees)) == MagickFail)
8161
5
    goto round_rectangle_done;
8162
6.73k
  p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base addr might have moved */
8163
6.73k
  p_PIMgr->StoreStartingAt += p->coordinates;
8164
8165
6.73k
  point.x=start.x+arc.x;
8166
6.73k
  point.y=start.y+arc.y;
8167
6.73k
  degrees.x=180.0;
8168
6.73k
  degrees.y=270.0;
8169
6.73k
  if ((status=TraceEllipse(p_PIMgr,point,arc,degrees)) == MagickFail)
8170
4
    goto round_rectangle_done;
8171
6.73k
  p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;  /* base addr might have moved */
8172
6.73k
  p_PIMgr->StoreStartingAt += p->coordinates;
8173
8174
  /* need only one element, but make sure there is some headroom */
8175
6.73k
  if ((p_PIMgr->StoreStartingAt + 100) > *p_PIMgr->p_AllocCount)
8176
12
    {
8177
12
      if ((status=PrimitiveInfoRealloc(p_PIMgr,100)) == MagickFail)
8178
0
        goto round_rectangle_done;
8179
12
    }
8180
6.73k
  p = *pp_PrimitiveInfo + p_PIMgr->StoreStartingAt;
8181
6.73k
  if ((status=TracePoint(p,(*pp_PrimitiveInfo+InitialStoreStartingAt)->point))  == MagickFail)
8182
0
    goto round_rectangle_done;
8183
6.73k
  p+=p->coordinates;
8184
6.73k
  p_PIMgr->StoreStartingAt = InitialStoreStartingAt;  /* restore original value */
8185
8186
6.73k
  primitive_info = *pp_PrimitiveInfo + InitialStoreStartingAt;
8187
6.73k
  primitive_info->coordinates=p-primitive_info;
8188
6.73k
  PRIMINF_CLEAR_FLAGS(primitive_info);
8189
6.73k
  PRIMINF_SET_IS_CLOSED_SUBPATH(primitive_info,1);
8190
  /* NOTE: p pointing just past last traced point! */
8191
51.2M
  for (p--; p > primitive_info; p--)
8192
51.2M
    p->primitive=primitive_info->primitive;
8193
8194
8.36k
 round_rectangle_done:;
8195
8196
8.36k
  return status;
8197
6.73k
}
8198
8199
static MagickPassFail
8200
TraceSquareLinecap(PrimitiveInfo *primitive_info,
8201
                   const unsigned long number_vertices,const double offset)
8202
184k
{
8203
184k
  double
8204
184k
    distance;
8205
8206
184k
  register double
8207
184k
    dx,
8208
184k
    dy;
8209
8210
184k
  register long
8211
184k
    i;
8212
8213
184k
  long
8214
184k
    j;
8215
8216
184k
  dx=0.0;
8217
184k
  dy=0.0;
8218
189k
  for (i=1; i < (long) number_vertices; i++)
8219
189k
  {
8220
189k
    dx=primitive_info[0].point.x-primitive_info[i].point.x;
8221
189k
    dy=primitive_info[0].point.y-primitive_info[i].point.y;
8222
189k
    if ((fabs(dx) >= MagickEpsilon) || (fabs(dy) >= MagickEpsilon))
8223
184k
      break;
8224
189k
  }
8225
184k
  if (i == (long) number_vertices)
8226
0
    i=number_vertices-1;
8227
184k
  distance=sqrt(dx*dx+dy*dy+MagickEpsilon);
8228
184k
  primitive_info[0].point.x=primitive_info[i].point.x+
8229
184k
    dx*(distance+offset)/distance;
8230
184k
  primitive_info[0].point.y=primitive_info[i].point.y+
8231
184k
    dy*(distance+offset)/distance;
8232
292k
  for (j=(long) number_vertices-2; j >= 0;  j--)
8233
292k
  {
8234
292k
    dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
8235
292k
    dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
8236
292k
    if ((fabs(dx) >= MagickEpsilon) || (fabs(dy) >= MagickEpsilon))
8237
184k
      break;
8238
292k
  }
8239
184k
  distance=sqrt(dx*dx+dy*dy+MagickEpsilon);
8240
184k
  primitive_info[number_vertices-1].point.x=primitive_info[j].point.x+
8241
184k
    dx*(distance+offset)/distance;
8242
184k
  primitive_info[number_vertices-1].point.y=primitive_info[j].point.y+
8243
184k
    dy*(distance+offset)/distance;
8244
8245
184k
  return MagickPass;
8246
184k
}
8247
8248
static PrimitiveInfo *
8249
TraceStrokePolygon(const Image *image,  /* added Image* param so DrawInfo::stroke_width can be clamped */
8250
                   const DrawInfo *draw_info,
8251
                   const PrimitiveInfo *primitive_info,
8252
                   ExceptionInfo *exception)
8253
3.00M
{
8254
3.00M
  typedef struct _LineSegment
8255
3.00M
  {
8256
3.00M
    double
8257
3.00M
      p,
8258
3.00M
      q;
8259
3.00M
  } LineSegment;
8260
8261
3.00M
  LineSegment
8262
3.00M
    dx={0,0},
8263
3.00M
    dy={0,0},
8264
3.00M
    inverse_slope={0,0},
8265
3.00M
    slope={0,0},
8266
3.00M
    theta={0,0};
8267
8268
3.00M
  MagickBool
8269
3.00M
    closed_path;
8270
8271
3.00M
  double
8272
3.00M
    delta_theta,
8273
3.00M
    dot_product,
8274
3.00M
    mid,
8275
3.00M
    miterlimit,
8276
3.00M
    stroke_width_limited;
8277
8278
3.00M
  PointInfo
8279
3.00M
    box_p[5],
8280
3.00M
    box_q[5],
8281
3.00M
    center,
8282
3.00M
    offset,
8283
3.00M
    *path_p,
8284
3.00M
    *path_q;
8285
8286
3.00M
  PrimitiveInfo
8287
3.00M
    *polygon_primitive,
8288
3.00M
    *stroke_polygon = (PrimitiveInfo *) NULL;
8289
8290
3.00M
  register long
8291
3.00M
    i;
8292
8293
3.00M
  size_t
8294
3.00M
    arc_segments,
8295
3.00M
    max_strokes_p,
8296
3.00M
    max_strokes_q,
8297
3.00M
    max_strokes_extra,
8298
3.00M
    n,
8299
3.00M
    number_vertices;
8300
8301
3.00M
  unsigned long
8302
3.00M
    j,
8303
3.00M
    p,
8304
3.00M
    q;
8305
8306
3.00M
  MagickPassFail
8307
3.00M
    status = MagickPass;
8308
8309
  /*
8310
    Compute initial sizes for paths based on the number of primitive coordinates.  We
8311
    will always allocate max_strokes_extra additional points, so that for each loop
8312
    iteration there will always be enough space, provided we haven't passed
8313
    max_strokes_{p|q} yet.
8314
  */
8315
3.00M
  number_vertices=primitive_info->coordinates;
8316
3.00M
  max_strokes_p=max_strokes_q=2*number_vertices;
8317
3.00M
  max_strokes_extra=6*BezierQuantum+360;  /* will always allocate this much extra */
8318
8319
  /* moved path_p and path_q mem alloc to later since we might not need them */
8320
8321
3.00M
  polygon_primitive=
8322
3.00M
    MagickAllocateResourceLimitedArray(PrimitiveInfo *,
8323
3.00M
                                       ((size_t) number_vertices+2),
8324
3.00M
                                       sizeof(PrimitiveInfo));
8325
3.00M
  if (polygon_primitive == (PrimitiveInfo *) NULL)
8326
23
    {
8327
23
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8328
23
                      UnableToDrawOnImage);
8329
23
      return((PrimitiveInfo *) NULL);
8330
23
    }
8331
3.00M
  (void) memcpy(polygon_primitive,primitive_info,number_vertices*
8332
3.00M
    sizeof(PrimitiveInfo));
8333
3.00M
  closed_path=PRIMINF_GET_IS_CLOSED_SUBPATH(&primitive_info[0]);
8334
3.00M
  if ((draw_info->linejoin == RoundJoin) ||
8335
1.88M
      ((draw_info->linejoin == MiterJoin) && closed_path))
8336
2.11M
    {
8337
2.11M
      polygon_primitive[number_vertices]=primitive_info[1];
8338
2.11M
      number_vertices++;
8339
2.11M
    }
8340
3.00M
  polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
8341
  /*
8342
    Compute the slope for the first line segment, p.
8343
  */
8344
3.33M
  for (n=1; n < number_vertices; n++)
8345
3.32M
  {
8346
3.32M
    dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
8347
3.32M
    dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
8348
3.32M
    if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
8349
3.00M
      break;
8350
3.32M
  }
8351
3.00M
  if (n == number_vertices)
8352
1.36k
    {
8353
      /*
8354
        If we get here the subpath consists entirely of "zero length" (within MagickEpsilon)
8355
        segments.  According to the SVG spec:  "Any zero length subpath shall not be stroked
8356
        if the 'stroke-linecap' property has a value of butt but shall be stroked if the
8357
        'stroke-linecap' property has a value of round or square".  Since 'stroke-linecap'
8358
        is only used for open paths, this is only significant if the path is not closed.
8359
      */
8360
1.36k
      MagickBool DoStroke;
8361
1.36k
      DoStroke = (!closed_path && (draw_info->linecap == RoundCap));
8362
1.36k
      if  ( !DoStroke )
8363
1.22k
        {/*skip stroking*/
8364
          /* create polygon with one element and 0 coords; DrawPolygonPrimitive() will ignore it */
8365
1.22k
          stroke_polygon = MagickAllocateResourceLimitedArray(PrimitiveInfo *,
8366
1.22k
                                                              1,sizeof(PrimitiveInfo));
8367
1.22k
          if (stroke_polygon == (PrimitiveInfo *) NULL)
8368
0
            {
8369
0
              ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8370
0
                              UnableToDrawOnImage);
8371
0
            }
8372
1.22k
          else
8373
1.22k
            {
8374
1.22k
              stroke_polygon[0] = polygon_primitive[0];
8375
1.22k
              stroke_polygon[0].coordinates = 0;
8376
1.22k
            }
8377
1.22k
          MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8378
1.22k
          return(stroke_polygon);
8379
1.22k
        }/*skip stroking*/
8380
140
      n=number_vertices-1;
8381
140
    }
8382
8383
  /* moved path_p and path_q mem allocs to here */
8384
8385
  /*
8386
    Allocate paths.
8387
  */
8388
3.00M
  path_p=MagickAllocateResourceLimitedArray(PointInfo *,
8389
3.00M
                                            (size_t) max_strokes_p+max_strokes_extra,
8390
3.00M
                                            sizeof(PointInfo));
8391
3.00M
  if (path_p == (PointInfo *) NULL)
8392
19
    {
8393
19
      MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8394
19
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8395
19
                      UnableToDrawOnImage);
8396
19
      return((PrimitiveInfo *) NULL);
8397
19
    }
8398
3.00M
  path_q=MagickAllocateResourceLimitedArray(PointInfo *,
8399
3.00M
                                            (size_t) max_strokes_q+max_strokes_extra,
8400
3.00M
                                            sizeof(PointInfo));
8401
3.00M
  if (path_q == (PointInfo *) NULL)
8402
29
    {
8403
29
      MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8404
29
      MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8405
29
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8406
29
                      UnableToDrawOnImage);
8407
29
      return((PrimitiveInfo *) NULL);
8408
29
    }
8409
8410
3.00M
  slope.p=0.0;
8411
3.00M
  inverse_slope.p=0.0;
8412
3.00M
  if (fabs(dx.p) < MagickEpsilon)
8413
334k
    {
8414
334k
      if (dx.p >= 0.0)
8415
333k
        slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
8416
1.08k
      else
8417
1.08k
        slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
8418
334k
    }
8419
2.66M
  else
8420
2.66M
    if (fabs(dy.p) < MagickEpsilon)
8421
255k
      {
8422
255k
        if (dy.p >= 0.0)
8423
227k
          inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
8424
28.8k
        else
8425
28.8k
          inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
8426
255k
      }
8427
2.41M
    else
8428
2.41M
      {
8429
2.41M
        slope.p=dy.p/dx.p;
8430
2.41M
        inverse_slope.p=(-1.0/slope.p);
8431
2.41M
      }
8432
8433
  /* sanity check for excessively big stroke_width (ticket #515) */
8434
3.00M
  if  ( (stroke_width_limited = STROKE_WIDTH_LIMIT(image)) > draw_info->stroke_width )
8435
2.71M
      stroke_width_limited = draw_info->stroke_width;
8436
8437
3.00M
  mid=ExpandAffine(&draw_info->affine)*stroke_width_limited/2.0;
8438
3.00M
  miterlimit=(double) draw_info->miterlimit*draw_info->miterlimit*mid*mid;
8439
3.00M
  if (((draw_info->linecap == SquareCap) && !closed_path) ||
8440
2.81M
      (number_vertices != (size_t) ((long) number_vertices)))
8441
184k
    {
8442
184k
      if ((status=TraceSquareLinecap(polygon_primitive,(long) number_vertices,mid)) == MagickFail)
8443
0
        goto trace_stroke_polygon_done;
8444
184k
    }
8445
3.00M
  offset.x=sqrt(mid*mid/(inverse_slope.p*inverse_slope.p+1.0));
8446
3.00M
  offset.y=offset.x*inverse_slope.p;
8447
3.00M
  if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
8448
1.70M
    {
8449
1.70M
      box_p[0].x=polygon_primitive[0].point.x-offset.x;
8450
1.70M
      box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
8451
1.70M
      box_p[1].x=polygon_primitive[n].point.x-offset.x;
8452
1.70M
      box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
8453
1.70M
      box_q[0].x=polygon_primitive[0].point.x+offset.x;
8454
1.70M
      box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
8455
1.70M
      box_q[1].x=polygon_primitive[n].point.x+offset.x;
8456
1.70M
      box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
8457
1.70M
    }
8458
1.29M
  else
8459
1.29M
    {
8460
1.29M
      box_p[0].x=polygon_primitive[0].point.x+offset.x;
8461
1.29M
      box_p[0].y=polygon_primitive[0].point.y+offset.y;
8462
1.29M
      box_p[1].x=polygon_primitive[n].point.x+offset.x;
8463
1.29M
      box_p[1].y=polygon_primitive[n].point.y+offset.y;
8464
1.29M
      box_q[0].x=polygon_primitive[0].point.x-offset.x;
8465
1.29M
      box_q[0].y=polygon_primitive[0].point.y-offset.y;
8466
1.29M
      box_q[1].x=polygon_primitive[n].point.x-offset.x;
8467
1.29M
      box_q[1].y=polygon_primitive[n].point.y-offset.y;
8468
1.29M
    }
8469
  /*
8470
    Create strokes for the line join attribute: bevel, miter, round.
8471
  */
8472
3.00M
  p=0;
8473
3.00M
  q=0;
8474
3.00M
  path_q[p++]=box_q[0];
8475
3.00M
  path_p[q++]=box_p[0];
8476
56.6M
  for (i=(long) n+1; i < (long) number_vertices; i++)
8477
53.6M
  {
8478
    /*
8479
      Compute the slope for this line segment, q.
8480
    */
8481
53.6M
    dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
8482
53.6M
    dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
8483
53.6M
    dot_product=dx.q*dx.q+dy.q*dy.q;
8484
53.6M
    if (dot_product < 0.25)
8485
7.33M
      continue;
8486
46.3M
    slope.q=0.0;
8487
46.3M
    inverse_slope.q=0.0;
8488
46.3M
    if (fabs(dx.q) < MagickEpsilon)
8489
146k
      {
8490
146k
        if (dx.q >= 0.0)
8491
137k
          slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
8492
8.26k
        else
8493
8.26k
          slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
8494
146k
      }
8495
46.1M
    else
8496
46.1M
      if (fabs(dy.q) < MagickEpsilon)
8497
1.85M
        {
8498
1.85M
          if (dy.q >= 0.0)
8499
1.46M
            inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
8500
388k
          else
8501
388k
            inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
8502
1.85M
        }
8503
44.3M
      else
8504
44.3M
        {
8505
44.3M
          slope.q=dy.q/dx.q;
8506
44.3M
          inverse_slope.q=(-1.0/slope.q);
8507
44.3M
        }
8508
46.3M
    offset.x=sqrt(mid*mid/(inverse_slope.q*inverse_slope.q+1.0));
8509
46.3M
    offset.y=offset.x*inverse_slope.q;
8510
46.3M
    dot_product=dy.q*offset.x-dx.q*offset.y;
8511
46.3M
    if (dot_product > 0.0)
8512
23.6M
      {
8513
23.6M
        box_p[2].x=polygon_primitive[n].point.x-offset.x;
8514
23.6M
        box_p[2].y=polygon_primitive[n].point.y-offset.y;
8515
23.6M
        box_p[3].x=polygon_primitive[i].point.x-offset.x;
8516
23.6M
        box_p[3].y=polygon_primitive[i].point.y-offset.y;
8517
23.6M
        box_q[2].x=polygon_primitive[n].point.x+offset.x;
8518
23.6M
        box_q[2].y=polygon_primitive[n].point.y+offset.y;
8519
23.6M
        box_q[3].x=polygon_primitive[i].point.x+offset.x;
8520
23.6M
        box_q[3].y=polygon_primitive[i].point.y+offset.y;
8521
23.6M
      }
8522
22.6M
    else
8523
22.6M
      {
8524
22.6M
        box_p[2].x=polygon_primitive[n].point.x+offset.x;
8525
22.6M
        box_p[2].y=polygon_primitive[n].point.y+offset.y;
8526
22.6M
        box_p[3].x=polygon_primitive[i].point.x+offset.x;
8527
22.6M
        box_p[3].y=polygon_primitive[i].point.y+offset.y;
8528
22.6M
        box_q[2].x=polygon_primitive[n].point.x-offset.x;
8529
22.6M
        box_q[2].y=polygon_primitive[n].point.y-offset.y;
8530
22.6M
        box_q[3].x=polygon_primitive[i].point.x-offset.x;
8531
22.6M
        box_q[3].y=polygon_primitive[i].point.y-offset.y;
8532
22.6M
      }
8533
46.3M
    if (fabs(slope.p-slope.q) <= MagickEpsilon)
8534
5.72M
      {
8535
5.72M
        box_p[4]=box_p[1];
8536
5.72M
        box_q[4]=box_q[1];
8537
5.72M
      }
8538
40.5M
    else
8539
40.5M
      {
8540
40.5M
        box_p[4].x=(slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
8541
40.5M
          box_p[3].y)/(slope.p-slope.q);
8542
40.5M
        box_p[4].y=slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y;
8543
40.5M
        box_q[4].x=(slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
8544
40.5M
          box_q[3].y)/(slope.p-slope.q);
8545
40.5M
        box_q[4].y=slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y;
8546
40.5M
      }
8547
46.3M
    if (p >= max_strokes_p)
8548
1.17k
      {/*p pointing into extra; time to realloc*/
8549
1.17k
        PointInfo *new_path_p;
8550
1.17k
         max_strokes_p+=max_strokes_extra;
8551
1.17k
         new_path_p=MagickReallocateResourceLimitedArray(PointInfo *,path_p,
8552
1.17k
                                                         (size_t) max_strokes_p+
8553
1.17k
                                                         max_strokes_extra,
8554
1.17k
                                                         sizeof(PointInfo));
8555
1.17k
         if (new_path_p == (PointInfo *) NULL)
8556
0
           {
8557
0
             MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8558
0
             MagickFreeResourceLimitedMemory(PointInfo *,path_q);
8559
0
             MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8560
0
             ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8561
0
                             UnableToDrawOnImage);
8562
0
             return((PrimitiveInfo *) NULL);
8563
0
           }
8564
1.17k
         path_p=new_path_p;
8565
1.17k
      }/*p pointing into extra; time to realloc*/
8566
46.3M
    if (q >= max_strokes_q)
8567
1.29k
      {/*q pointing into extra; time to realloc*/
8568
1.29k
        PointInfo *new_path_q;
8569
1.29k
         max_strokes_q+=max_strokes_extra;
8570
1.29k
         new_path_q=MagickReallocateResourceLimitedArray(PointInfo *,path_q,
8571
1.29k
                                              (size_t) max_strokes_q+max_strokes_extra,
8572
1.29k
                                              sizeof(PointInfo));
8573
1.29k
         if (new_path_q == (PointInfo *) NULL)
8574
0
           {
8575
0
             MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8576
0
             MagickFreeResourceLimitedMemory(PointInfo *,path_q);
8577
0
             MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8578
0
             ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8579
0
                             UnableToDrawOnImage);
8580
0
             return((PrimitiveInfo *) NULL);
8581
0
           }
8582
1.29k
         path_q=new_path_q;
8583
1.29k
      }/*q pointing into extra; time to realloc*/
8584
46.3M
    dot_product=dx.q*dy.p-dx.p*dy.q;
8585
46.3M
    if (dot_product <= 0.0)
8586
32.7M
      switch (draw_info->linejoin)
8587
32.7M
      {
8588
63.1k
        case BevelJoin:
8589
63.1k
        {
8590
63.1k
          path_q[q++]=box_q[1];
8591
63.1k
          path_q[q++]=box_q[2];
8592
63.1k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8593
63.1k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8594
63.1k
          if (dot_product <= miterlimit)
8595
35.3k
            path_p[p++]=box_p[4];
8596
27.7k
          else
8597
27.7k
            {
8598
27.7k
              path_p[p++]=box_p[1];
8599
27.7k
              path_p[p++]=box_p[2];
8600
27.7k
            }
8601
63.1k
          break;
8602
0
        }
8603
31.6M
        case MiterJoin:
8604
31.6M
        {
8605
31.6M
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8606
31.6M
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8607
31.6M
          if (dot_product > miterlimit)
8608
17.6M
            {
8609
17.6M
              path_q[q++]=box_q[1];
8610
17.6M
              path_q[q++]=box_q[2];
8611
17.6M
              path_p[p++]=box_p[1];
8612
17.6M
              path_p[p++]=box_p[2];
8613
17.6M
           }
8614
13.9M
         else
8615
13.9M
           {
8616
13.9M
             path_q[q++]=box_q[4];
8617
13.9M
             path_p[p++]=box_p[4];
8618
13.9M
           }
8619
31.6M
          break;
8620
0
        }
8621
1.09M
        case RoundJoin:
8622
1.09M
        {
8623
1.09M
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8624
1.09M
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8625
1.09M
          if (dot_product <= miterlimit)
8626
53.7k
            path_p[p++]=box_p[4];
8627
1.03M
          else
8628
1.03M
            {
8629
1.03M
              path_p[p++]=box_p[1];
8630
1.03M
              path_p[p++]=box_p[2];
8631
1.03M
            }
8632
1.09M
          center=polygon_primitive[n].point;
8633
1.09M
          theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
8634
1.09M
          theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
8635
1.09M
          if (theta.q < theta.p)
8636
210k
            theta.q+=2.0*MagickPI;
8637
1.09M
          arc_segments=(long) ceil((theta.q-theta.p)/(2.0*sqrt(1.0/mid)));
8638
          /* in case arc_segments is big */
8639
1.09M
          if  ( (q+arc_segments) >= max_strokes_q )
8640
5.62k
            {/*q+arc_segments will point into extra; time to realloc*/
8641
5.62k
              PointInfo *new_path_q;
8642
5.62k
              max_strokes_q+=arc_segments+max_strokes_extra;
8643
5.62k
              new_path_q=MagickReallocateResourceLimitedArray(PointInfo *,path_q,
8644
5.62k
                                                   (size_t) max_strokes_q+max_strokes_extra,
8645
5.62k
                                                   sizeof(PointInfo));
8646
5.62k
              if (new_path_q == (PointInfo *) NULL)
8647
41
                {
8648
41
                  MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8649
41
                  MagickFreeResourceLimitedMemory(PointInfo *,path_q);
8650
41
                  MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8651
41
                  ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8652
41
                                  UnableToDrawOnImage);
8653
41
                  return((PrimitiveInfo *) NULL);
8654
41
                }
8655
5.57k
              path_q=new_path_q;
8656
5.57k
            }/*q+arc_segments will point into extra; time to realloc*/
8657
1.09M
          path_q[q].x=box_q[1].x;
8658
1.09M
          path_q[q].y=box_q[1].y;
8659
1.09M
          q++;
8660
96.6M
          for (j=1; j < arc_segments; j++)
8661
95.5M
          {
8662
95.5M
            delta_theta=j*(theta.q-theta.p)/arc_segments;
8663
95.5M
            path_q[q].x=center.x+mid*
8664
95.5M
              cos(fmod(theta.p+delta_theta,DegreesToRadians(360.0)));
8665
95.5M
            path_q[q].y=center.y+mid*
8666
95.5M
              sin(fmod(theta.p+delta_theta,DegreesToRadians(360.0)));
8667
95.5M
            q++;
8668
95.5M
          }
8669
1.09M
          path_q[q++]=box_q[2];
8670
1.09M
          break;
8671
1.09M
        }
8672
0
        default:
8673
0
          break;
8674
32.7M
      }
8675
13.5M
    else
8676
13.5M
      switch (draw_info->linejoin)
8677
13.5M
      {
8678
45.3k
        case BevelJoin:
8679
45.3k
        {
8680
45.3k
          path_p[p++]=box_p[1];
8681
45.3k
          path_p[p++]=box_p[2];
8682
45.3k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8683
45.3k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8684
45.3k
          if (dot_product <= miterlimit)
8685
26.6k
            path_q[q++]=box_q[4];
8686
18.6k
          else
8687
18.6k
            {
8688
18.6k
              path_q[q++]=box_q[1];
8689
18.6k
              path_q[q++]=box_q[2];
8690
18.6k
            }
8691
45.3k
          break;
8692
0
        }
8693
12.9M
        case MiterJoin:
8694
12.9M
        {
8695
12.9M
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8696
12.9M
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8697
12.9M
          if (dot_product <= miterlimit)
8698
3.41M
            {
8699
3.41M
              path_q[q++]=box_q[4];
8700
3.41M
              path_p[p++]=box_p[4];
8701
3.41M
            }
8702
9.50M
          else
8703
9.50M
            {
8704
9.50M
              path_q[q++]=box_q[1];
8705
9.50M
              path_q[q++]=box_q[2];
8706
9.50M
              path_p[p++]=box_p[1];
8707
9.50M
              path_p[p++]=box_p[2];
8708
9.50M
            }
8709
12.9M
          break;
8710
0
        }
8711
564k
        case RoundJoin:
8712
564k
        {
8713
564k
          dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
8714
564k
            (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
8715
564k
          if (dot_product <= miterlimit)
8716
39.4k
            path_q[q++]=box_q[4];
8717
525k
          else
8718
525k
            {
8719
525k
              path_q[q++]=box_q[1];
8720
525k
              path_q[q++]=box_q[2];
8721
525k
            }
8722
564k
          center=polygon_primitive[n].point;
8723
564k
          theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
8724
564k
          theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
8725
564k
          if (theta.p < theta.q)
8726
200k
            theta.p+=2.0*MagickPI;
8727
564k
          arc_segments=(long) ceil((theta.p-theta.q)/(2.0*sqrt(1.0/mid)));
8728
          /* in case arc_segments is big */
8729
564k
          if  ( (p+arc_segments) >= max_strokes_p )
8730
1.85k
            {/*p+arc_segments will point into extra; time to realloc*/
8731
1.85k
              PointInfo *new_path_p;
8732
1.85k
              max_strokes_p+=arc_segments+max_strokes_extra;
8733
1.85k
              new_path_p=MagickReallocateResourceLimitedArray(PointInfo *,path_p,
8734
1.85k
                                                   (size_t) max_strokes_p+max_strokes_extra,
8735
1.85k
                                                   sizeof(PointInfo));
8736
1.85k
              if (new_path_p == (PointInfo *) NULL)
8737
61
                {
8738
61
                  MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8739
61
                  MagickFreeResourceLimitedMemory(PointInfo *,path_q);
8740
61
                  MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8741
61
                  ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8742
61
                                  UnableToDrawOnImage);
8743
61
                  return((PrimitiveInfo *) NULL);
8744
61
                }
8745
1.79k
              path_p=new_path_p;
8746
1.79k
            }/*p+arc_segments will point into extra; time to realloc*/
8747
564k
          path_p[p++]=box_p[1];
8748
90.4M
          for (j=1; j < arc_segments; j++)
8749
89.8M
          {
8750
89.8M
            delta_theta=j*(theta.q-theta.p)/arc_segments;
8751
89.8M
            path_p[p].x=center.x+mid*
8752
89.8M
              cos(fmod(theta.p+delta_theta,DegreesToRadians(360.0)));
8753
89.8M
            path_p[p].y=center.y+mid*
8754
89.8M
              sin(fmod(theta.p+delta_theta,DegreesToRadians(360.0)));
8755
89.8M
            p++;
8756
89.8M
          }
8757
564k
          path_p[p++]=box_p[2];
8758
564k
          break;
8759
564k
        }
8760
0
        default:
8761
0
          break;
8762
13.5M
      }
8763
46.3M
    slope.p=slope.q;
8764
46.3M
    inverse_slope.p=inverse_slope.q;
8765
46.3M
    box_p[0]=box_p[2];
8766
46.3M
    box_p[1]=box_p[3];
8767
46.3M
    box_q[0]=box_q[2];
8768
46.3M
    box_q[1]=box_q[3];
8769
46.3M
    dx.p=dx.q;
8770
46.3M
    dy.p=dy.q;
8771
46.3M
    n=i;
8772
46.3M
  }
8773
3.00M
  path_p[p++]=box_p[1];
8774
3.00M
  path_q[q++]=box_q[1];
8775
  /*
8776
    Trace stroked polygon.
8777
  */
8778
3.00M
  stroke_polygon=
8779
3.00M
    MagickAllocateResourceLimitedArray(PrimitiveInfo *,
8780
3.00M
                        MagickArraySize((size_t) p+q+2,(size_t) closed_path+2),
8781
3.00M
                        sizeof(PrimitiveInfo));
8782
3.00M
  if (stroke_polygon == (PrimitiveInfo *) NULL)
8783
40
    {
8784
40
      ThrowException3(exception,ResourceLimitError,MemoryAllocationFailed,
8785
40
                      UnableToDrawOnImage);
8786
40
      goto trace_stroke_polygon_done;
8787
40
    }
8788
8789
83.5M
  for (i=0; i < (long) p; i++)
8790
80.5M
    {
8791
80.5M
      stroke_polygon[i]=polygon_primitive[0];
8792
80.5M
      stroke_polygon[i].point=path_p[i];
8793
80.5M
    }
8794
3.00M
  if (closed_path)
8795
1.19M
    {
8796
1.19M
      stroke_polygon[i]=polygon_primitive[0];
8797
1.19M
      stroke_polygon[i].point=stroke_polygon[0].point;
8798
1.19M
      i++;
8799
1.19M
    }
8800
93.5M
  for ( ; i < (long) (p+q+closed_path); i++)
8801
90.5M
    {
8802
90.5M
      stroke_polygon[i]=polygon_primitive[0];
8803
90.5M
      stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
8804
90.5M
    }
8805
3.00M
  if (closed_path)
8806
1.19M
    {
8807
1.19M
      stroke_polygon[i]=polygon_primitive[0];
8808
1.19M
      stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
8809
1.19M
      i++;
8810
1.19M
    }
8811
3.00M
  stroke_polygon[i]=polygon_primitive[0];
8812
3.00M
  stroke_polygon[i].point=stroke_polygon[0].point;
8813
3.00M
  i++;
8814
3.00M
  stroke_polygon[i].primitive=UndefinedPrimitive;
8815
3.00M
  stroke_polygon[0].coordinates=(size_t) p+q+(size_t) 2*closed_path+1;
8816
8817
3.00M
 trace_stroke_polygon_done:;
8818
8819
3.00M
  MagickFreeResourceLimitedMemory(PointInfo *,path_p);
8820
3.00M
  MagickFreeResourceLimitedMemory(PointInfo *,path_q);
8821
3.00M
  MagickFreeResourceLimitedMemory(PrimitiveInfo *,polygon_primitive);
8822
3.00M
  return(stroke_polygon);
8823
3.00M
}
8824
8825
#if 0
8826
/* is the DrawInfo drawn as SVG compliant */
8827
static MagickBool
8828
IsDrawInfoClippingPath(const DrawInfo * draw_info)
8829
{
8830
  return(((draw_info->flags&0x2U)==2U)?MagickTrue:MagickFalse);
8831
}
8832
#endif
8833
8834
/* is the DrawInfo drawn as SVG compliant */
8835
static MagickBool
8836
IsDrawInfoSVGCompliant(const DrawInfo * draw_info)
8837
19.1k
{
8838
19.1k
  return(((draw_info->flags&0x1U)==1U)?MagickTrue:MagickFalse);
8839
19.1k
}
8840
8841
/* is the DrawInfo drawn as an SVG compliant clipping path */
8842
static MagickBool
8843
IsDrawInfoSVGCompliantClippingPath(const DrawInfo * draw_info)
8844
397k
{
8845
397k
  return(((draw_info->flags&0x3U)==3U)?MagickTrue:MagickFalse);
8846
397k
}
8847
8848
/* tag the DrawInfo as being a clipping path or not */
8849
static void
8850
SetDrawInfoClippingPath(DrawInfo * draw_info, MagickBool ClippingPath)
8851
4.38M
{
8852
4.38M
  draw_info->flags = (draw_info->flags & (~0x2U)) | (ClippingPath?2U:0U);
8853
4.38M
}
8854
8855
/* tag the DrawInfo as being drawn as SVG compliant or not */
8856
static void
8857
SetDrawInfoSVGCompliant(DrawInfo * draw_info, MagickBool SVGCompliant)
8858
4.41M
{
8859
4.41M
  draw_info->flags = (draw_info->flags & (~0x1U)) | (SVGCompliant?1U:0U);
8860
4.41M
}