clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name svgtiny_path.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-libsvgtiny -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -D _BSD_SOURCE -D _DEFAULT_SOURCE -I /var/lib/jenkins/workspace/scan-build-libsvgtiny/include/ -I /var/lib/jenkins/workspace/scan-build-libsvgtiny/src -D _ALIGNED=__attribute__((aligned)) -D STMTEXPR=1 -D DEBUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Og -Wwrite-strings -Wno-error -std=c99 -fconst-strings -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-libsvgtiny -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-libsvgtiny/clangScanBuildReports/2024-12-27-083421-3320267-1 -x c src/svgtiny_path.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | #include <stddef.h> |
9 | #include <math.h> |
10 | #include <stdlib.h> |
11 | |
12 | #include "svgtiny.h" |
13 | #include "svgtiny_internal.h" |
14 | #include "svgtiny_parse.h" |
15 | |
16 | #define TAU 6.28318530717958647692 |
17 | |
18 | #ifndef M_PI |
19 | #define M_PI 3.14159265358979323846 |
20 | #endif |
21 | |
22 | #ifndef M_PI_2 |
23 | #define M_PI_2 1.57079632679489661923 |
24 | #endif |
25 | |
26 | #define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0) |
27 | #define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI) |
28 | |
29 | |
30 | struct internal_points { |
31 | float *p; |
32 | unsigned int used; |
33 | size_t alloc; |
34 | }; |
35 | |
36 | |
37 | |
38 | |
39 | struct internal_path_state { |
40 | struct internal_points path; |
41 | struct internal_points cmd; |
42 | float prev_x; |
43 | float prev_y; |
44 | float subpath_x; |
45 | float subpath_y; |
46 | float cubic_x; |
47 | float cubic_y; |
48 | float quad_x; |
49 | float quad_y; |
50 | }; |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | static inline svgtiny_code |
60 | ensure_internal_points(struct internal_points *ipts, unsigned int count) |
61 | { |
62 | svgtiny_code res = svgtiny_OK; |
63 | float *nalloc; |
64 | |
65 | if ((ipts->used + count) > ipts->alloc) { |
66 | nalloc = realloc(ipts->p, sizeof ipts->p[0] * (ipts->alloc + 84)); |
67 | if (nalloc == NULL) { |
68 | return svgtiny_OUT_OF_MEMORY; |
69 | } |
70 | ipts->p = nalloc; |
71 | ipts->alloc = ipts->alloc + 84; |
72 | } |
73 | return res; |
74 | } |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | static inline svgtiny_code |
82 | generate_path_move(struct internal_path_state *state, int relative) |
83 | { |
84 | svgtiny_code res; |
85 | unsigned int cmdpc = 0; |
86 | |
87 | if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) { |
88 | |
89 | return svgtiny_SVG_ERROR; |
90 | } |
91 | |
92 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 2) { |
93 | res = ensure_internal_points(&state->path, 3); |
94 | if (res != svgtiny_OK) { |
95 | return res; |
96 | } |
97 | |
98 | if (relative != 0) { |
99 | state->cmd.p[cmdpc] += state->prev_x; |
100 | state->cmd.p[cmdpc + 1] += state->prev_y; |
101 | } |
102 | |
103 | if (cmdpc == 0) { |
104 | state->path.p[state->path.used++] = svgtiny_PATH_MOVE; |
105 | |
106 | state->path.p[state->path.used++] = |
107 | state->subpath_x = |
108 | state->cubic_x = |
109 | state->prev_x = state->cmd.p[cmdpc]; |
110 | state->path.p[state->path.used++] = |
111 | state->subpath_y = |
112 | state->cubic_y = |
113 | state->prev_y = state->cmd.p[cmdpc + 1]; |
114 | } else { |
115 | state->path.p[state->path.used++] = svgtiny_PATH_LINE; |
116 | |
117 | state->path.p[state->path.used++] = |
118 | state->cubic_x = |
119 | state->quad_x = |
120 | state->prev_x = state->cmd.p[cmdpc]; |
121 | state->path.p[state->path.used++] = |
122 | state->cubic_y = |
123 | state->quad_y = |
124 | state->prev_y = state->cmd.p[cmdpc + 1]; |
125 | } |
126 | |
127 | } |
128 | return svgtiny_OK; |
129 | } |
130 | |
131 | |
132 | |
133 | |
134 | |
135 | |
136 | static inline svgtiny_code |
137 | generate_path_close(struct internal_path_state *state) |
138 | { |
139 | svgtiny_code res; |
140 | |
141 | if (state->cmd.used != 0) { |
142 | |
143 | return svgtiny_SVG_ERROR; |
144 | } |
145 | |
146 | res = ensure_internal_points(&state->path, 1); |
147 | if (res != svgtiny_OK) { |
148 | return res; |
149 | } |
150 | |
151 | state->path.p[state->path.used++] = svgtiny_PATH_CLOSE; |
152 | |
153 | state->cubic_x = state->quad_x = state->prev_x = state->subpath_x; |
154 | state->cubic_y = state->quad_y = state->prev_y = state->subpath_y; |
155 | |
156 | return svgtiny_OK; |
157 | } |
158 | |
159 | |
160 | |
161 | |
162 | |
163 | |
164 | |
165 | static inline svgtiny_code |
166 | generate_path_line(struct internal_path_state *state, int relative) |
167 | { |
168 | svgtiny_code res; |
169 | unsigned int cmdpc = 0; |
170 | |
171 | if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) { |
172 | |
173 | return svgtiny_SVG_ERROR; |
174 | } |
175 | |
176 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 2) { |
177 | res = ensure_internal_points(&state->path, 3); |
178 | if (res != svgtiny_OK) { |
179 | return res; |
180 | } |
181 | |
182 | if (relative != 0) { |
183 | state->cmd.p[cmdpc] += state->prev_x; |
184 | state->cmd.p[cmdpc + 1] += state->prev_y; |
185 | } |
186 | state->path.p[state->path.used++] = svgtiny_PATH_LINE; |
187 | state->path.p[state->path.used++] = |
188 | state->cubic_x = |
189 | state->quad_x = |
190 | state->prev_x = state->cmd.p[cmdpc]; |
191 | state->path.p[state->path.used++] = |
192 | state->cubic_y = |
193 | state->quad_y = |
194 | state->prev_y = state->cmd.p[cmdpc + 1]; |
195 | } |
196 | return svgtiny_OK; |
197 | } |
198 | |
199 | |
200 | |
201 | |
202 | |
203 | |
204 | |
205 | static inline svgtiny_code |
206 | generate_path_hline(struct internal_path_state *state, int relative) |
207 | { |
208 | svgtiny_code res; |
209 | unsigned int cmdpc = 0; |
210 | |
211 | if (state->cmd.used < 1) { |
212 | |
213 | return svgtiny_SVG_ERROR; |
214 | } |
215 | |
216 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc++) { |
217 | res = ensure_internal_points(&state->path, 3); |
218 | if (res != svgtiny_OK) { |
219 | return res; |
220 | } |
221 | |
222 | if (relative != 0) { |
223 | state->cmd.p[cmdpc] += state->prev_x; |
224 | } |
225 | state->path.p[state->path.used++] = svgtiny_PATH_LINE; |
226 | state->path.p[state->path.used++] = |
227 | state->cubic_x = |
228 | state->quad_x = |
229 | state->prev_x = state->cmd.p[cmdpc]; |
230 | state->path.p[state->path.used++] = |
231 | state->cubic_y = |
232 | state->quad_x = state->prev_y; |
233 | } |
234 | return svgtiny_OK; |
235 | } |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | static inline svgtiny_code |
243 | generate_path_vline(struct internal_path_state *state, int relative) |
244 | { |
245 | svgtiny_code res; |
246 | unsigned int cmdpc = 0; |
247 | |
248 | if (state->cmd.used < 1) { |
249 | |
250 | return svgtiny_SVG_ERROR; |
251 | } |
252 | |
253 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc++) { |
254 | res = ensure_internal_points(&state->path, 3); |
255 | if (res != svgtiny_OK) { |
256 | return res; |
257 | } |
258 | |
259 | if (relative != 0) { |
260 | state->cmd.p[cmdpc] += state->prev_y; |
261 | } |
262 | state->path.p[state->path.used++] = svgtiny_PATH_LINE; |
263 | state->path.p[state->path.used++] = |
264 | state->cubic_x = |
265 | state->quad_x = state->prev_x; |
266 | state->path.p[state->path.used++] = |
267 | state->cubic_y = |
268 | state->quad_y = |
269 | state->prev_y = state->cmd.p[cmdpc]; |
270 | } |
271 | return svgtiny_OK; |
272 | } |
273 | |
274 | |
275 | |
276 | |
277 | |
278 | |
279 | static inline svgtiny_code |
280 | generate_path_curveto(struct internal_path_state *state, int relative) |
281 | { |
282 | svgtiny_code res; |
283 | unsigned int cmdpc = 0; |
284 | |
285 | if ((state->cmd.used < 6) || ((state->cmd.used % 6) != 0)) { |
286 | |
287 | return svgtiny_SVG_ERROR; |
288 | } |
289 | |
290 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 6) { |
291 | res = ensure_internal_points(&state->path, 7); |
292 | if (res != svgtiny_OK) { |
293 | return res; |
294 | } |
295 | |
296 | if (relative != 0) { |
297 | state->cmd.p[cmdpc + 0] += state->prev_x; |
298 | state->cmd.p[cmdpc + 1] += state->prev_y; |
299 | state->cmd.p[cmdpc + 2] += state->prev_x; |
300 | state->cmd.p[cmdpc + 3] += state->prev_y; |
301 | state->cmd.p[cmdpc + 4] += state->prev_x; |
302 | state->cmd.p[cmdpc + 5] += state->prev_y; |
303 | } |
304 | |
305 | state->path.p[state->path.used++] = svgtiny_PATH_BEZIER; |
306 | state->path.p[state->path.used++] = state->cmd.p[cmdpc + 0]; |
307 | state->path.p[state->path.used++] = state->cmd.p[cmdpc + 1]; |
308 | state->path.p[state->path.used++] = |
309 | state->cubic_x = state->cmd.p[cmdpc + 2]; |
310 | state->path.p[state->path.used++] = |
311 | state->cubic_y = state->cmd.p[cmdpc + 3]; |
312 | state->path.p[state->path.used++] = |
313 | state->quad_x = state->prev_x = state->cmd.p[cmdpc + 4]; |
314 | state->path.p[state->path.used++] = |
315 | state->quad_y = state->prev_y = state->cmd.p[cmdpc + 5]; |
316 | } |
317 | return svgtiny_OK; |
318 | } |
319 | |
320 | |
321 | |
322 | |
323 | |
324 | |
325 | static inline svgtiny_code |
326 | generate_path_scurveto(struct internal_path_state *state, int relative) |
327 | { |
328 | svgtiny_code res; |
329 | unsigned int cmdpc = 0; |
330 | |
331 | if ((state->cmd.used < 4) || ((state->cmd.used % 4) != 0)) { |
332 | |
333 | return svgtiny_SVG_ERROR; |
334 | } |
335 | |
336 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 4) { |
337 | float x1; |
338 | float y1; |
339 | |
340 | res = ensure_internal_points(&state->path, 7); |
341 | if (res != svgtiny_OK) { |
342 | return res; |
343 | } |
344 | |
345 | x1 = state->prev_x + (state->prev_x - state->cubic_x); |
346 | y1 = state->prev_y + (state->prev_y - state->cubic_y); |
347 | if (relative != 0) { |
348 | state->cmd.p[cmdpc + 0] += state->prev_x; |
349 | state->cmd.p[cmdpc + 1] += state->prev_y; |
350 | state->cmd.p[cmdpc + 2] += state->prev_x; |
351 | state->cmd.p[cmdpc + 3] += state->prev_y; |
352 | } |
353 | |
354 | state->path.p[state->path.used++] = svgtiny_PATH_BEZIER; |
355 | state->path.p[state->path.used++] = x1; |
356 | state->path.p[state->path.used++] = y1; |
357 | state->path.p[state->path.used++] = |
358 | state->cubic_x = state->cmd.p[cmdpc + 0]; |
359 | state->path.p[state->path.used++] = |
360 | state->cubic_x = state->cmd.p[cmdpc + 1]; |
361 | state->path.p[state->path.used++] = |
362 | state->quad_x = |
363 | state->prev_x = state->cmd.p[cmdpc + 2]; |
364 | state->path.p[state->path.used++] = |
365 | state->quad_y = |
366 | state->prev_y = state->cmd.p[cmdpc + 3]; |
367 | } |
368 | return svgtiny_OK; |
369 | } |
370 | |
371 | |
372 | |
373 | |
374 | |
375 | |
376 | static inline svgtiny_code |
377 | generate_path_bcurveto(struct internal_path_state *state, int relative) |
378 | { |
379 | svgtiny_code res; |
380 | unsigned int cmdpc = 0; |
381 | |
382 | if ((state->cmd.used < 4) || ((state->cmd.used % 4) != 0)) { |
383 | |
384 | return svgtiny_SVG_ERROR; |
385 | } |
386 | |
387 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 4) { |
388 | res = ensure_internal_points(&state->path, 7); |
389 | if (res != svgtiny_OK) { |
390 | return res; |
391 | } |
392 | |
393 | state->quad_x = state->cmd.p[cmdpc + 0] ; |
394 | state->quad_y = state->cmd.p[cmdpc + 1] ; |
395 | if (relative != 0) { |
396 | state->cmd.p[cmdpc + 0] += state->prev_x; |
397 | state->cmd.p[cmdpc + 1] += state->prev_y; |
398 | state->cmd.p[cmdpc + 2] += state->prev_x; |
399 | state->cmd.p[cmdpc + 3] += state->prev_y; |
400 | } |
401 | |
402 | state->path.p[state->path.used++] = svgtiny_PATH_BEZIER; |
403 | state->path.p[state->path.used++] = |
404 | 1./3 * state->prev_x + |
405 | 2./3 * state->cmd.p[cmdpc + 0]; |
406 | state->path.p[state->path.used++] = |
407 | 1./3 * state->prev_y + |
408 | 2./3 * state->cmd.p[cmdpc + 1]; |
409 | state->path.p[state->path.used++] = |
410 | 2./3 * state->cmd.p[cmdpc + 0] + |
411 | 1./3 * state->cmd.p[cmdpc + 2]; |
412 | state->path.p[state->path.used++] = |
413 | 2./3 * state->cmd.p[cmdpc + 1] + |
414 | 1./3 * state->cmd.p[cmdpc + 3]; |
415 | state->path.p[state->path.used++] = |
416 | state->cubic_x = |
417 | state->prev_x = state->cmd.p[cmdpc + 2]; |
418 | state->path.p[state->path.used++] = |
419 | state->cubic_y = |
420 | state->prev_y = state->cmd.p[cmdpc + 3]; |
421 | } |
422 | return svgtiny_OK; |
423 | } |
424 | |
425 | |
426 | |
427 | |
428 | |
429 | |
430 | static inline svgtiny_code |
431 | generate_path_sbcurveto(struct internal_path_state *state, int relative) |
432 | { |
433 | svgtiny_code res; |
434 | unsigned int cmdpc = 0; |
435 | |
436 | if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) { |
437 | |
438 | return svgtiny_SVG_ERROR; |
439 | } |
440 | |
441 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 2) { |
442 | float x1; |
443 | float y1; |
444 | |
445 | res = ensure_internal_points(&state->path, 7); |
446 | if (res != svgtiny_OK) { |
447 | return res; |
448 | } |
449 | |
450 | x1 = state->prev_x + (state->prev_x - state->quad_x); |
451 | y1 = state->prev_y + (state->prev_y - state->quad_y); |
452 | state->quad_x = x1; |
453 | state->quad_y = y1; |
454 | |
455 | if (relative != 0) { |
456 | x1 += state->prev_x; |
457 | y1 += state->prev_y; |
458 | state->cmd.p[cmdpc + 0] += state->prev_x; |
459 | state->cmd.p[cmdpc + 1] += state->prev_y; |
460 | } |
461 | |
462 | state->path.p[state->path.used++] = svgtiny_PATH_BEZIER; |
463 | state->path.p[state->path.used++] = |
464 | 1./3 * state->prev_x + |
465 | 2./3 * x1; |
466 | state->path.p[state->path.used++] = |
467 | 1./3 * state->prev_y + |
468 | 2./3 * y1; |
469 | state->path.p[state->path.used++] = |
470 | 2./3 * x1 + |
471 | 1./3 * state->cmd.p[cmdpc + 0]; |
472 | state->path.p[state->path.used++] = |
473 | 2./3 * y1 + |
474 | 1./3 * state->cmd.p[cmdpc + 1]; |
475 | state->path.p[state->path.used++] = |
476 | state->cubic_x = |
477 | state->prev_x = state->cmd.p[cmdpc + 0]; |
478 | state->path.p[state->path.used++] = |
479 | state->cubic_y = |
480 | state->prev_y = state->cmd.p[cmdpc + 1]; |
481 | } |
482 | return svgtiny_OK; |
483 | } |
484 | |
485 | |
486 | |
487 | |
488 | |
489 | static void |
490 | rotate_midpoint_vector(float ax, float ay, |
491 | float bx, float by, |
492 | double radangle, |
493 | double *x_out, double *y_out) |
494 | { |
495 | double dx2; |
496 | double dy2; |
497 | double cosangle; |
498 | double sinangle; |
499 | |
500 | |
501 | cosangle = cos(radangle); |
502 | sinangle = sin(radangle); |
503 | |
504 | |
505 | dx2 = (ax - bx) / 2.0; |
506 | dy2 = (ay - by) / 2.0; |
507 | |
508 | |
509 | *x_out = ((cosangle * dx2) + (sinangle * dy2)); |
510 | *y_out = ((-sinangle * dx2) + (cosangle * dy2)); |
511 | } |
512 | |
513 | |
514 | |
515 | |
516 | |
517 | |
518 | |
519 | |
520 | |
521 | static void |
522 | ensure_radii_scale(double x1_sq, double y1_sq, |
523 | float *rx, float *ry, |
524 | double *rx_sq, double *ry_sq) |
525 | { |
526 | double radiisum; |
527 | double radiiscale; |
528 | |
529 | |
530 | (*rx_sq) = (*rx) * (*rx); |
531 | (*ry_sq) = (*ry) * (*ry); |
532 | |
533 | radiisum = (x1_sq / (*rx_sq)) + (y1_sq / (*ry_sq)); |
534 | if (radiisum > 0.99999) { |
535 | |
536 | radiiscale = sqrt(radiisum) * 1.00001; |
537 | *rx = (float)(radiiscale * (*rx)); |
538 | *ry = (float)(radiiscale * (*ry)); |
539 | |
540 | (*rx_sq) = (*rx) * (*rx); |
541 | (*ry_sq) = (*ry) * (*ry); |
542 | } |
543 | } |
544 | |
545 | |
546 | |
547 | |
548 | |
549 | static void |
550 | compute_transformed_centre_point(double sign, float rx, float ry, |
551 | double rx_sq, double ry_sq, |
552 | double x1, double y1, |
553 | double x1_sq, double y1_sq, |
554 | double *cx1, double *cy1) |
555 | { |
556 | double sq; |
557 | double coef; |
558 | sq = ((rx_sq * ry_sq) - (rx_sq * y1_sq) - (ry_sq * x1_sq)) / |
559 | ((rx_sq * y1_sq) + (ry_sq * x1_sq)); |
560 | sq = (sq < 0) ? 0 : sq; |
561 | |
562 | coef = (sign * sqrt(sq)); |
563 | |
564 | *cx1 = coef * ((rx * y1) / ry); |
565 | *cy1 = coef * -((ry * x1) / rx); |
566 | } |
567 | |
568 | |
569 | |
570 | |
571 | |
572 | |
573 | |
574 | |
575 | |
576 | |
577 | static void |
578 | compute_centre_point(float ax, float ay, |
579 | float bx, float by, |
580 | double cx1, double cy1, |
581 | double radangle, |
582 | double *x_out, double *y_out) |
583 | { |
584 | double sx2; |
585 | double sy2; |
586 | double cosangle; |
587 | double sinangle; |
588 | |
589 | |
590 | cosangle = cos(radangle); |
591 | sinangle = sin(radangle); |
592 | |
593 | sx2 = (ax + bx) / 2.0; |
594 | sy2 = (ay + by) / 2.0; |
595 | |
596 | *x_out = sx2 + (cosangle * cx1 - sinangle * cy1); |
597 | *y_out = sy2 + (sinangle * cx1 + cosangle * cy1); |
598 | } |
599 | |
600 | |
601 | |
602 | |
603 | |
604 | static void |
605 | compute_angle_start_extent(float rx, float ry, |
606 | double x1, double y1, |
607 | double cx1, double cy1, |
608 | double *start, double *extent) |
609 | { |
610 | double sign; |
611 | double ux; |
612 | double uy; |
613 | double vx; |
614 | double vy; |
615 | double p, n; |
616 | double actmp; |
617 | |
618 | |
619 | |
620 | |
621 | |
622 | |
623 | |
624 | |
625 | ux = (x1 - cx1) / rx; |
626 | uy = (y1 - cy1) / ry; |
627 | vx = (-x1 - cx1) / rx; |
628 | vy = (-y1 - cy1) / ry; |
629 | |
630 | |
631 | |
632 | |
633 | |
634 | n = sqrt((ux * ux) + (uy * uy)); |
635 | |
636 | p = ux; |
637 | |
638 | sign = (uy < 0) ? -1.0 : 1.0; |
639 | |
640 | *start = sign * acos(p / n); |
641 | |
642 | |
643 | n = sqrt(((ux * ux) + (uy * uy)) * ((vx * vx) + (vy * vy))); |
644 | p = (ux * vx) + (uy * vy); |
645 | sign = ((ux * vy) - (uy * vx) < 0) ? -1.0f : 1.0f; |
646 | |
647 | |
648 | actmp = p / n; |
649 | if (actmp < -1.0) { |
650 | *extent = sign * M_PI; |
651 | } else if (actmp > 1.0) { |
652 | *extent = 0; |
653 | } else { |
654 | *extent = sign * acos(actmp); |
655 | } |
656 | } |
657 | |
658 | |
659 | |
660 | |
661 | |
662 | |
663 | |
664 | |
665 | |
666 | |
667 | |
668 | |
669 | |
670 | |
671 | static int |
672 | circle_arc_to_bezier(double start, double extent, double *bzpt) |
673 | { |
674 | int bzsegments; |
675 | double increment; |
676 | double controllen; |
677 | int pos = 0; |
678 | int segment; |
679 | double angle; |
680 | double dx, dy; |
681 | |
682 | bzsegments = (int) ceil(fabs(extent) / M_PI_2); |
683 | increment = extent / bzsegments; |
684 | controllen = 4.0 / 3.0 * sin(increment / 2.0) / (1.0 + cos(increment / 2.0)); |
685 | |
686 | for (segment = 0; segment < bzsegments; segment++) { |
| 19 | | Assuming 'segment' is >= 'bzsegments' | |
|
| 20 | | Loop condition is false. Execution continues on line 704 | |
|
687 | |
688 | angle = start + (segment * increment); |
689 | dx = cos(angle); |
690 | dy = sin(angle); |
691 | bzpt[pos++] = dx - controllen * dy; |
692 | bzpt[pos++] = dy + controllen * dx; |
693 | |
694 | angle+=increment; |
695 | dx = cos(angle); |
696 | dy = sin(angle); |
697 | bzpt[pos++] = dx + controllen * dy; |
698 | bzpt[pos++] = dy - controllen * dx; |
699 | |
700 | bzpt[pos++] = dx; |
701 | bzpt[pos++] = dy; |
702 | |
703 | } |
704 | return bzsegments; |
| 21 | | Returning without writing to '*bzpt' | |
|
705 | } |
706 | |
707 | |
708 | |
709 | |
710 | |
711 | |
712 | |
713 | |
714 | |
715 | |
716 | |
717 | |
718 | |
719 | |
720 | |
721 | |
722 | |
723 | |
724 | |
725 | |
726 | |
727 | |
728 | |
729 | |
730 | |
731 | |
732 | |
733 | |
734 | |
735 | |
736 | |
737 | |
738 | |
739 | |
740 | |
741 | |
742 | |
743 | |
744 | |
745 | |
746 | |
747 | |
748 | |
749 | |
750 | |
751 | |
752 | |
753 | |
754 | |
755 | |
756 | |
757 | |
758 | |
759 | |
760 | |
761 | |
762 | |
763 | |
764 | |
765 | |
766 | |
767 | |
768 | |
769 | |
770 | |
771 | static void |
772 | scale_rotate_translate_points(double rx, double ry, |
773 | double radangle, |
774 | double cx, double cy, |
775 | int pntsize, |
776 | double *points) |
777 | { |
778 | int pnt; |
779 | double cosangle; |
780 | double sinangle; |
781 | double rxcosangle, rxsinangle, rycosangle, rynsinangle; |
782 | double x2,y2; |
783 | |
784 | |
785 | cosangle = cos(radangle); |
786 | sinangle = sin(radangle); |
787 | |
788 | rxcosangle = rx * cosangle; |
789 | rxsinangle = rx * sinangle; |
790 | rycosangle = ry * cosangle; |
791 | rynsinangle = ry * -1 * sinangle; |
792 | |
793 | for (pnt = 0; pnt < pntsize; pnt+=2) { |
| 24 | | The value 0 is assigned to 'pnt' | |
|
| 25 | | Assuming 'pnt' is < 'pntsize' | |
|
| 26 | | Loop condition is true. Entering loop body | |
|
794 | x2 = cx + (points[pnt] * rxcosangle) + (points[pnt + 1] * rynsinangle); |
| 27 | | The left operand of '*' is a garbage value |
|
795 | y2 = cy + (points[pnt + 1] * rycosangle) + (points[pnt] * rxsinangle); |
796 | points[pnt] = x2; |
797 | points[pnt + 1] = y2; |
798 | } |
799 | } |
800 | |
801 | |
802 | |
803 | |
804 | |
805 | |
806 | |
807 | |
808 | |
809 | |
810 | |
811 | |
812 | |
813 | |
814 | |
815 | |
816 | |
817 | |
818 | static int |
819 | svgarc_to_bezier(float start_x, |
820 | float start_y, |
821 | float end_x, |
822 | float end_y, |
823 | float rx, |
824 | float ry, |
825 | float angle, |
826 | bool largearc, |
827 | bool sweep, |
828 | double *bzpoints) |
829 | { |
830 | double radangle; |
831 | double rx_sq; |
832 | double ry_sq; |
833 | double x1, y1; |
834 | double x1_sq, y1_sq; |
835 | double cx1,cy1; |
836 | double cx,cy; |
837 | double start, extent; |
838 | int bzsegments; |
839 | |
840 | if ((start_x == end_x) && (start_y == end_y)) { |
| 8 | | Assuming 'start_x' is not equal to 'end_x' | |
|
841 | |
842 | |
843 | |
844 | |
845 | |
846 | return 0; |
847 | } |
848 | |
849 | if ((rx == 0) || (ry == 0)) { |
| 9 | | Assuming 'rx' is not equal to 0 | |
|
| 10 | | Assuming 'ry' is not equal to 0 | |
|
| |
850 | |
851 | |
852 | |
853 | return -1; |
854 | } |
855 | |
856 | |
857 | rx = fabsf(rx); |
858 | ry = fabsf(ry); |
859 | |
860 | |
861 | radangle = degToRad(fmod(angle, 360.0)); |
862 | |
863 | |
864 | |
865 | rotate_midpoint_vector(start_x, start_y, end_x, end_y, radangle, &x1, &y1); |
866 | |
867 | |
868 | |
869 | x1_sq = x1 * x1; |
870 | y1_sq = y1 * y1; |
871 | |
872 | |
873 | ensure_radii_scale(x1_sq, y1_sq, &rx, &ry, &rx_sq, &ry_sq); |
874 | |
875 | |
876 | compute_transformed_centre_point(largearc == sweep?-1:1, |
| 12 | | Assuming 'largearc' is not equal to 'sweep' | |
|
| |
877 | rx, ry, |
878 | rx_sq, ry_sq, |
879 | x1, y1, |
880 | x1_sq, y1_sq, |
881 | &cx1, &cy1); |
882 | |
883 | |
884 | |
885 | compute_centre_point(start_x, start_y, |
886 | end_x, end_y, |
887 | cx1, cy1, |
888 | radangle, |
889 | &cx, &cy); |
890 | |
891 | |
892 | |
893 | compute_angle_start_extent(rx,ry, |
894 | x1,y1, |
895 | cx1, cy1, |
896 | &start, &extent); |
897 | |
898 | |
899 | if (extent == 0) { |
| 14 | | Assuming 'extent' is not equal to 0 | |
|
900 | return -1; |
901 | } |
902 | |
903 | |
904 | if (!sweep && extent > 0) { |
| 15 | | Assuming 'sweep' is true | |
|
905 | extent -= TAU; |
906 | } else if (sweep && extent < 0) { |
| 16 | | Assuming 'extent' is >= 0 | |
|
| |
907 | extent += TAU; |
908 | } |
909 | |
910 | |
911 | extent = fmod(extent, TAU); |
912 | start = fmod(start, TAU); |
913 | |
914 | |
915 | bzsegments = circle_arc_to_bezier(start, extent, bzpoints); |
| 18 | | Calling 'circle_arc_to_bezier' | |
|
| 22 | | Returning from 'circle_arc_to_bezier' | |
|
916 | |
917 | |
918 | scale_rotate_translate_points(rx, ry, |
| 23 | | Calling 'scale_rotate_translate_points' | |
|
919 | radangle, |
920 | cx, cy, |
921 | bzsegments * 6, |
922 | bzpoints); |
923 | |
924 | return bzsegments; |
925 | } |
926 | |
927 | |
928 | |
929 | |
930 | |
931 | |
932 | static svgtiny_code |
933 | generate_path_ellipticalarc(struct internal_path_state *state, int relative) |
934 | { |
935 | svgtiny_code res; |
936 | unsigned int cmdpc = 0; |
937 | |
938 | if ((state->cmd.used < 7) || ((state->cmd.used % 7) != 0)) { |
| 1 | Assuming field 'used' is >= 7 | |
|
| 2 | | Assuming the condition is false | |
|
| |
939 | |
940 | return svgtiny_SVG_ERROR; |
941 | } |
942 | |
943 | for (cmdpc = 0; cmdpc < state->cmd.used; cmdpc += 7) { |
| 4 | | Loop condition is true. Entering loop body | |
|
944 | int bzsegments; |
945 | double bzpoints[6*4]; |
946 | if (relative != 0) { |
| 5 | | Assuming 'relative' is equal to 0 | |
|
| |
947 | state->cmd.p[cmdpc + 5] += state->prev_x; |
948 | state->cmd.p[cmdpc + 6] += state->prev_y; |
949 | } |
950 | |
951 | bzsegments = svgarc_to_bezier(state->prev_x, state->prev_y, |
| 7 | | Calling 'svgarc_to_bezier' | |
|
952 | state->cmd.p[cmdpc + 5], |
953 | state->cmd.p[cmdpc + 6], |
954 | state->cmd.p[cmdpc + 0], |
955 | state->cmd.p[cmdpc + 1], |
956 | state->cmd.p[cmdpc + 2], |
957 | state->cmd.p[cmdpc + 3], |
958 | state->cmd.p[cmdpc + 4], |
959 | bzpoints); |
960 | |
961 | if (bzsegments == -1) { |
962 | |
963 | res = ensure_internal_points(&state->path, 3); |
964 | if (res != svgtiny_OK) { |
965 | return res; |
966 | } |
967 | state->path.p[state->path.used++] = svgtiny_PATH_LINE; |
968 | state->path.p[state->path.used++] = |
969 | state->cubic_x = |
970 | state->quad_x = |
971 | state->prev_x = state->cmd.p[cmdpc + 5] ; |
972 | state->path.p[state->path.used++] = |
973 | state->cubic_y = |
974 | state->quad_y = |
975 | state->prev_y = state->cmd.p[cmdpc + 6] ; |
976 | } else if (bzsegments > 0){ |
977 | int bzpnt; |
978 | for (bzpnt = 0;bzpnt < (bzsegments * 6); bzpnt+=6) { |
979 | res = ensure_internal_points(&state->path, 7); |
980 | if (res != svgtiny_OK) { |
981 | return res; |
982 | } |
983 | state->path.p[state->path.used++] = svgtiny_PATH_BEZIER; |
984 | state->path.p[state->path.used++] = bzpoints[bzpnt]; |
985 | state->path.p[state->path.used++] = bzpoints[bzpnt+1]; |
986 | state->path.p[state->path.used++] = bzpoints[bzpnt+2]; |
987 | state->path.p[state->path.used++] = bzpoints[bzpnt+3]; |
988 | state->path.p[state->path.used++] = |
989 | state->cubic_y = |
990 | state->quad_x = |
991 | state->prev_x = bzpoints[bzpnt+4]; |
992 | state->path.p[state->path.used++] = |
993 | state->cubic_y = |
994 | state->quad_y = |
995 | state->prev_y = bzpoints[bzpnt+5]; |
996 | } |
997 | } |
998 | } |
999 | return svgtiny_OK; |
1000 | } |
1001 | |
1002 | |
1003 | |
1004 | |
1005 | |
1006 | static inline svgtiny_code |
1007 | parse_path_parameters(const char **cursor, |
1008 | const char *textend, |
1009 | struct internal_points *cmdp) |
1010 | { |
1011 | const char *numend; |
1012 | svgtiny_code res; |
1013 | |
1014 | cmdp->used = 0; |
1015 | |
1016 | do { |
1017 | res = ensure_internal_points(cmdp, 1); |
1018 | if (res != svgtiny_OK) { |
1019 | return res; |
1020 | } |
1021 | |
1022 | numend = textend; |
1023 | res = svgtiny_parse_number(*cursor, |
1024 | &numend, |
1025 | cmdp->p + cmdp->used); |
1026 | if (res != svgtiny_OK) { |
1027 | break; |
1028 | } |
1029 | cmdp->used++; |
1030 | *cursor = numend; |
1031 | |
1032 | advance_comma_whitespace(cursor, textend); |
1033 | |
1034 | } while (res == svgtiny_OK); |
1035 | |
1036 | return svgtiny_OK; |
1037 | } |
1038 | |
1039 | |
1040 | |
1041 | |
1042 | |
1043 | static inline svgtiny_code |
1044 | parse_path_command(const char **cursor, |
1045 | const char *textend, |
1046 | char *pathcommand, |
1047 | int *relative) |
1048 | { |
1049 | advance_whitespace(cursor, textend); |
1050 | |
1051 | if ((**cursor != 'M') && (**cursor != 'm') && |
1052 | (**cursor != 'Z') && (**cursor != 'z') && |
1053 | (**cursor != 'L') && (**cursor != 'l') && |
1054 | (**cursor != 'H') && (**cursor != 'h') && |
1055 | (**cursor != 'V') && (**cursor != 'v') && |
1056 | (**cursor != 'C') && (**cursor != 'c') && |
1057 | (**cursor != 'S') && (**cursor != 's') && |
1058 | (**cursor != 'Q') && (**cursor != 'q') && |
1059 | (**cursor != 'T') && (**cursor != 't') && |
1060 | (**cursor != 'A') && (**cursor != 'a')) { |
1061 | return svgtiny_SVG_ERROR; |
1062 | } |
1063 | |
1064 | if ((**cursor >= 0x61 ) && (**cursor <= 0x7A )) { |
1065 | *pathcommand = (**cursor) & ~(1 << 5); |
1066 | *relative = 1; |
1067 | } else { |
1068 | *pathcommand = **cursor; |
1069 | *relative = 0; |
1070 | } |
1071 | (*cursor)++; |
1072 | |
1073 | advance_whitespace(cursor, textend); |
1074 | |
1075 | return svgtiny_OK; |
1076 | } |
1077 | |
1078 | |
1079 | |
1080 | |
1081 | |
1082 | svgtiny_code |
1083 | svgtiny_parse_path_data(const char *text, |
1084 | size_t textlen, |
1085 | float **pointv, |
1086 | unsigned int *pointc) |
1087 | { |
1088 | const char *cursor = text; |
1089 | const char *textend = text + textlen; |
1090 | svgtiny_code res; |
1091 | char pathcmd = 0; |
1092 | int relative = 0; |
1093 | struct internal_path_state pathstate = { |
1094 | {NULL, 0, 0}, |
1095 | {NULL, 0, 0}, |
1096 | 0.0, 0.0, |
1097 | 0.0, 0.0, |
1098 | 0.0, 0.0, |
1099 | 0.0, 0.0 |
1100 | }; |
1101 | |
1102 | advance_whitespace(&cursor, textend); |
1103 | |
1104 | |
1105 | if (cursor == textend) { |
1106 | *pointc = 0; |
1107 | return svgtiny_OK; |
1108 | } |
1109 | |
1110 | |
1111 | res = svgtiny_parse_none(cursor, textend); |
1112 | if (res == svgtiny_OK) { |
1113 | *pointc = 0; |
1114 | return res; |
1115 | } |
1116 | |
1117 | while (cursor < textend) { |
1118 | res = parse_path_command(&cursor, textend, &pathcmd, &relative); |
1119 | if (res != svgtiny_OK) { |
1120 | goto parse_path_data_error; |
1121 | } |
1122 | |
1123 | res = parse_path_parameters(&cursor, textend, &pathstate.cmd); |
1124 | if (res != svgtiny_OK) { |
1125 | goto parse_path_data_error; |
1126 | } |
1127 | |
1128 | switch (pathcmd) { |
1129 | case 'M': |
1130 | res = generate_path_move(&pathstate, relative); |
1131 | break; |
1132 | case 'Z': |
1133 | res = generate_path_close(&pathstate); |
1134 | break; |
1135 | case 'L': |
1136 | res = generate_path_line(&pathstate, relative); |
1137 | break; |
1138 | case 'H': |
1139 | res = generate_path_hline(&pathstate, relative); |
1140 | break; |
1141 | case 'V': |
1142 | res = generate_path_vline(&pathstate, relative); |
1143 | break; |
1144 | case 'C': |
1145 | res = generate_path_curveto(&pathstate, relative); |
1146 | break; |
1147 | case 'S': |
1148 | res = generate_path_scurveto(&pathstate, relative); |
1149 | break; |
1150 | case 'Q': |
1151 | res = generate_path_bcurveto(&pathstate, relative); |
1152 | break; |
1153 | case 'T': |
1154 | res = generate_path_sbcurveto(&pathstate, relative); |
1155 | break; |
1156 | case 'A': |
1157 | res = generate_path_ellipticalarc(&pathstate, relative); |
1158 | break; |
1159 | } |
1160 | |
1161 | if (res != svgtiny_OK) { |
1162 | goto parse_path_data_error; |
1163 | } |
1164 | |
1165 | } |
1166 | *pointv = pathstate.path.p; |
1167 | *pointc = pathstate.path.used; |
1168 | |
1169 | if (pathstate.cmd.alloc > 0) { |
1170 | free(pathstate.cmd.p); |
1171 | } |
1172 | return svgtiny_OK; |
1173 | |
1174 | parse_path_data_error: |
1175 | |
1176 | if (pathstate.path.alloc > 0) { |
1177 | free(pathstate.path.p); |
1178 | } |
1179 | if (pathstate.cmd.alloc > 0) { |
1180 | free(pathstate.cmd.p); |
1181 | } |
1182 | return res; |
1183 | } |