/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(¤t); |
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 | } |