Bug Summary

File:svgtiny_path.c
Warning:line 794, column 26
The left operand of '*' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

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-10-24-085850-690962-1 -x c src/svgtiny_path.c
1/*
2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2024 Vincent Sanders <vince@netsurf-browser.org>
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 TAU6.28318530717958647692 6.28318530717958647692
17
18#ifndef M_PI3.14159265358979323846
19#define M_PI3.14159265358979323846 3.14159265358979323846
20#endif
21
22#ifndef M_PI_21.57079632679489661923
23#define M_PI_21.57079632679489661923 1.57079632679489661923
24#endif
25
26#define degToRad(angleInDegrees)((angleInDegrees) * 3.14159265358979323846 / 180.0) ((angleInDegrees) * M_PI3.14159265358979323846 / 180.0)
27#define radToDeg(angleInRadians)((angleInRadians) * 180.0 / 3.14159265358979323846) ((angleInRadians) * 180.0 / M_PI3.14159265358979323846)
28
29
30struct internal_points {
31 float *p; /* points */
32 unsigned int used; /* number of used points */
33 size_t alloc; /* number of allocated points */
34};
35
36/**
37 * internal path representation
38 */
39struct internal_path_state {
40 struct internal_points path;/* generated path */
41 struct internal_points cmd; /* parameters to current command */
42 float prev_x; /* previous x coordinate */
43 float prev_y; /* previous y coordinate */
44 float subpath_x; /* subpath start x coordinate */
45 float subpath_y; /* subpath start y coordinate */
46 float cubic_x; /* previous cubic x control point */
47 float cubic_y; /* previous cubic y control point */
48 float quad_x; /* previous quadratic x control point */
49 float quad_y; /* previous quadratic y control point */
50};
51
52/**
53 * ensure there is enough storage allocated to make points available
54 *
55 * \param ipts internal points to enaure space in
56 * \param count The number of points of space to ensure.
57 * \return svgtiny_OK on success else svgtiny_OUT_OF_MEMORY if allocation fails
58 */
59static inline svgtiny_code
60ensure_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((void*)0)) {
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 * moveto
78 *
79 * command and parameters: M|m (x, y)+
80 */
81static inline svgtiny_code
82generate_path_move(struct internal_path_state *state, int relative)
83{
84 svgtiny_code res;
85 unsigned int cmdpc = 0; /* command parameter count */
86
87 if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) {
88 /* wrong number of parameters */
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 /* the move starts a subpath */
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 * closepath
133 *
134 * command and parameters: Z|z
135 */
136static inline svgtiny_code
137generate_path_close(struct internal_path_state *state)
138{
139 svgtiny_code res;
140
141 if (state->cmd.used != 0) {
142 /* wrong number of parameters */
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 * lineto
162 *
163 * command and parameters: L|l (x, y)+
164 */
165static inline svgtiny_code
166generate_path_line(struct internal_path_state *state, int relative)
167{
168 svgtiny_code res;
169 unsigned int cmdpc = 0; /* command parameter count */
170
171 if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) {
172 /* wrong number of parameters */
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 * horizontal lineto
202 *
203 * command and parameters: H|h x+
204 */
205static inline svgtiny_code
206generate_path_hline(struct internal_path_state *state, int relative)
207{
208 svgtiny_code res;
209 unsigned int cmdpc = 0; /* command parameter count */
210
211 if (state->cmd.used < 1) {
212 /* wrong number of parameters */
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 * vertical lineto
239 *
240 * command and parameters: V|v y+
241 */
242static inline svgtiny_code
243generate_path_vline(struct internal_path_state *state, int relative)
244{
245 svgtiny_code res;
246 unsigned int cmdpc = 0; /* command parameter count */
247
248 if (state->cmd.used < 1) {
249 /* wrong number of parameters */
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 * curveto
276 *
277 * command and parameters: C|c (x1, y1, x2, y2, x, y)+
278 */
279static inline svgtiny_code
280generate_path_curveto(struct internal_path_state *state, int relative)
281{
282 svgtiny_code res;
283 unsigned int cmdpc = 0; /* command parameter count */
284
285 if ((state->cmd.used < 6) || ((state->cmd.used % 6) != 0)) {
286 /* wrong number of parameters */
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; /* x1 */
298 state->cmd.p[cmdpc + 1] += state->prev_y; /* y1 */
299 state->cmd.p[cmdpc + 2] += state->prev_x; /* x2 */
300 state->cmd.p[cmdpc + 3] += state->prev_y; /* y2 */
301 state->cmd.p[cmdpc + 4] += state->prev_x; /* x */
302 state->cmd.p[cmdpc + 5] += state->prev_y; /* 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 * smooth curveto
322 *
323 * command and parameters: S|s (x2, y2, x, y)+
324 */
325static inline svgtiny_code
326generate_path_scurveto(struct internal_path_state *state, int relative)
327{
328 svgtiny_code res;
329 unsigned int cmdpc = 0; /* command parameter count */
330
331 if ((state->cmd.used < 4) || ((state->cmd.used % 4) != 0)) {
332 /* wrong number of parameters */
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; /* x2 */
349 state->cmd.p[cmdpc + 1] += state->prev_y; /* y2 */
350 state->cmd.p[cmdpc + 2] += state->prev_x; /* x */
351 state->cmd.p[cmdpc + 3] += state->prev_y; /* 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 * quadratic Bezier curveto
373 *
374 * command and parameters: Q|q (x1, y1, x ,y)+
375 */
376static inline svgtiny_code
377generate_path_bcurveto(struct internal_path_state *state, int relative)
378{
379 svgtiny_code res;
380 unsigned int cmdpc = 0; /* command parameter count */
381
382 if ((state->cmd.used < 4) || ((state->cmd.used % 4) != 0)) {
383 /* wrong number of parameters */
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] /* x1 */;
394 state->quad_y = state->cmd.p[cmdpc + 1] /* y1 */;
395 if (relative != 0) {
396 state->cmd.p[cmdpc + 0] += state->prev_x; /* x1 */
397 state->cmd.p[cmdpc + 1] += state->prev_y; /* y1 */
398 state->cmd.p[cmdpc + 2] += state->prev_x; /* x */
399 state->cmd.p[cmdpc + 3] += state->prev_y; /* 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 * smooth quadratic Bezier curveto
427 *
428 * command and parameters: T|t (x ,y)+
429 */
430static inline svgtiny_code
431generate_path_sbcurveto(struct internal_path_state *state, int relative)
432{
433 svgtiny_code res;
434 unsigned int cmdpc = 0; /* command parameter count */
435
436 if ((state->cmd.used < 2) || ((state->cmd.used % 2) != 0)) {
437 /* wrong number of parameters */
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; /* x */
459 state->cmd.p[cmdpc + 1] += state->prev_y; /* 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 * rotate midpoint vector
488 */
489static void
490rotate_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; /* midpoint x coordinate */
496 double dy2; /* midpoint y coordinate */
497 double cosangle; /* cosine of rotation angle */
498 double sinangle; /* sine of rotation angle */
499
500 /* compute the sin and cos of the angle */
501 cosangle = cos(radangle);
502 sinangle = sin(radangle);
503
504 /* compute the midpoint between start and end points */
505 dx2 = (ax - bx) / 2.0;
506 dy2 = (ay - by) / 2.0;
507
508 /* rotate vector to remove angle */
509 *x_out = ((cosangle * dx2) + (sinangle * dy2));
510 *y_out = ((-sinangle * dx2) + (cosangle * dy2));
511}
512
513
514/**
515 * ensure the arc radii are large enough and scale as appropriate
516 *
517 * the radii need to be large enough if they are not they must be
518 * adjusted. This allows for elimination of differences between
519 * implementations especialy with rounding.
520 */
521static void
522ensure_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 /* set radii square values */
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 /* need to scale radii */
536 radiiscale = sqrt(radiisum) * 1.00001;
537 *rx = (float)(radiiscale * (*rx));
538 *ry = (float)(radiiscale * (*ry));
539 /* update squares too */
540 (*rx_sq) = (*rx) * (*rx);
541 (*ry_sq) = (*ry) * (*ry);
542 }
543}
544
545
546/**
547 * compute the transformed centre point
548 */
549static void
550compute_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 * compute untransformed centre point
571 *
572 * \param ax The first point x coordinate
573 * \param ay The first point y coordinate
574 * \param bx The second point x coordinate
575 * \param ay The second point y coordinate
576 */
577static void
578compute_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; /* cosine of rotation angle */
587 double sinangle; /* sine of rotation angle */
588
589 /* compute the sin and cos of the angle */
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 * compute the angle start and extent
603 */
604static void
605compute_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 * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v))
620 * Where:
621 * '.' is the dot product.
622 * +/- is calculated from the sign of the cross product (u x v)
623 */
624
625 ux = (x1 - cx1) / rx;
626 uy = (y1 - cy1) / ry;
627 vx = (-x1 - cx1) / rx;
628 vy = (-y1 - cy1) / ry;
629
630 /* compute the start angle */
631 /* The angle between (ux, uy) and the 0 angle */
632
633 /* len(u) * len(1,0) == len(u) */
634 n = sqrt((ux * ux) + (uy * uy));
635 /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */
636 p = ux;
637 /* u x v == (1 * uy - ux * 0) == uy */
638 sign = (uy < 0) ? -1.0 : 1.0;
639 /* (p >= n) so safe */
640 *start = sign * acos(p / n);
641
642 /* compute the extent angle */
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 /* arc cos must operate between -1 and 1 */
648 actmp = p / n;
649 if (actmp < -1.0) {
650 *extent = sign * M_PI3.14159265358979323846;
651 } else if (actmp > 1.0) {
652 *extent = 0;
653 } else {
654 *extent = sign * acos(actmp);
655 }
656}
657
658/**
659 * converts a circle centered unit circle arc to a series of bezier curves
660 *
661 * Each bezier is stored as six values of three pairs of coordinates
662 *
663 * The beziers are stored without their start point as that is assumed
664 * to be the preceding elements end point.
665 *
666 * \param start The start angle of the arc (in radians)
667 * \param extent The size of the arc (in radians)
668 * \param bzpt The array to store the bezier values in
669 * \return The number of bezier segments output (max 4)
670 */
671static int
672circle_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_21.57079632679489661923);
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 /* first control point */
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 /* second control point */
694 angle+=increment;
695 dx = cos(angle);
696 dy = sin(angle);
697 bzpt[pos++] = dx + controllen * dy;
698 bzpt[pos++] = dy - controllen * dx;
699 /* endpoint */
700 bzpt[pos++] = dx;
701 bzpt[pos++] = dy;
702
703 }
704 return bzsegments;
21
Returning without writing to '*bzpt'
705}
706
707
708/**
709 * transform coordinate list
710 *
711 * perform a scale, rotate and translate on list of coordinates
712 *
713 * scale(rx,ry)
714 * rotate(an)
715 * translate (cx, cy)
716 *
717 * homogeneous transforms
718 *
719 * scaling
720 * | rx 0 0 |
721 * S = | 0 ry 0 |
722 * | 0 0 1 |
723 *
724 * rotate
725 * | cos(an) -sin(an) 0 |
726 * R = | sin(an) cos(an) 0 |
727 * | 0 0 1 |
728 *
729 * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}}
730 *
731 * translate
732 * | 1 0 cx |
733 * T = | 0 1 cy |
734 * | 0 0 1 |
735 *
736 * note order is significat here and the combined matrix is
737 * M = T.R.S
738 *
739 * | cos(an) -sin(an) cx |
740 * T.R = | sin(an) cos(an) cy |
741 * | 0 0 1 |
742 *
743 * | rx * cos(an) ry * -sin(an) cx |
744 * T.R.S = | rx * sin(an) ry * cos(an) cy |
745 * | 0 0 1 |
746 *
747 * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}}
748 *
749 * Each point
750 * | x1 |
751 * P = | y1 |
752 * | 1 |
753 *
754 * output
755 * | x2 |
756 * | y2 | = M . P
757 * | 1 |
758 *
759 * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a))
760 * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a))
761 *
762 *
763 * \param rx X scaling to apply
764 * \param ry Y scaling to apply
765 * \param radangle rotation to apply (in radians)
766 * \param cx X translation to apply
767 * \param cy Y translation to apply
768 * \param points The size of the bzpoints array
769 * \param bzpoints an array of x,y values to apply the transform to
770 */
771static void
772scale_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; /* cosine of rotation angle */
780 double sinangle; /* sine of rotation angle */
781 double rxcosangle, rxsinangle, rycosangle, rynsinangle;
782 double x2,y2;
783
784 /* compute the sin and cos of the angle */
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 * convert an svg path arc to a bezier curve
804 *
805 * This function perfoms a transform on the nine arc parameters
806 * (coordinate pairs for start and end together with the radii of the
807 * elipse, the rotation angle and which of the four arcs to draw)
808 * which generates the parameters (coordinate pairs for start,
809 * end and their control points) for a set of up to four bezier curves.
810 *
811 * Obviously the start and end coordinates are not altered between
812 * representations so the aim is to calculate the coordinate pairs for
813 * the bezier control points.
814 *
815 * \param bzpoints the array to fill with bezier curves
816 * \return the number of bezier segments generated or -1 for a line
817 */
818static int
819svgarc_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_Bool largearc,
827 bool_Bool sweep,
828 double *bzpoints)
829{
830 double radangle; /* normalised elipsis rotation angle in radians */
831 double rx_sq; /* x radius squared */
832 double ry_sq; /* y radius squared */
833 double x1, y1; /* rotated midpoint vector */
834 double x1_sq, y1_sq; /* x1 vector squared */
835 double cx1,cy1; /* transformed circle center */
836 double cx,cy; /* circle center */
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 * if the start and end coordinates are the same the
843 * svg spec says this is equivalent to having no segment
844 * at all
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
11
Taking false branch
850 /*
851 * if either radii is zero the specified behaviour is a line
852 */
853 return -1;
854 }
855
856 /* obtain the absolute values of the radii */
857 rx = fabsf(rx);
858 ry = fabsf(ry);
859
860 /* convert normalised angle to radians */
861 radangle = degToRad(fmod(angle, 360.0))((fmod(angle, 360.0)) * 3.14159265358979323846 / 180.0);
862
863 /* step 1 */
864 /* x1,x2 is the midpoint vector rotated to remove the arc angle */
865 rotate_midpoint_vector(start_x, start_y, end_x, end_y, radangle, &x1, &y1);
866
867 /* step 2 */
868 /* get squared x1 values */
869 x1_sq = x1 * x1;
870 y1_sq = y1 * y1;
871
872 /* ensure radii are correctly scaled */
873 ensure_radii_scale(x1_sq, y1_sq, &rx, &ry, &rx_sq, &ry_sq);
874
875 /* compute the transformed centre point */
876 compute_transformed_centre_point(largearc == sweep?-1:1,
12
Assuming 'largearc' is not equal to 'sweep'
13
'?' condition is false
877 rx, ry,
878 rx_sq, ry_sq,
879 x1, y1,
880 x1_sq, y1_sq,
881 &cx1, &cy1);
882
883 /* step 3 */
884 /* get the untransformed centre point */
885 compute_centre_point(start_x, start_y,
886 end_x, end_y,
887 cx1, cy1,
888 radangle,
889 &cx, &cy);
890
891 /* step 4 */
892 /* compute anglestart and extent */
893 compute_angle_start_extent(rx,ry,
894 x1,y1,
895 cx1, cy1,
896 &start, &extent);
897
898 /* extent of 0 is a straight line */
899 if (extent == 0) {
14
Assuming 'extent' is not equal to 0
900 return -1;
901 }
902
903 /* take account of sweep */
904 if (!sweep && extent > 0) {
15
Assuming 'sweep' is true
905 extent -= TAU6.28318530717958647692;
906 } else if (sweep
15.1
'sweep' is true
&& extent < 0) {
16
Assuming 'extent' is >= 0
17
Taking false branch
907 extent += TAU6.28318530717958647692;
908 }
909
910 /* normalise start and extent */
911 extent = fmod(extent, TAU6.28318530717958647692);
912 start = fmod(start, TAU6.28318530717958647692);
913
914 /* convert the arc to unit circle bezier curves */
915 bzsegments = circle_arc_to_bezier(start, extent, bzpoints);
18
Calling 'circle_arc_to_bezier'
22
Returning from 'circle_arc_to_bezier'
916
917 /* transform the bezier curves */
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 * elliptical arc
929 *
930 * command and parameters: A|a (rx, ry, rotation, largearc, sweep, x, y)+
931 */
932static svgtiny_code
933generate_path_ellipticalarc(struct internal_path_state *state, int relative)
934{
935 svgtiny_code res;
936 unsigned int cmdpc = 0; /* command parameter count */
937
938 if ((state->cmd.used < 7) || ((state->cmd.used % 7) != 0)) {
1
Assuming field 'used' is >= 7
2
Assuming the condition is false
3
Taking false branch
939 /* wrong number of parameters */
940 return svgtiny_SVG_ERROR;
941 }
942
943 for (cmdpc = 0; cmdpc
3.1
'cmdpc' is < field 'used'
< state->cmd.used; cmdpc += 7) {
4
Loop condition is true. Entering loop body
944 int bzsegments;
945 double bzpoints[6*4]; /* allow for up to four bezier segments per arc */
946 if (relative != 0) {
5
Assuming 'relative' is equal to 0
6
Taking false branch
947 state->cmd.p[cmdpc + 5] += state->prev_x; /* x */
948 state->cmd.p[cmdpc + 6] += state->prev_y; /* 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], /* x */
953 state->cmd.p[cmdpc + 6], /* y */
954 state->cmd.p[cmdpc + 0], /* rx */
955 state->cmd.p[cmdpc + 1], /* ry */
956 state->cmd.p[cmdpc + 2], /* rotation */
957 state->cmd.p[cmdpc + 3], /* large_arc */
958 state->cmd.p[cmdpc + 4], /* sweep */
959 bzpoints);
960
961 if (bzsegments == -1) {
962 /* failed to convert arc to bezier replace with line */
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] /* x */;
972 state->path.p[state->path.used++] =
973 state->cubic_y =
974 state->quad_y =
975 state->prev_y = state->cmd.p[cmdpc + 6] /* y */;
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 * parse parameters to a path command
1005 */
1006static inline svgtiny_code
1007parse_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 * parse a path command
1042 */
1043static inline svgtiny_code
1044parse_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 /* a */) && (**cursor <= 0x7A /* z */)) {
1065 *pathcommand = (**cursor) & ~(1 << 5); /* upper case char */
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 * parse path data attribute into a shape list
1081 */
1082svgtiny_code
1083svgtiny_parse_path_data(const char *text,
1084 size_t textlen,
1085 float **pointv,
1086 unsigned int *pointc)
1087{
1088 const char *cursor = text; /* text cursor */
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((void*)0), 0, 0},
1095 {NULL((void*)0), 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 /* empty path data - equivalent to none */
1105 if (cursor == textend) {
1106 *pointc = 0;
1107 return svgtiny_OK;
1108 }
1109
1110 /* none */
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
1174parse_path_data_error:
1175 /* free any local state */
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}