NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
rasterize.h
Go to the documentation of this file.
1#pragma once
2
3#include "private.h"
4#include "utils.h"
5
6#include "vendor/ft_raster.h"
7#include "vendor/ft_stroker.h"
8
9#include <limits.h>
10
11namespace novasvg {
12namespace render {
13
15{
16 novasvg_array_init(span_buffer->spans);
17 span_buffer_reset(span_buffer);
18}
19
20NOVASVG_INLINE void span_buffer_init_rect(span_buffer_t* span_buffer, int x, int y, int width, int height)
21{
22 novasvg_array_clear(span_buffer->spans);
23 novasvg_array_ensure(span_buffer->spans, height);
24 span_t* spans = span_buffer->spans.data;
25 for(int i = 0; i < height; i++) {
26 spans[i].x = x;
27 spans[i].y = y + i;
28 spans[i].len = width;
29 spans[i].coverage = 255;
30 }
31
32 span_buffer->x = x;
33 span_buffer->y = y;
34 span_buffer->w = width;
35 span_buffer->h = height;
36 span_buffer->spans.size = height;
37}
38
40{
41 novasvg_array_clear(span_buffer->spans);
42 span_buffer->x = 0;
43 span_buffer->y = 0;
44 span_buffer->w = -1;
45 span_buffer->h = -1;
46}
47
49{
50 novasvg_array_destroy(span_buffer->spans);
51}
52
54{
55 novasvg_array_clear(span_buffer->spans);
56 novasvg_array_append(span_buffer->spans, source->spans);
57 span_buffer->x = source->x;
58 span_buffer->y = source->y;
59 span_buffer->w = source->w;
60 span_buffer->h = source->h;
61}
62
63NOVASVG_INLINE bool span_buffer_contains(const span_buffer_t* span_buffer, float x, float y)
64{
65 const int ix = (int)floorf(x);
66 const int iy = (int)floorf(y);
67
68 for(int i = 0; i < span_buffer->spans.size; i++) {
69 span_t* span = &span_buffer->spans.data[i];
70 if(span->y != iy)
71 continue;
72 if(ix >= span->x && ix < (span->x + span->len)) {
73 return true;
74 }
75 }
76
77 return false;
78}
79
81{
82 if(span_buffer->w != -1 && span_buffer->h != -1)
83 return;
84 if(span_buffer->spans.size == 0) {
85 span_buffer->x = 0;
86 span_buffer->y = 0;
87 span_buffer->w = 0;
88 span_buffer->h = 0;
89 return;
90 }
91
92 span_t* spans = span_buffer->spans.data;
93 int x1 = INT_MAX;
94 int y1 = spans[0].y;
95 int x2 = 0;
96 int y2 = spans[span_buffer->spans.size - 1].y;
97 for(int i = 0; i < span_buffer->spans.size; i++) {
98 if(spans[i].x < x1) x1 = spans[i].x;
99 if(spans[i].x + spans[i].len > x2) x2 = spans[i].x + spans[i].len;
100 }
101
102 span_buffer->x = x1;
103 span_buffer->y = y1;
104 span_buffer->w = x2 - x1;
105 span_buffer->h = y2 - y1 + 1;
106}
107
109{
110 span_buffer_update_extents(span_buffer);
111 extents->x = span_buffer->x;
112 extents->y = span_buffer->y;
113 extents->w = span_buffer->w;
114 extents->h = span_buffer->h;
115}
116
118{
119 span_buffer_reset(span_buffer);
121
122 span_t* a_spans = a->spans.data;
123 span_t* a_end = a_spans + a->spans.size;
124
125 span_t* b_spans = b->spans.data;
126 span_t* b_end = b_spans + b->spans.size;
127 while(a_spans < a_end && b_spans < b_end) {
128 if(b_spans->y > a_spans->y) {
129 ++a_spans;
130 continue;
131 }
132
133 if(a_spans->y != b_spans->y) {
134 ++b_spans;
135 continue;
136 }
137
138 int ax1 = a_spans->x;
139 int ax2 = ax1 + a_spans->len;
140 int bx1 = b_spans->x;
141 int bx2 = bx1 + b_spans->len;
142 if(bx1 < ax1 && bx2 < ax1) {
143 ++b_spans;
144 continue;
145 }
146
147 if(ax1 < bx1 && ax2 < bx1) {
148 ++a_spans;
149 continue;
150 }
151
152 int x = NOVASVG_MAX(ax1, bx1);
153 int len = NOVASVG_MIN(ax2, bx2) - x;
154 if(len) {
155 novasvg_array_ensure(span_buffer->spans, 1);
156 span_t* span = span_buffer->spans.data + span_buffer->spans.size;
157 span->x = x;
158 span->len = len;
159 span->y = a_spans->y;
160 span->coverage = (a_spans->coverage * b_spans->coverage) / 255;
161 span_buffer->spans.size += 1;
162 }
163
164 if(ax2 < bx2) {
165 ++a_spans;
166 } else {
167 ++b_spans;
168 }
169 }
170}
171
172#define ALIGN_SIZE(size) (((size) + 7ul) & ~7ul)
173NOVASVG_INLINE PVG_FT_Outline* ft_outline_create(int points, int contours)
174{
175 size_t points_size = ALIGN_SIZE((points + contours) * sizeof(PVG_FT_Vector));
176 size_t tags_size = ALIGN_SIZE((points + contours) * sizeof(char));
177 size_t contours_size = ALIGN_SIZE(contours * sizeof(int));
178 size_t contours_flag_size = ALIGN_SIZE(contours * sizeof(char));
179 PVG_FT_Outline* outline = static_cast<PVG_FT_Outline*>(malloc(points_size + tags_size + contours_size + contours_flag_size + sizeof(PVG_FT_Outline)));
180
181 PVG_FT_Byte* outline_data = (PVG_FT_Byte*)(outline + 1);
182 outline->points = (PVG_FT_Vector*)(outline_data);
183 outline->tags = (char*)(outline_data + points_size);
184 outline->contours = (int*)(outline_data + points_size + tags_size);
185 outline->contours_flag = (char*)(outline_data + points_size + tags_size + contours_size);
186 outline->n_points = 0;
187 outline->n_contours = 0;
188 outline->flags = 0x0;
189 return outline;
190}
191
192NOVASVG_INLINE void ft_outline_destroy(PVG_FT_Outline* outline)
193{
194 free(outline);
195}
196
197// roundf() without the libm call: nearest integer, halves away from zero -- exactly roundf's result. This runs
198// twice for every point of every path. `v - float(t)` is exact (t is v's integer part), so the comparison with
199// one half is exact too; anything too big for an int, or not a number, goes to roundf itself.
201{
202 if(!(v > -1e9f && v < 1e9f))
203 return (PVG_FT_Pos)roundf(v);
204 const int t = int(v);
205 const float diff = v - float(t);
206 if(diff >= 0.5f)
207 return PVG_FT_Pos(t) + 1;
208 if(diff <= -0.5f)
209 return PVG_FT_Pos(t) - 1;
210 return PVG_FT_Pos(t);
211}
212
213#define FT_COORD(x) ft_coord_round((x) * 64)
214NOVASVG_INLINE void ft_outline_move_to(PVG_FT_Outline* ft, float x, float y)
215{
216 ft->points[ft->n_points].x = FT_COORD(x);
217 ft->points[ft->n_points].y = FT_COORD(y);
218 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_ON;
219 if(ft->n_points) {
220 ft->contours[ft->n_contours] = ft->n_points - 1;
221 ft->n_contours++;
222 }
223
224 ft->contours_flag[ft->n_contours] = 1;
225 ft->n_points++;
226}
227
228NOVASVG_INLINE void ft_outline_line_to(PVG_FT_Outline* ft, float x, float y)
229{
230 ft->points[ft->n_points].x = FT_COORD(x);
231 ft->points[ft->n_points].y = FT_COORD(y);
232 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_ON;
233 ft->n_points++;
234}
235
236NOVASVG_INLINE void ft_outline_cubic_to(PVG_FT_Outline* ft, float x1, float y1, float x2, float y2, float x3, float y3)
237{
238 ft->points[ft->n_points].x = FT_COORD(x1);
239 ft->points[ft->n_points].y = FT_COORD(y1);
240 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_CUBIC;
241 ft->n_points++;
242
243 ft->points[ft->n_points].x = FT_COORD(x2);
244 ft->points[ft->n_points].y = FT_COORD(y2);
245 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_CUBIC;
246 ft->n_points++;
247
248 ft->points[ft->n_points].x = FT_COORD(x3);
249 ft->points[ft->n_points].y = FT_COORD(y3);
250 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_ON;
251 ft->n_points++;
252}
253
254NOVASVG_INLINE void ft_outline_close(PVG_FT_Outline* ft)
255{
256 ft->contours_flag[ft->n_contours] = 0;
257 int index = ft->n_contours ? ft->contours[ft->n_contours - 1] + 1 : 0;
258 if(index == ft->n_points)
259 return;
260 ft->points[ft->n_points].x = ft->points[index].x;
261 ft->points[ft->n_points].y = ft->points[index].y;
262 ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_ON;
263 ft->n_points++;
264}
265
266NOVASVG_INLINE void ft_outline_end(PVG_FT_Outline* ft)
267{
268 if(ft->n_points) {
269 ft->contours[ft->n_contours] = ft->n_points - 1;
270 ft->n_contours++;
271 }
272}
273
274NOVASVG_INLINE PVG_FT_Outline* ft_outline_convert_stroke(const path_t* path, const matrix_t* matrix, const stroke_data_t* stroke_data);
275
276NOVASVG_INLINE PVG_FT_Outline* ft_outline_convert(const path_t* path, const matrix_t* matrix, const stroke_data_t* stroke_data)
277{
278 if(stroke_data) {
279 return ft_outline_convert_stroke(path, matrix, stroke_data);
280 }
281
284
285 point_t points[3];
286 PVG_FT_Outline* outline = ft_outline_create(path->num_points, path->num_contours);
287 while(path_iterator_has_next(&it)) {
288 switch(path_iterator_next(&it, points)) {
290 matrix_map_points(matrix, points, points, 1);
291 ft_outline_move_to(outline, points[0].x, points[0].y);
292 break;
294 matrix_map_points(matrix, points, points, 1);
295 ft_outline_line_to(outline, points[0].x, points[0].y);
296 break;
298 matrix_map_points(matrix, points, points, 3);
299 ft_outline_cubic_to(outline, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
300 break;
302 ft_outline_close(outline);
303 break;
304 }
305 }
306
307 ft_outline_end(outline);
308 return outline;
309}
310
311NOVASVG_INLINE PVG_FT_Outline* ft_outline_convert_dash(const path_t* path, const matrix_t* matrix, const stroke_dash_t* stroke_dash)
312{
313 if(stroke_dash->array.size == 0)
314 return ft_outline_convert(path, matrix, NULL);
315 path_t* dashed = path_clone_dashed(path, stroke_dash->offset, stroke_dash->array.data, stroke_dash->array.size);
316 PVG_FT_Outline* outline = ft_outline_convert(dashed, matrix, NULL);
317 path_destroy(dashed);
318 return outline;
319}
320
321NOVASVG_INLINE PVG_FT_Outline* ft_outline_convert_stroke(const path_t* path, const matrix_t* matrix, const stroke_data_t* stroke_data)
322{
323 double scale_x = sqrt(matrix->a * matrix->a + matrix->b * matrix->b);
324 double scale_y = sqrt(matrix->c * matrix->c + matrix->d * matrix->d);
325
326 double scale = hypot(scale_x, scale_y) / NOVASVG_SQRT2;
327 double width = stroke_data->style.width * scale;
328
329 PVG_FT_Fixed ftWidth = (PVG_FT_Fixed)(width * 0.5 * (1 << 6));
330 PVG_FT_Fixed ftMiterLimit = (PVG_FT_Fixed)(stroke_data->style.miter_limit * (1 << 16));
331
332 PVG_FT_Stroker_LineCap ftCap;
333 switch(stroke_data->style.cap) {
335 ftCap = PVG_FT_STROKER_LINECAP_SQUARE;
336 break;
338 ftCap = PVG_FT_STROKER_LINECAP_ROUND;
339 break;
340 default:
341 ftCap = PVG_FT_STROKER_LINECAP_BUTT;
342 break;
343 }
344
345 PVG_FT_Stroker_LineJoin ftJoin;
346 switch(stroke_data->style.join) {
348 ftJoin = PVG_FT_STROKER_LINEJOIN_BEVEL;
349 break;
351 ftJoin = PVG_FT_STROKER_LINEJOIN_ROUND;
352 break;
353 default:
354 ftJoin = PVG_FT_STROKER_LINEJOIN_MITER_FIXED;
355 break;
356 }
357
358 PVG_FT_Stroker stroker;
359 PVG_FT_Stroker_New(&stroker);
360 PVG_FT_Stroker_Set(stroker, ftWidth, ftCap, ftJoin, ftMiterLimit);
361
362 PVG_FT_Outline* outline = ft_outline_convert_dash(path, matrix, &stroke_data->dash);
363 PVG_FT_Stroker_ParseOutline(stroker, outline);
364
365 PVG_FT_UInt points;
366 PVG_FT_UInt contours;
367 PVG_FT_Stroker_GetCounts(stroker, &points, &contours);
368
369 PVG_FT_Outline* stroke_outline = ft_outline_create(points, contours);
370 PVG_FT_Stroker_Export(stroker, stroke_outline);
371
372 PVG_FT_Stroker_Done(stroker);
373 ft_outline_destroy(outline);
374 return stroke_outline;
375}
376
377NOVASVG_INLINE void spans_generation_callback(int count, const PVG_FT_Span* spans, void* user)
378{
379 span_buffer_t* span_buffer = (span_buffer_t*)(user);
380 novasvg_array_append_data(span_buffer->spans, spans, count);
381}
382
383NOVASVG_INLINE void rasterize(span_buffer_t* span_buffer, const path_t* path, const matrix_t* matrix, const rect_t* clip_rect, const stroke_data_t* stroke_data, fill_rule_t winding)
384{
385 PVG_FT_Outline* outline = ft_outline_convert(path, matrix, stroke_data);
386 if(stroke_data) {
387 outline->flags = PVG_FT_OUTLINE_NONE;
388 } else {
389 switch(winding) {
391 outline->flags = PVG_FT_OUTLINE_EVEN_ODD_FILL;
392 break;
393 default:
394 outline->flags = PVG_FT_OUTLINE_NONE;
395 break;
396 }
397 }
398
399 PVG_FT_Raster_Params params;
400 params.flags = PVG_FT_RASTER_FLAG_DIRECT | PVG_FT_RASTER_FLAG_AA;
401 params.gray_spans = spans_generation_callback;
402 params.user = span_buffer;
403 params.source = outline;
404 if(clip_rect) {
405 params.flags |= PVG_FT_RASTER_FLAG_CLIP;
406 params.clip_box.xMin = (PVG_FT_Pos)clip_rect->x;
407 params.clip_box.yMin = (PVG_FT_Pos)clip_rect->y;
408 params.clip_box.xMax = (PVG_FT_Pos)(clip_rect->x + clip_rect->w);
409 params.clip_box.yMax = (PVG_FT_Pos)(clip_rect->y + clip_rect->h);
410 }
411
412 span_buffer_reset(span_buffer);
413 PVG_FT_Raster_Render(&params);
414 ft_outline_destroy(outline);
415}
416
417
418} // namespace render
419} // namespace novasvg
#define NOVASVG_MAX(a, b)
Definition color.h:11
#define NOVASVG_MIN(a, b)
Definition color.h:14
void span_buffer_init_rect(span_buffer_t *span_buffer, int x, int y, int width, int height)
Definition rasterize.h:20
void span_buffer_init(span_buffer_t *span_buffer)
Definition rasterize.h:14
@ NOVASVG_PATH_COMMAND_CUBIC_TO
Draws a cubic Bézier curve to a new point.
Definition render.h:313
@ NOVASVG_PATH_COMMAND_CLOSE
Closes the current path by drawing a line to the starting point.
Definition render.h:314
@ NOVASVG_PATH_COMMAND_LINE_TO
Draws a straight line to a new point.
Definition render.h:312
@ NOVASVG_PATH_COMMAND_MOVE_TO
Moves the current point to a new position.
Definition render.h:311
NOVASVG_INLINE void path_iterator_init(path_iterator_t *it, const path_t *path)
Initializes a path iterator for a given path.
Definition path.h:11
NOVASVG_INLINE void matrix_map_points(const matrix_t *matrix, const point_t *src, point_t *dst, int count)
Transforms an array of src points using matrix and stores the results in dst.
Definition matrix.h:111
NOVASVG_INLINE PVG_FT_Outline * ft_outline_create(int points, int contours)
Definition rasterize.h:173
NOVASVG_INLINE void ft_outline_end(PVG_FT_Outline *ft)
Definition rasterize.h:266
NOVASVG_INLINE void ft_outline_cubic_to(PVG_FT_Outline *ft, float x1, float y1, float x2, float y2, float x3, float y3)
Definition rasterize.h:236
NOVASVG_INLINE void path_destroy(path_t *path)
Decreases the reference count of a path object.
Definition path.h:61
NOVASVG_INLINE bool path_iterator_has_next(const path_iterator_t *it)
Checks if there are more elements to iterate over.
Definition path.h:18
NOVASVG_INLINE void ft_outline_close(PVG_FT_Outline *ft)
Definition rasterize.h:254
NOVASVG_INLINE void span_buffer_update_extents(span_buffer_t *span_buffer)
Definition rasterize.h:80
@ NOVASVG_LINE_JOIN_BEVEL
Beveled join with a flattened corner.
Definition render.h:1467
@ NOVASVG_LINE_JOIN_ROUND
Rounded join.
Definition render.h:1466
fill_rule_t
Defines fill rule types for filling paths.
Definition render.h:1421
@ NOVASVG_FILL_RULE_EVEN_ODD
Even-odd fill rule.
Definition render.h:1423
NOVASVG_INLINE PVG_FT_Outline * ft_outline_convert_dash(const path_t *path, const matrix_t *matrix, const stroke_dash_t *stroke_dash)
Definition rasterize.h:311
NOVASVG_INLINE void spans_generation_callback(int count, const PVG_FT_Span *spans, void *user)
Definition rasterize.h:377
@ NOVASVG_LINE_CAP_SQUARE
Square ends at the end of the stroke.
Definition render.h:1458
@ NOVASVG_LINE_CAP_ROUND
Rounded ends at the end of the stroke.
Definition render.h:1457
void span_buffer_reset(span_buffer_t *span_buffer)
Definition rasterize.h:39
void span_buffer_destroy(span_buffer_t *span_buffer)
Definition rasterize.h:48
NOVASVG_INLINE void ft_outline_move_to(PVG_FT_Outline *ft, float x, float y)
Definition rasterize.h:214
void span_buffer_copy(span_buffer_t *span_buffer, const span_buffer_t *source)
Definition rasterize.h:53
void span_buffer_extents(span_buffer_t *span_buffer, rect_t *extents)
Definition rasterize.h:108
NOVASVG_INLINE path_t * path_clone_dashed(const path_t *path, float offset, const float *dashes, int ndashes)
Creates a copy of the path with a dashed pattern applied.
Definition path.h:657
NOVASVG_INLINE void ft_outline_line_to(PVG_FT_Outline *ft, float x, float y)
Definition rasterize.h:228
NOVASVG_INLINE void ft_outline_destroy(PVG_FT_Outline *outline)
Definition rasterize.h:192
void span_buffer_intersect(span_buffer_t *span_buffer, const span_buffer_t *a, const span_buffer_t *b)
Definition rasterize.h:117
NOVASVG_INLINE path_command_t path_iterator_next(path_iterator_t *it, point_t points[3])
Retrieves the current command and its associated points, then advances the iterator.
Definition path.h:23
NOVASVG_INLINE PVG_FT_Pos ft_coord_round(float v)
Definition rasterize.h:200
bool span_buffer_contains(const span_buffer_t *span_buffer, float x, float y)
Definition rasterize.h:63
NOVASVG_INLINE PVG_FT_Outline * ft_outline_convert(const path_t *path, const matrix_t *matrix, const stroke_data_t *stroke_data)
Definition rasterize.h:276
void rasterize(span_buffer_t *span_buffer, const path_t *path, const matrix_t *matrix, const rect_t *clip_rect, const stroke_data_t *stroke_data, fill_rule_t winding)
Definition rasterize.h:383
NOVASVG_INLINE PVG_FT_Outline * ft_outline_convert_stroke(const path_t *path, const matrix_t *matrix, const stroke_data_t *stroke_data)
Definition rasterize.h:321
Main namespace for the NovaSVG library.
Definition bitmap.h:23
#define NOVASVG_INLINE
Definition novasvg.h:42
#define FT_COORD(x)
Definition rasterize.h:213
#define ALIGN_SIZE(size)
Definition rasterize.h:172
#define NOVASVG_SQRT2
Definition render.h:105
A structure representing a 2D transformation matrix.
Definition render.h:138
float d
The vertical scaling factor.
Definition render.h:142
float c
The horizontal shearing factor.
Definition render.h:141
float b
The vertical shearing factor.
Definition render.h:140
float a
The horizontal scaling factor.
Definition render.h:139
Iterator for traversing path elements in a path.
Definition render.h:362
A structure representing a point in 2D space.
Definition render.h:114
A structure representing a rectangle in 2D space.
Definition render.h:125
float x
The x-coordinate of the top-left corner of the rectangle.
Definition render.h:126
float w
The width of the rectangle.
Definition render.h:128
float h
The height of the rectangle.
Definition render.h:129
float y
The y-coordinate of the top-left corner of the rectangle.
Definition render.h:127
struct novasvg::render::span_buffer_t::@12 spans
unsigned char coverage
Definition private.h:116
struct novasvg::render::stroke_dash_t::@13 array
#define novasvg_array_append_data(array, newdata, count)
Definition utils.h:60
#define novasvg_array_ensure(array, count)
Definition utils.h:49
#define novasvg_array_destroy(array)
Definition utils.h:71
#define novasvg_array_append(array, other)
Definition utils.h:69
#define novasvg_array_clear(array)
Definition utils.h:70
#define novasvg_array_init(array)
Definition utils.h:36