NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1#pragma once
2
3#ifndef NOVASVG_UTILS_H
4#define NOVASVG_UTILS_H
5
6#include <stdbool.h>
7#include <stdint.h>
8#include <stdlib.h>
9#include <stddef.h>
10#include <string.h>
11#include <float.h>
12#include <math.h>
13#include <algorithm>
14
15namespace novasvg {
16namespace render {
17
18#define NOVASVG_IS_NUM(c) ((c) >= '0' && (c) <= '9')
19#define NOVASVG_IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
20#define NOVASVG_IS_ALNUM(c) (NOVASVG_IS_ALPHA(c) || NOVASVG_IS_NUM(c))
21#define NOVASVG_IS_WS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
22
23// min/max/clamp were plain macros here (novasvg_min/max/clamp) -- pure
24// numeric helpers with an exact STL equivalent, so they're gone in favor
25// of std::min/std::max/std::clamp at every call site instead of being
26// reimplemented. What's left below (alpha/red/green/blue channel
27// extraction, the dynamic array, the mutex wrapper, the refcounting
28// helpers) has no STL equivalent doing the same job, so those stay as
29// genuinely-own, C-style, prefixed macros/functions.
30
31#define novasvg_alpha(c) (((c) >> 24) & 0xff)
32#define novasvg_red(c) (((c) >> 16) & 0xff)
33#define novasvg_green(c) (((c) >> 8) & 0xff)
34#define novasvg_blue(c) (((c) >> 0) & 0xff)
35
36#define novasvg_array_init(array) \
37 do { \
38 (array).data = NULL; \
39 (array).size = 0; \
40 (array).capacity = 0; \
41 } while(0)
42
43#ifdef __cplusplus
44#define NOVASVG_ARRAY_CAST(array, value) static_cast<decltype((array).data)>(value)
45#else
46#define NOVASVG_ARRAY_CAST(array, value) (value)
47#endif
48
49#define novasvg_array_ensure(array, count) \
50 do { \
51 if((array).size + (count) > (array).capacity) { \
52 int capacity = (array).size + (count); \
53 int newcapacity = (array).capacity == 0 ? 8 : (array).capacity; \
54 while(newcapacity < capacity) { newcapacity *= 2; } \
55 (array).data = NOVASVG_ARRAY_CAST(array, realloc((array).data, newcapacity * sizeof((array).data[0]))); \
56 (array).capacity = newcapacity; \
57 } \
58 } while(0)
59
60#define novasvg_array_append_data(array, newdata, count) \
61 do { \
62 if(newdata && count > 0) { \
63 novasvg_array_ensure(array, count); \
64 memcpy((array).data + (array).size, newdata, (count) * sizeof((newdata)[0])); \
65 (array).size += count; \
66 } \
67 } while(0)
68
69#define novasvg_array_append(array, other) novasvg_array_append_data(array, (other).data, (other).size)
70#define novasvg_array_clear(array) ((array).size = 0)
71#define novasvg_array_destroy(array) free((array).data)
72
74{
75 uint32_t a = novasvg_alpha(color);
76 uint32_t r = novasvg_red(color);
77 uint32_t g = novasvg_green(color);
78 uint32_t b = novasvg_blue(color);
79 if(a != 255) {
80 r = (r * a) / 255;
81 g = (g * a) / 255;
82 b = (b * a) / 255;
83 }
84
85 return (a << 24) | (r << 16) | (g << 8) | (b);
86}
87
88NOVASVG_INLINE bool parse_number(const char** begin, const char* end, float* number)
89{
90 const char* it = *begin;
91 float integer = 0;
92 float fraction = 0;
93 float exponent = 0;
94 int sign = 1;
95 int expsign = 1;
96
97 if(it < end && *it == '+') {
98 ++it;
99 } else if(it < end && *it == '-') {
100 ++it;
101 sign = -1;
102 }
103
104 if(it >= end || (*it != '.' && !NOVASVG_IS_NUM(*it)))
105 return false;
106 if(NOVASVG_IS_NUM(*it)) {
107 do {
108 integer = 10.f * integer + (*it++ - '0');
109 } while(it < end && NOVASVG_IS_NUM(*it));
110 }
111
112 if(it < end && *it == '.') {
113 ++it;
114 if(it >= end || !NOVASVG_IS_NUM(*it))
115 return false;
116 float divisor = 1.f;
117 do {
118 fraction = 10.f * fraction + (*it++ - '0');
119 divisor *= 10.f;
120 } while(it < end && NOVASVG_IS_NUM(*it));
121 fraction /= divisor;
122 }
123
124 if(it < end && (*it == 'e' || *it == 'E')) {
125 ++it;
126 if(it < end && *it == '+') {
127 ++it;
128 } else if(it < end && *it == '-') {
129 ++it;
130 expsign = -1;
131 }
132
133 if(it >= end || !NOVASVG_IS_NUM(*it))
134 return false;
135 do {
136 exponent = 10 * exponent + (*it++ - '0');
137 } while(it < end && NOVASVG_IS_NUM(*it));
138 }
139
140 *begin = it;
141 *number = sign * (integer + fraction);
142 if(exponent)
143 *number *= powf(10.f, expsign * exponent);
144 return *number >= -FLT_MAX && *number <= FLT_MAX;
145}
146
147NOVASVG_INLINE bool skip_delim(const char** begin, const char* end, const char delim)
148{
149 const char* it = *begin;
150 if(it < end && *it == delim) {
151 *begin = it + 1;
152 return true;
153 }
154
155 return false;
156}
157
158NOVASVG_INLINE bool skip_string(const char** begin, const char* end, const char* data)
159{
160 const char* it = *begin;
161 while(it < end && *data && *it == *data) {
162 ++data;
163 ++it;
164 }
165
166 if(*data == '\0') {
167 *begin = it;
168 return true;
169 }
170
171 return false;
172}
173
174NOVASVG_INLINE bool skip_ws(const char** begin, const char* end)
175{
176 const char* it = *begin;
177 while(it < end && NOVASVG_IS_WS(*it))
178 ++it;
179 *begin = it;
180 return it < end;
181}
182
183NOVASVG_INLINE bool skip_ws_and_delim(const char** begin, const char* end, char delim)
184{
185 const char* it = *begin;
186 if(skip_ws(&it, end)) {
187 if(!skip_delim(&it, end, delim))
188 return false;
189 skip_ws(&it, end);
190 } else {
191 return false;
192 }
193
194 *begin = it;
195 return it < end;
196}
197
198NOVASVG_INLINE bool skip_ws_and_comma(const char** begin, const char* end)
199{
200 return skip_ws_and_delim(begin, end, ',');
201}
202
203NOVASVG_INLINE bool skip_ws_or_delim(const char** begin, const char* end, char delim, bool* has_delim)
204{
205 const char* it = *begin;
206 if(has_delim)
207 *has_delim = false;
208 if(skip_ws(&it, end)) {
209 if(skip_delim(&it, end, delim)) {
210 if(has_delim)
211 *has_delim = true;
212 skip_ws(&it, end);
213 }
214 }
215
216 if(it == *begin)
217 return false;
218 *begin = it;
219 return it < end;
220}
221
222NOVASVG_INLINE bool skip_ws_or_comma(const char** begin, const char* end, bool* has_comma)
223{
224 return skip_ws_or_delim(begin, end, ',', has_comma);
225}
226
227// ponytail: CSS transform values (e.g. `rotate(15deg)`, set via a CSS
228// class/style rather than the plain SVG presentation attribute) carry a
229// unit immediately after the number, which the SVG attribute grammar
230// above doesn't expect. Rather than reject the whole transform list on
231// the first such token (the previous behavior), convert angle units to
232// degrees in place and just skip length units (px and friends -- treating
233// them as raw user-units is an acceptable simplification for translate).
234// Upgrade path: consult the target element's viewport/font-size for true
235// unit conversion (%, em, ...) if that ever matters in practice.
236NOVASVG_INLINE void skip_css_unit(const char** begin, const char* end, float* number)
237{
238 const char* it = *begin;
239 const char* unitStart = it;
240 while(it < end && ((*it >= 'a' && *it <= 'z') || (*it >= 'A' && *it <= 'Z') || *it == '%'))
241 ++it;
242 if(it == unitStart)
243 return;
244
245 auto length = size_t(it - unitStart);
246 auto matches = [&](const char* unit) {
247 auto unitLength = strlen(unit);
248 return length == unitLength && strncmp(unitStart, unit, unitLength) == 0;
249 };
250
251 if(matches("rad"))
252 *number *= 180.f / 3.14159265f;
253 else if(matches("grad"))
254 *number *= 0.9f;
255 else if(matches("turn"))
256 *number *= 360.f;
257 // "deg", "px", "pt", "%", and anything else: value already usable as-is.
258
259 *begin = it;
260}
261
262} // namespace render
263} // namespace novasvg
264
265#endif // NOVASVG_UTILS_H
NOVASVG_INLINE bool parse_number(const char **begin, const char *end, float *number)
Definition utils.h:88
NOVASVG_INLINE bool skip_ws_or_comma(const char **begin, const char *end, bool *has_comma)
Definition utils.h:222
NOVASVG_INLINE bool skip_ws_and_delim(const char **begin, const char *end, char delim)
Definition utils.h:183
NOVASVG_INLINE uint32_t premultiply_argb(uint32_t color)
Definition utils.h:73
NOVASVG_INLINE bool skip_ws_and_comma(const char **begin, const char *end)
Definition utils.h:198
NOVASVG_INLINE bool skip_delim(const char **begin, const char *end, const char delim)
Definition utils.h:147
NOVASVG_INLINE bool skip_string(const char **begin, const char *end, const char *data)
Definition utils.h:158
NOVASVG_INLINE bool skip_ws(const char **begin, const char *end)
Definition utils.h:174
NOVASVG_INLINE void skip_css_unit(const char **begin, const char *end, float *number)
Definition utils.h:236
NOVASVG_INLINE bool skip_ws_or_delim(const char **begin, const char *end, char delim, bool *has_delim)
Definition utils.h:203
Main namespace for the NovaSVG library.
Definition bitmap.h:23
#define NOVASVG_INLINE
Definition novasvg.h:42
Represents a color with red, green, blue, and alpha components.
Definition render.h:1019
#define NOVASVG_IS_NUM(c)
Definition utils.h:18
#define novasvg_green(c)
Definition utils.h:33
#define novasvg_blue(c)
Definition utils.h:34
#define NOVASVG_IS_WS(c)
Definition utils.h:21
#define novasvg_red(c)
Definition utils.h:32
#define novasvg_alpha(c)
Definition utils.h:31