NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
blend.h
Go to the documentation of this file.
1#pragma once
2
3#include "private.h"
4#include "utils.h"
5
6#include <assert.h>
7#include <limits.h>
8
9namespace novasvg {
10namespace render {
11
12#define COLOR_TABLE_SIZE 1024
13typedef struct {
16 uint32_t colortable[COLOR_TABLE_SIZE];
17 union {
18 struct {
19 float x1, y1;
20 float x2, y2;
21 } linear;
22 struct {
23 float cx, cy, cr;
24 float fx, fy, fr;
25 } radial;
26 } values;
28
29typedef struct {
31 uint8_t* data;
32 int width;
33 int height;
34 int stride;
37
38typedef struct {
39 float dx;
40 float dy;
41 float l;
42 float off;
44
45typedef struct {
46 float dx;
47 float dy;
48 float dr;
49 float sqrfr;
50 float a;
53
55{
56 uint32_t alpha = lroundf(color->a * opacity * 255);
57 uint32_t pr = lroundf(color->r * alpha);
58 uint32_t pg = lroundf(color->g * alpha);
59 uint32_t pb = lroundf(color->b * alpha);
60 return (alpha << 24) | (pr << 16) | (pg << 8) | (pb);
61}
62
63NOVASVG_INLINE uint32_t INTERPOLATE_PIXEL_255(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
64{
65 uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
66 t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
67 t &= 0xff00ff;
68
69 x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
70 x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
71 x &= 0xff00ff00;
72 x |= t;
73 return x;
74}
75
76NOVASVG_INLINE uint32_t INTERPOLATE_PIXEL_256(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
77{
78 uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
79 t >>= 8;
80 t &= 0xff00ff;
81
82 x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
83 x &= 0xff00ff00;
84 x |= t;
85 return x;
86}
87
88NOVASVG_INLINE uint32_t BYTE_MUL(uint32_t x, uint32_t a)
89{
90 uint32_t t = (x & 0xff00ff) * a;
91 t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
92 t &= 0xff00ff;
93
94 x = ((x >> 8) & 0xff00ff) * a;
95 x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
96 x &= 0xff00ff00;
97 x |= t;
98 return x;
99}
100
101#ifdef __SSE2__
102
103} // namespace render
104} // namespace novasvg
105#include <emmintrin.h>
106namespace novasvg {
107namespace render {
108
109NOVASVG_INLINE void memfill32(unsigned int* dest, int length, unsigned int value)
110{
111 __m128i vector_data = _mm_set_epi32(value, value, value, value);
112 while(length && ((uintptr_t)dest & 0xf)) {
113 *dest++ = value;
114 length--;
115 }
116
117 while(length >= 32) {
118 _mm_store_si128((__m128i*)(dest), vector_data);
119 _mm_store_si128((__m128i*)(dest + 4), vector_data);
120 _mm_store_si128((__m128i*)(dest + 8), vector_data);
121 _mm_store_si128((__m128i*)(dest + 12), vector_data);
122 _mm_store_si128((__m128i*)(dest + 16), vector_data);
123 _mm_store_si128((__m128i*)(dest + 20), vector_data);
124 _mm_store_si128((__m128i*)(dest + 24), vector_data);
125 _mm_store_si128((__m128i*)(dest + 28), vector_data);
126
127 dest += 32;
128 length -= 32;
129 }
130
131 if(length >= 16) {
132 _mm_store_si128((__m128i*)(dest), vector_data);
133 _mm_store_si128((__m128i*)(dest + 4), vector_data);
134 _mm_store_si128((__m128i*)(dest + 8), vector_data);
135 _mm_store_si128((__m128i*)(dest + 12), vector_data);
136
137 dest += 16;
138 length -= 16;
139 }
140
141 if(length >= 8) {
142 _mm_store_si128((__m128i*)(dest), vector_data);
143 _mm_store_si128((__m128i*)(dest + 4), vector_data);
144
145 dest += 8;
146 length -= 8;
147 }
148
149 if(length >= 4) {
150 _mm_store_si128((__m128i*)(dest), vector_data);
151
152 dest += 4;
153 length -= 4;
154 }
155
156 while(length) {
157 *dest++ = value;
158 length--;
159 }
160}
161
162#else
163
164NOVASVG_INLINE void memfill32(unsigned int* dest, int length, unsigned int value)
165{
166 while(length--) {
167 *dest++ = value;
168 }
169}
170
171#endif // __SSE2__
172
173NOVASVG_INLINE int gradient_clamp(const gradient_data_t* gradient, int ipos)
174{
175 if(gradient->spread == NOVASVG_SPREAD_METHOD_REPEAT) {
176 ipos = ipos % COLOR_TABLE_SIZE;
177 ipos = ipos < 0 ? COLOR_TABLE_SIZE + ipos : ipos;
178 } else if(gradient->spread == NOVASVG_SPREAD_METHOD_REFLECT) {
179 const int limit = COLOR_TABLE_SIZE * 2;
180 ipos = ipos % limit;
181 ipos = ipos < 0 ? limit + ipos : ipos;
182 ipos = ipos >= COLOR_TABLE_SIZE ? limit - 1 - ipos : ipos;
183 } else {
184 if(ipos < 0) {
185 ipos = 0;
186 } else if(ipos >= COLOR_TABLE_SIZE) {
187 ipos = COLOR_TABLE_SIZE - 1;
188 }
189 }
190
191 return ipos;
192}
193
194#define FIXPT_BITS 8
195#define FIXPT_SIZE (1 << FIXPT_BITS)
196NOVASVG_INLINE uint32_t gradient_pixel_fixed(const gradient_data_t* gradient, int fixed_pos)
197{
198 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
199 return gradient->colortable[gradient_clamp(gradient, ipos)];
200}
201
202NOVASVG_INLINE uint32_t gradient_pixel(const gradient_data_t* gradient, float pos)
203{
204 int ipos = (int)(pos * (COLOR_TABLE_SIZE - 1) + 0.5f);
205 return gradient->colortable[gradient_clamp(gradient, ipos)];
206}
207
208NOVASVG_INLINE void fetch_linear_gradient(uint32_t* buffer, const linear_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length)
209{
210 float t, inc;
211 float rx = 0, ry = 0;
212
213 if(v->l == 0.f) {
214 t = inc = 0;
215 } else {
216 rx = gradient->matrix.c * (y + 0.5f) + gradient->matrix.a * (x + 0.5f) + gradient->matrix.e;
217 ry = gradient->matrix.d * (y + 0.5f) + gradient->matrix.b * (x + 0.5f) + gradient->matrix.f;
218 t = v->dx * rx + v->dy * ry + v->off;
219 inc = v->dx * gradient->matrix.a + v->dy * gradient->matrix.b;
220 t *= (COLOR_TABLE_SIZE - 1);
221 inc *= (COLOR_TABLE_SIZE - 1);
222 }
223
224 const uint32_t* end = buffer + length;
225 if(inc > -1e-5f && inc < 1e-5f) {
226 memfill32(buffer, length, gradient_pixel_fixed(gradient, (int)(t * FIXPT_SIZE)));
227 } else {
228 if(t + inc * length < (float)(INT_MAX >> (FIXPT_BITS + 1)) && t + inc * length > (float)(INT_MIN >> (FIXPT_BITS + 1))) {
229 int t_fixed = (int)(t * FIXPT_SIZE);
230 int inc_fixed = (int)(inc * FIXPT_SIZE);
231 while(buffer < end) {
232 *buffer = gradient_pixel_fixed(gradient, t_fixed);
233 t_fixed += inc_fixed;
234 ++buffer;
235 }
236 } else {
237 while(buffer < end) {
238 *buffer = gradient_pixel(gradient, t / COLOR_TABLE_SIZE);
239 t += inc;
240 ++buffer;
241 }
242 }
243 }
244}
245
246NOVASVG_INLINE void fetch_radial_gradient(uint32_t* buffer, const radial_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length)
247{
248 if(v->a == 0.f) {
249 memfill32(buffer, length, 0);
250 return;
251 }
252
253 float rx = gradient->matrix.c * (y + 0.5f) + gradient->matrix.e + gradient->matrix.a * (x + 0.5f);
254 float ry = gradient->matrix.d * (y + 0.5f) + gradient->matrix.f + gradient->matrix.b * (x + 0.5f);
255
256 rx -= gradient->values.radial.fx;
257 ry -= gradient->values.radial.fy;
258
259 float inv_a = 1.f / (2.f * v->a);
260 float delta_rx = gradient->matrix.a;
261 float delta_ry = gradient->matrix.b;
262
263 float b = 2 * (v->dr * gradient->values.radial.fr + rx * v->dx + ry * v->dy);
264 float delta_b = 2 * (delta_rx * v->dx + delta_ry * v->dy);
265 float b_delta_b = 2 * b * delta_b;
266 float delta_b_delta_b = 2 * delta_b * delta_b;
267
268 float bb = b * b;
269 float delta_bb = delta_b * delta_b;
270
271 b *= inv_a;
272 delta_b *= inv_a;
273
274 float rxrxryry = rx * rx + ry * ry;
275 float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
276 float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
277 float delta_rx_plus_ry = 2 * delta_rxrxryry;
278
279 inv_a *= inv_a;
280
281 float det = (bb - 4 * v->a * (v->sqrfr - rxrxryry)) * inv_a;
282 float delta_det = (b_delta_b + delta_bb + 4 * v->a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
283 float delta_delta_det = (delta_b_delta_b + 4 * v->a * delta_rx_plus_ry) * inv_a;
284
285 const uint32_t* end = buffer + length;
286 if(v->extended) {
287 while(buffer < end) {
288 uint32_t result = 0;
289 if(det >= 0) {
290 float w = sqrtf(det) - b;
291 if(gradient->values.radial.fr + v->dr * w >= 0) {
292 result = gradient_pixel(gradient, w);
293 }
294 }
295
296 *buffer = result;
297 det += delta_det;
298 delta_det += delta_delta_det;
299 b += delta_b;
300 ++buffer;
301 }
302 } else {
303 while(buffer < end) {
304 *buffer++ = gradient_pixel(gradient, sqrtf(det) - b);
305 det += delta_det;
306 delta_det += delta_delta_det;
307 b += delta_b;
308 }
309 }
310}
311
312NOVASVG_INLINE void composition_solid_clear(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
313{
314 if(const_alpha == 255) {
315 memfill32(dest, length, 0);
316 } else {
317 uint32_t ialpha = 255 - const_alpha;
318 for(int i = 0; i < length; i++) {
319 dest[i] = BYTE_MUL(dest[i], ialpha);
320 }
321 }
322}
323
324NOVASVG_INLINE void composition_solid_source(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
325{
326 if(const_alpha == 255) {
327 memfill32(dest, length, color);
328 } else {
329 uint32_t ialpha = 255 - const_alpha;
330 color = BYTE_MUL(color, const_alpha);
331 for(int i = 0; i < length; i++) {
332 dest[i] = color + BYTE_MUL(dest[i], ialpha);
333 }
334 }
335}
336
337NOVASVG_INLINE void composition_solid_destination(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
338{
339}
340
341NOVASVG_INLINE void composition_solid_source_over(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
342{
343 if(const_alpha != 255)
344 color = BYTE_MUL(color, const_alpha);
345 uint32_t ialpha = 255 - novasvg_alpha(color);
346 for(int i = 0; i < length; i++) {
347 dest[i] = color + BYTE_MUL(dest[i], ialpha);
348 }
349}
350
351NOVASVG_INLINE void composition_solid_destination_over(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
352{
353 if(const_alpha != 255)
354 color = BYTE_MUL(color, const_alpha);
355 for(int i = 0; i < length; i++) {
356 uint32_t d = dest[i];
357 dest[i] = d + BYTE_MUL(color, novasvg_alpha(~d));
358 }
359}
360
361NOVASVG_INLINE void composition_solid_source_in(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
362{
363 if(const_alpha == 255) {
364 for(int i = 0; i < length; i++) {
365 dest[i] = BYTE_MUL(color, novasvg_alpha(dest[i]));
366 }
367 } else {
368 color = BYTE_MUL(color, const_alpha);
369 uint32_t cia = 255 - const_alpha;
370 for(int i = 0; i < length; i++) {
371 uint32_t d = dest[i];
372 dest[i] = INTERPOLATE_PIXEL_255(color, novasvg_alpha(d), d, cia);
373 }
374 }
375}
376
377NOVASVG_INLINE void composition_solid_destination_in(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
378{
379 uint32_t a = novasvg_alpha(color);
380 if(const_alpha != 255)
381 a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
382 for(int i = 0; i < length; i++) {
383 dest[i] = BYTE_MUL(dest[i], a);
384 }
385}
386
387NOVASVG_INLINE void composition_solid_source_out(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
388{
389 if(const_alpha == 255) {
390 for(int i = 0; i < length; i++) {
391 dest[i] = BYTE_MUL(color, novasvg_alpha(~dest[i]));
392 }
393 } else {
394 color = BYTE_MUL(color, const_alpha);
395 uint32_t cia = 255 - const_alpha;
396 for(int i = 0; i < length; i++) {
397 uint32_t d = dest[i];
398 dest[i] = INTERPOLATE_PIXEL_255(color, novasvg_alpha(~d), d, cia);
399 }
400 }
401}
402
403NOVASVG_INLINE void composition_solid_destination_out(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
404{
405 uint32_t a = novasvg_alpha(~color);
406 if(const_alpha != 255)
407 a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
408 for(int i = 0; i < length; i++) {
409 dest[i] = BYTE_MUL(dest[i], a);
410 }
411}
412
413NOVASVG_INLINE void composition_solid_source_atop(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
414{
415 if(const_alpha != 255)
416 color = BYTE_MUL(color, const_alpha);
417 uint32_t sia = novasvg_alpha(~color);
418 for(int i = 0; i < length; i++) {
419 uint32_t d = dest[i];
420 dest[i] = INTERPOLATE_PIXEL_255(color, novasvg_alpha(d), d, sia);
421 }
422}
423
424NOVASVG_INLINE void composition_solid_destination_atop(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
425{
426 uint32_t a = novasvg_alpha(color);
427 if(const_alpha != 255) {
428 color = BYTE_MUL(color, const_alpha);
429 a = novasvg_alpha(color) + 255 - const_alpha;
430 }
431
432 for(int i = 0; i < length; i++) {
433 uint32_t d = dest[i];
434 dest[i] = INTERPOLATE_PIXEL_255(d, a, color, novasvg_alpha(~d));
435 }
436}
437
438NOVASVG_INLINE void composition_solid_xor(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
439{
440 if(const_alpha != 255)
441 color = BYTE_MUL(color, const_alpha);
442 uint32_t sia = novasvg_alpha(~color);
443 for(int i = 0; i < length; i++) {
444 uint32_t d = dest[i];
445 dest[i] = INTERPOLATE_PIXEL_255(color, novasvg_alpha(~d), d, sia);
446 }
447}
448
449// ---- CSS blend modes -------------------------------------------------------------------------------
450// W3C Compositing and Blending, on premultiplied channels (all 0..255, "mul" = x*y/255 rounded):
451// result colour = Cs*(1-ab) + Cb*(1-as) + as*ab*B(Cb/ab, Cs/as) result alpha = as + ab - as*ab
452// which for each separable mode B simplifies to (Cs, Cb premultiplied; as, ab the two alphas):
453// multiply Cs*(1-ab) + Cb*(1-as) + Cs*Cb
454// screen Cs + Cb - Cs*Cb
455// darken Cs + Cb - max(Cs*ab, Cb*as)
456// lighten Cs + Cb - min(Cs*ab, Cb*as)
457// difference Cs + Cb - 2*min(Cs*ab, Cb*as)
458// exclusion Cs + Cb - 2*Cs*Cb
460{
461 return (x * y + 127) / 255;
462}
463
464template<operator_t Mode>
465NOVASVG_INLINE int blend_mode_channel(int cs, int cb, int as, int ab)
466{
467 int value;
468 if(Mode == NOVASVG_OPERATOR_MULTIPLY)
469 value = blend_mul255(cs, 255 - ab) + blend_mul255(cb, 255 - as) + blend_mul255(cs, cb);
470 else if(Mode == NOVASVG_OPERATOR_SCREEN)
471 value = cs + cb - blend_mul255(cs, cb);
472 else if(Mode == NOVASVG_OPERATOR_DARKEN)
473 value = cs + cb - NOVASVG_MAX(blend_mul255(cs, ab), blend_mul255(cb, as));
474 else if(Mode == NOVASVG_OPERATOR_LIGHTEN)
475 value = cs + cb - NOVASVG_MIN(blend_mul255(cs, ab), blend_mul255(cb, as));
476 else if(Mode == NOVASVG_OPERATOR_DIFFERENCE)
477 value = cs + cb - 2 * NOVASVG_MIN(blend_mul255(cs, ab), blend_mul255(cb, as));
478 else
479 value = cs + cb - 2 * blend_mul255(cs, cb);
480 return value;
481}
482
483template<operator_t Mode>
484NOVASVG_INLINE uint32_t blend_mode_pixel(uint32_t dest, uint32_t src)
485{
486 if(src == 0)
487 return dest;
488 const int as = novasvg_alpha(src);
489 const int ab = novasvg_alpha(dest);
490 const int ar = as + ab - blend_mul255(as, ab);
491 uint32_t result = uint32_t(ar) << 24;
492 for(int shift = 16; shift >= 0; shift -= 8) {
493 int c = blend_mode_channel<Mode>(int((src >> shift) & 0xff), int((dest >> shift) & 0xff), as, ab);
494 c = NOVASVG_MAX(0, NOVASVG_MIN(c, ar)); // a premultiplied channel never exceeds the alpha
495 result |= uint32_t(c) << shift;
496 }
497
498 return result;
499}
500
501template<operator_t Mode>
502NOVASVG_INLINE void composition_blend_mode(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
503{
504 for(int i = 0; i < length; i++) {
505 uint32_t s = const_alpha == 255 ? src[i] : BYTE_MUL(src[i], const_alpha);
506 dest[i] = blend_mode_pixel<Mode>(dest[i], s);
507 }
508}
509
510template<operator_t Mode>
511NOVASVG_INLINE void composition_solid_blend_mode(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
512{
513 if(const_alpha != 255)
514 color = BYTE_MUL(color, const_alpha);
515 for(int i = 0; i < length; i++)
516 dest[i] = blend_mode_pixel<Mode>(dest[i], color);
517}
518
519typedef void(*composition_solid_function_t)(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha);
520
534 composition_solid_blend_mode<NOVASVG_OPERATOR_MULTIPLY>,
535 composition_solid_blend_mode<NOVASVG_OPERATOR_SCREEN>,
536 composition_solid_blend_mode<NOVASVG_OPERATOR_DARKEN>,
537 composition_solid_blend_mode<NOVASVG_OPERATOR_LIGHTEN>,
538 composition_solid_blend_mode<NOVASVG_OPERATOR_DIFFERENCE>,
539 composition_solid_blend_mode<NOVASVG_OPERATOR_EXCLUSION>
540};
541
542NOVASVG_INLINE void composition_clear(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
543{
544 if(const_alpha == 255) {
545 memfill32(dest, length, 0);
546 } else {
547 uint32_t ialpha = 255 - const_alpha;
548 for(int i = 0; i < length; i++) {
549 dest[i] = BYTE_MUL(dest[i], ialpha);
550 }
551 }
552}
553
554NOVASVG_INLINE void composition_source(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
555{
556 if(const_alpha == 255) {
557 memcpy(dest, src, length * sizeof(uint32_t));
558 } else {
559 uint32_t ialpha = 255 - const_alpha;
560 for(int i = 0; i < length; i++) {
561 dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha);
562 }
563 }
564}
565
566NOVASVG_INLINE void composition_destination(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
567{
568}
569
570NOVASVG_INLINE void composition_source_over(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
571{
572 if(const_alpha == 255) {
573 for(int i = 0; i < length; i++) {
574 uint32_t s = src[i];
575 if(s >= 0xff000000) {
576 dest[i] = s;
577 } else if(s != 0) {
578 dest[i] = s + BYTE_MUL(dest[i], novasvg_alpha(~s));
579 }
580 }
581 } else {
582 for(int i = 0; i < length; i++) {
583 uint32_t s = BYTE_MUL(src[i], const_alpha);
584 dest[i] = s + BYTE_MUL(dest[i], novasvg_alpha(~s));
585 }
586 }
587}
588
589NOVASVG_INLINE void composition_destination_over(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
590{
591 if(const_alpha == 255) {
592 for(int i = 0; i < length; i++) {
593 uint32_t d = dest[i];
594 dest[i] = d + BYTE_MUL(src[i], novasvg_alpha(~d));
595 }
596 } else {
597 for(int i = 0; i < length; i++) {
598 uint32_t d = dest[i];
599 uint32_t s = BYTE_MUL(src[i], const_alpha);
600 dest[i] = d + BYTE_MUL(s, novasvg_alpha(~d));
601 }
602 }
603}
604
605NOVASVG_INLINE void composition_source_in(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
606{
607 if(const_alpha == 255) {
608 for(int i = 0; i < length; i++) {
609 dest[i] = BYTE_MUL(src[i], novasvg_alpha(dest[i]));
610 }
611 } else {
612 uint32_t cia = 255 - const_alpha;
613 for(int i = 0; i < length; i++) {
614 uint32_t d = dest[i];
615 uint32_t s = BYTE_MUL(src[i], const_alpha);
616 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(d), d, cia);
617 }
618 }
619}
620
621NOVASVG_INLINE void composition_destination_in(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
622{
623 if(const_alpha == 255) {
624 for(int i = 0; i < length; i++) {
625 dest[i] = BYTE_MUL(dest[i], novasvg_alpha(src[i]));
626 }
627 } else {
628 uint32_t cia = 255 - const_alpha;
629 for(int i = 0; i < length; i++) {
630 uint32_t a = BYTE_MUL(novasvg_alpha(src[i]), const_alpha) + cia;
631 dest[i] = BYTE_MUL(dest[i], a);
632 }
633 }
634}
635
636NOVASVG_INLINE void composition_source_out(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
637{
638 if(const_alpha == 255) {
639 for(int i = 0; i < length; i++) {
640 dest[i] = BYTE_MUL(src[i], novasvg_alpha(~dest[i]));
641 }
642 } else {
643 uint32_t cia = 255 - const_alpha;
644 for(int i = 0; i < length; i++) {
645 uint32_t s = BYTE_MUL(src[i], const_alpha);
646 uint32_t d = dest[i];
647 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(~d), d, cia);
648 }
649 }
650}
651
652NOVASVG_INLINE void composition_destination_out(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
653{
654 if(const_alpha == 255) {
655 for(int i = 0; i < length; i++) {
656 dest[i] = BYTE_MUL(dest[i], novasvg_alpha(~src[i]));
657 }
658 } else {
659 uint32_t cia = 255 - const_alpha;
660 for(int i = 0; i < length; i++) {
661 uint32_t sia = BYTE_MUL(novasvg_alpha(~src[i]), const_alpha) + cia;
662 dest[i] = BYTE_MUL(dest[i], sia);
663 }
664 }
665}
666
667NOVASVG_INLINE void composition_source_atop(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
668{
669 if(const_alpha == 255) {
670 for(int i = 0; i < length; i++) {
671 uint32_t s = src[i];
672 uint32_t d = dest[i];
673 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(d), d, novasvg_alpha(~s));
674 }
675 } else {
676 for(int i = 0; i < length; i++) {
677 uint32_t s = BYTE_MUL(src[i], const_alpha);
678 uint32_t d = dest[i];
679 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(d), d, novasvg_alpha(~s));
680 }
681 }
682}
683
684NOVASVG_INLINE void composition_destination_atop(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
685{
686 if(const_alpha == 255) {
687 for(int i = 0; i < length; i++) {
688 uint32_t s = src[i];
689 uint32_t d = dest[i];
690 dest[i] = INTERPOLATE_PIXEL_255(d, novasvg_alpha(s), s, novasvg_alpha(~d));
691 }
692 } else {
693 uint32_t cia = 255 - const_alpha;
694 for(int i = 0; i < length; i++) {
695 uint32_t s = BYTE_MUL(src[i], const_alpha);
696 uint32_t d = dest[i];
697 uint32_t a = novasvg_alpha(s) + cia;
698 dest[i] = INTERPOLATE_PIXEL_255(d, a, s, novasvg_alpha(~d));
699 }
700 }
701}
702
703NOVASVG_INLINE void composition_xor(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
704{
705 if(const_alpha == 255) {
706 for(int i = 0; i < length; i++) {
707 uint32_t d = dest[i];
708 uint32_t s = src[i];
709 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(~d), d, novasvg_alpha(~s));
710 }
711 } else {
712 for(int i = 0; i < length; i++) {
713 uint32_t d = dest[i];
714 uint32_t s = BYTE_MUL(src[i], const_alpha);
715 dest[i] = INTERPOLATE_PIXEL_255(s, novasvg_alpha(~d), d, novasvg_alpha(~s));
716 }
717 }
718}
719
720typedef void(*composition_function_t)(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha);
721
735 composition_blend_mode<NOVASVG_OPERATOR_MULTIPLY>,
736 composition_blend_mode<NOVASVG_OPERATOR_SCREEN>,
737 composition_blend_mode<NOVASVG_OPERATOR_DARKEN>,
738 composition_blend_mode<NOVASVG_OPERATOR_LIGHTEN>,
739 composition_blend_mode<NOVASVG_OPERATOR_DIFFERENCE>,
740 composition_blend_mode<NOVASVG_OPERATOR_EXCLUSION>
741};
742
743NOVASVG_INLINE void blend_solid(surface_t* surface, operator_t op, uint32_t solid, const span_buffer_t* span_buffer)
744{
746 int count = span_buffer->spans.size;
747 const span_t* spans = span_buffer->spans.data;
748 while(count--) {
749 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
750 func(target, spans->len, solid, spans->coverage);
751 ++spans;
752 }
753}
754
755#define BUFFER_SIZE 1024
757{
759 unsigned int buffer[BUFFER_SIZE];
760
762 v.dx = gradient->values.linear.x2 - gradient->values.linear.x1;
763 v.dy = gradient->values.linear.y2 - gradient->values.linear.y1;
764 v.l = v.dx * v.dx + v.dy * v.dy;
765 v.off = 0.f;
766 if(v.l != 0.f) {
767 v.dx /= v.l;
768 v.dy /= v.l;
769 v.off = -v.dx * gradient->values.linear.x1 - v.dy * gradient->values.linear.y1;
770 }
771
772 int count = span_buffer->spans.size;
773 const span_t* spans = span_buffer->spans.data;
774 while(count--) {
775 int length = spans->len;
776 int x = spans->x;
777 while(length) {
778 int l = NOVASVG_MIN(length, BUFFER_SIZE);
779 fetch_linear_gradient(buffer, &v, gradient, spans->y, x, l);
780 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
781 func(target, l, buffer, spans->coverage);
782 x += l;
783 length -= l;
784 }
785
786 ++spans;
787 }
788}
789
791{
793 unsigned int buffer[BUFFER_SIZE];
794
796 v.dx = gradient->values.radial.cx - gradient->values.radial.fx;
797 v.dy = gradient->values.radial.cy - gradient->values.radial.fy;
798 v.dr = gradient->values.radial.cr - gradient->values.radial.fr;
799 v.sqrfr = gradient->values.radial.fr * gradient->values.radial.fr;
800 v.a = v.dr * v.dr - v.dx * v.dx - v.dy * v.dy;
801 v.extended = gradient->values.radial.fr != 0.f || v.a <= 0.f;
802
803 int count = span_buffer->spans.size;
804 const span_t* spans = span_buffer->spans.data;
805 while(count--) {
806 int length = spans->len;
807 int x = spans->x;
808 while(length) {
809 int l = NOVASVG_MIN(length, BUFFER_SIZE);
810 fetch_radial_gradient(buffer, &v, gradient, spans->y, x, l);
811 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
812 func(target, l, buffer, spans->coverage);
813 x += l;
814 length -= l;
815 }
816
817 ++spans;
818 }
819}
820
822{
824
825 const int image_width = texture->width;
826 const int image_height = texture->height;
827
828 int xoff = (int)(texture->matrix.e);
829 int yoff = (int)(texture->matrix.f);
830
831 int count = span_buffer->spans.size;
832 const span_t* spans = span_buffer->spans.data;
833 while(count--) {
834 int x = spans->x;
835 int length = spans->len;
836 int sx = xoff + x;
837 int sy = yoff + spans->y;
838 if(sy >= 0 && sy < image_height && sx < image_width) {
839 if(sx < 0) {
840 x -= sx;
841 length += sx;
842 sx = 0;
843 }
844
845 if(sx + length > image_width)
846 length = image_width - sx;
847 if(length > 0) {
848 const int coverage = (spans->coverage * texture->const_alpha) >> 8;
849 const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx;
850 uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
851 func(dest, length, src, coverage);
852 }
853 }
854
855 ++spans;
856 }
857}
858
859#define FIXED_SCALE (1 << 16)
861{
863 uint32_t buffer[BUFFER_SIZE];
864
865 int image_width = texture->width;
866 int image_height = texture->height;
867
868 int fdx = (int)(texture->matrix.a * FIXED_SCALE);
869 int fdy = (int)(texture->matrix.b * FIXED_SCALE);
870
871 int count = span_buffer->spans.size;
872 const span_t* spans = span_buffer->spans.data;
873 while(count--) {
874 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
875
876 const float cx = spans->x + 0.5f;
877 const float cy = spans->y + 0.5f;
878
879 int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE);
880 int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE);
881
882 int length = spans->len;
883 const int coverage = (spans->coverage * texture->const_alpha) >> 8;
884 while(length) {
885 int l = NOVASVG_MIN(length, BUFFER_SIZE);
886 const uint32_t* end = buffer + l;
887 uint32_t* b = buffer;
888 while(b < end) {
889 int px = x >> 16;
890 int py = y >> 16;
891 if((px < 0) || (px >= image_width) || (py < 0) || (py >= image_height)) {
892 *b = 0x00000000;
893 } else {
894 *b = ((const uint32_t*)(texture->data + py * texture->stride))[px];
895 }
896
897 x += fdx;
898 y += fdy;
899 ++b;
900 }
901
902 func(target, l, buffer, coverage);
903 target += l;
904 length -= l;
905 }
906
907 ++spans;
908 }
909}
910
912{
914
915 int image_width = texture->width;
916 int image_height = texture->height;
917
918 int xoff = (int)(texture->matrix.e) % image_width;
919 int yoff = (int)(texture->matrix.f) % image_height;
920
921 if(xoff < 0)
922 xoff += image_width;
923 if(yoff < 0) {
924 yoff += image_height;
925 }
926
927 int count = span_buffer->spans.size;
928 const span_t* spans = span_buffer->spans.data;
929 while(count--) {
930 int x = spans->x;
931 int length = spans->len;
932 int sx = (xoff + spans->x) % image_width;
933 int sy = (spans->y + yoff) % image_height;
934 if(sx < 0)
935 sx += image_width;
936 if(sy < 0) {
937 sy += image_height;
938 }
939
940 const int coverage = (spans->coverage * texture->const_alpha) >> 8;
941 while(length) {
942 int l = NOVASVG_MIN(image_width - sx, length);
943 if(BUFFER_SIZE < l)
944 l = BUFFER_SIZE;
945 const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx;
946 uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
947 func(dest, l, src, coverage);
948 x += l;
949 sx += l;
950 length -= l;
951 if(sx >= image_width) {
952 sx = 0;
953 }
954 }
955
956 ++spans;
957 }
958}
959
961{
963 uint32_t buffer[BUFFER_SIZE];
964
965 int image_width = texture->width;
966 int image_height = texture->height;
967 const int scanline_offset = texture->stride / 4;
968
969 int fdx = (int)(texture->matrix.a * FIXED_SCALE);
970 int fdy = (int)(texture->matrix.b * FIXED_SCALE);
971
972 int count = span_buffer->spans.size;
973 const span_t* spans = span_buffer->spans.data;
974 while(count--) {
975 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
976 const uint32_t* image_bits = (const uint32_t*)texture->data;
977
978 const float cx = spans->x + 0.5f;
979 const float cy = spans->y + 0.5f;
980
981 int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE);
982 int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE);
983
984 const int coverage = (spans->coverage * texture->const_alpha) >> 8;
985 int length = spans->len;
986 while(length) {
987 int l = NOVASVG_MIN(length, BUFFER_SIZE);
988 const uint32_t* end = buffer + l;
989 uint32_t* b = buffer;
990 while(b < end) {
991 int px = x >> 16;
992 int py = y >> 16;
993 px %= image_width;
994 py %= image_height;
995 if(px < 0) px += image_width;
996 if(py < 0) py += image_height;
997 int y_offset = py * scanline_offset;
998
999 assert(px >= 0 && px < image_width);
1000 assert(py >= 0 && py < image_height);
1001
1002 *b = image_bits[y_offset + px];
1003 x += fdx;
1004 y += fdy;
1005 ++b;
1006 }
1007
1008 func(target, l, buffer, coverage);
1009 target += l;
1010 length -= l;
1011 }
1012
1013 ++spans;
1014 }
1015}
1016
1017NOVASVG_INLINE uint32_t interpolate_4_pixels(uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, uint32_t distx, uint32_t disty)
1018{
1019 uint32_t idistx = 256 - distx;
1020 uint32_t idisty = 256 - disty;
1021 uint32_t xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
1022 uint32_t xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
1023 return INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
1024}
1025
1026#define HALF_POINT (1 << 15)
1028{
1030 uint32_t buffer[BUFFER_SIZE];
1031
1032 int image_width = texture->width;
1033 int image_height = texture->height;
1034
1035 int fdx = (int)(texture->matrix.a * FIXED_SCALE);
1036 int fdy = (int)(texture->matrix.b * FIXED_SCALE);
1037
1038 int count = span_buffer->spans.size;
1039 const span_t* spans = span_buffer->spans.data;
1040 while(count--) {
1041 uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
1042
1043 const float cx = spans->x + 0.5f;
1044 const float cy = spans->y + 0.5f;
1045
1046 int fx = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE);
1047 int fy = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE);
1048
1049 fx -= HALF_POINT;
1050 fy -= HALF_POINT;
1051
1052 const int coverage = (spans->coverage * texture->const_alpha) >> 8;
1053 int length = spans->len;
1054 while(length) {
1055 int l = NOVASVG_MIN(length, BUFFER_SIZE);
1056 const uint32_t* end = buffer + l;
1057 uint32_t* b = buffer;
1058 while (b < end) {
1059 int x1 = (fx >> 16) % image_width;
1060 int y1 = (fy >> 16) % image_height;
1061
1062 if(x1 < 0) x1 += image_width;
1063 if(y1 < 0) y1 += image_height;
1064
1065 int x2 = (x1 + 1) % image_width;
1066 int y2 = (y1 + 1) % image_height;
1067
1068 const uint32_t* s1 = (const uint32_t*)(texture->data + y1 * texture->stride);
1069 const uint32_t* s2 = (const uint32_t*)(texture->data + y2 * texture->stride);
1070
1071 uint32_t tl = s1[x1];
1072 uint32_t tr = s1[x2];
1073 uint32_t bl = s2[x1];
1074 uint32_t br = s2[x2];
1075
1076 int distx = (fx & 0x0000ffff) >> 8;
1077 int disty = (fy & 0x0000ffff) >> 8;
1078 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1079
1080 fx += fdx;
1081 fy += fdy;
1082 ++b;
1083 }
1084
1085 func(target, l, buffer, coverage);
1086 target += l;
1087 length -= l;
1088 }
1089
1090 ++spans;
1091 }
1092}
1093
1095{
1098 uint32_t alpha = novasvg_alpha(solid);
1099
1100 if(alpha == 255 && state->op == NOVASVG_OPERATOR_SRC_OVER) {
1101 blend_solid(canvas->surface, NOVASVG_OPERATOR_SRC, solid, span_buffer);
1102 } else {
1103 blend_solid(canvas->surface, state->op, solid, span_buffer);
1104 }
1105}
1106
1108{
1109 if(gradient->nstops == 0)
1110 return;
1112 gradient_data_t data;
1113 data.spread = gradient->spread;
1114 data.matrix = gradient->matrix;
1115 matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
1116 if(!matrix_invert(&data.matrix, &data.matrix))
1117 return;
1118 int i, pos = 0, nstops = gradient->nstops;
1119 const gradient_stop_t *curr, *next, *start, *last;
1120 uint32_t curr_color, next_color, last_color;
1121 uint32_t dist, idist;
1122 float delta, t, incr, fpos;
1123 float opacity = state->opacity;
1124
1125 start = gradient->stops;
1126 curr = start;
1127 curr_color = premultiply_color_with_opacity(&curr->color, opacity);
1128
1129 data.colortable[pos++] = curr_color;
1130 incr = 1.0f / COLOR_TABLE_SIZE;
1131 fpos = 1.5f * incr;
1132
1133 while(fpos <= curr->offset) {
1134 data.colortable[pos] = data.colortable[pos - 1];
1135 ++pos;
1136 fpos += incr;
1137 }
1138
1139 for(i = 0; i < nstops - 1; i++) {
1140 curr = (start + i);
1141 next = (start + i + 1);
1142 if(curr->offset == next->offset)
1143 continue;
1144 delta = 1.f / (next->offset - curr->offset);
1145 next_color = premultiply_color_with_opacity(&next->color, opacity);
1146 while(fpos < next->offset && pos < COLOR_TABLE_SIZE) {
1147 t = (fpos - curr->offset) * delta;
1148 dist = (uint32_t)(255 * t);
1149 idist = 255 - dist;
1150 data.colortable[pos] = INTERPOLATE_PIXEL_255(curr_color, idist, next_color, dist);
1151 ++pos;
1152 fpos += incr;
1153 }
1154
1155 curr_color = next_color;
1156 }
1157
1158 last = start + nstops - 1;
1159 last_color = premultiply_color_with_opacity(&last->color, opacity);
1160 for(; pos < COLOR_TABLE_SIZE; ++pos) {
1161 data.colortable[pos] = last_color;
1162 }
1163
1164 if(gradient->type == NOVASVG_GRADIENT_TYPE_LINEAR) {
1165 data.values.linear.x1 = gradient->values[0];
1166 data.values.linear.y1 = gradient->values[1];
1167 data.values.linear.x2 = gradient->values[2];
1168 data.values.linear.y2 = gradient->values[3];
1169 blend_linear_gradient(canvas->surface, state->op, &data, span_buffer);
1170 } else {
1171 data.values.radial.cx = gradient->values[0];
1172 data.values.radial.cy = gradient->values[1];
1173 data.values.radial.cr = gradient->values[2];
1174 data.values.radial.fx = gradient->values[3];
1175 data.values.radial.fy = gradient->values[4];
1176 data.values.radial.fr = gradient->values[5];
1177 blend_radial_gradient(canvas->surface, state->op, &data, span_buffer);
1178 }
1179}
1180
1182{
1183 if(texture->surface == NULL)
1184 return;
1186 texture_data_t data;
1187 data.matrix = texture->matrix;
1188 data.data = texture->surface->data;
1189 data.width = texture->surface->width;
1190 data.height = texture->surface->height;
1191 data.stride = texture->surface->stride;
1192 data.const_alpha = lroundf(state->opacity * texture->opacity * 256);
1193
1194 matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
1195 if(!matrix_invert(&data.matrix, &data.matrix))
1196 return;
1197 const matrix_t* matrix = &data.matrix;
1198 if(matrix->a == 1 && matrix->b == 0 && matrix->c == 0 && matrix->d == 1) {
1199 if(texture->type == NOVASVG_TEXTURE_TYPE_PLAIN) {
1200 blend_untransformed_argb(canvas->surface, state->op, &data, span_buffer);
1201 } else {
1202 blend_untransformed_tiled_argb(canvas->surface, state->op, &data, span_buffer);
1203 }
1204 } else {
1205 if(texture->type == NOVASVG_TEXTURE_TYPE_PLAIN) {
1206 blend_transformed_argb(canvas->surface, state->op, &data, span_buffer);
1207 } else if(fabsf(matrix->b) > 1e-6f || fabsf(matrix->c) > 1e-6f) {
1209 } else {
1210 blend_transformed_tiled_argb(canvas->surface, state->op, &data, span_buffer);
1211 }
1212 }
1213}
1214
1216{
1217 if(span_buffer->spans.size == 0)
1218 return;
1219 if(canvas->state->paint == NULL) {
1220 blend_color(canvas, &canvas->state->color, span_buffer);
1221 return;
1222 }
1223
1226 solid_paint_t* solid = (solid_paint_t*)(paint);
1227 blend_color(canvas, &solid->color, span_buffer);
1228 } else if(paint->type == NOVASVG_PAINT_TYPE_GRADIENT) {
1229 gradient_paint_t* gradient = (gradient_paint_t*)(paint);
1230 blend_gradient(canvas, gradient, span_buffer);
1231 } else {
1232 texture_paint_t* texture = (texture_paint_t*)(paint);
1233 blend_texture(canvas, texture, span_buffer);
1234 }
1235}
1236
1237
1238} // namespace render
1239} // namespace novasvg
#define COLOR_TABLE_SIZE
Definition blend.h:12
#define FIXPT_SIZE
Definition blend.h:195
#define BUFFER_SIZE
Definition blend.h:755
#define FIXED_SCALE
Definition blend.h:859
#define HALF_POINT
Definition blend.h:1026
#define FIXPT_BITS
Definition blend.h:194
#define NOVASVG_MAX(a, b)
Definition color.h:11
#define NOVASVG_MIN(a, b)
Definition color.h:14
NOVASVG_INLINE void composition_solid_destination_over(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:351
NOVASVG_INLINE uint32_t premultiply_color_with_opacity(const color_t *color, float opacity)
Definition blend.h:54
NOVASVG_INLINE void fetch_radial_gradient(uint32_t *buffer, const radial_gradient_values_t *v, const gradient_data_t *gradient, int y, int x, int length)
Definition blend.h:246
NOVASVG_INLINE void composition_solid_destination(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:337
NOVASVG_INLINE int gradient_clamp(const gradient_data_t *gradient, int ipos)
Definition blend.h:173
NOVASVG_INLINE void blend_color(canvas_t *canvas, const color_t *color, const span_buffer_t *span_buffer)
Definition blend.h:1094
NOVASVG_INLINE void composition_source_in(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:605
NOVASVG_INLINE void composition_solid_blend_mode(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:511
NOVASVG_INLINE void blend_solid(surface_t *surface, operator_t op, uint32_t solid, const span_buffer_t *span_buffer)
Definition blend.h:743
@ NOVASVG_TEXTURE_TYPE_PLAIN
Plain texture.
Definition render.h:1295
NOVASVG_INLINE void blend_gradient(canvas_t *canvas, const gradient_paint_t *gradient, const span_buffer_t *span_buffer)
Definition blend.h:1107
NOVASVG_INLINE void blend_untransformed_argb(surface_t *surface, operator_t op, const texture_data_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:821
NOVASVG_INLINE void composition_destination_atop(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:684
NOVASVG_INLINE int blend_mode_channel(int cs, int cb, int as, int ab)
Definition blend.h:465
NOVASVG_INLINE void composition_solid_source_atop(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:413
NOVASVG_INLINE uint32_t blend_mode_pixel(uint32_t dest, uint32_t src)
Definition blend.h:484
NOVASVG_INLINE void blend_texture(canvas_t *canvas, const texture_paint_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:1181
NOVASVG_INLINE void composition_solid_source_out(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:387
NOVASVG_INLINE uint32_t gradient_pixel_fixed(const gradient_data_t *gradient, int fixed_pos)
Definition blend.h:196
NOVASVG_INLINE void composition_solid_destination_out(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:403
NOVASVG_INLINE void composition_source(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:554
NOVASVG_INLINE void composition_source_over(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:570
NOVASVG_INLINE void blend_untransformed_tiled_argb(surface_t *surface, operator_t op, const texture_data_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:911
NOVASVG_INLINE const composition_function_t composition_table[]
Definition blend.h:722
NOVASVG_INLINE uint32_t gradient_pixel(const gradient_data_t *gradient, float pos)
Definition blend.h:202
NOVASVG_INLINE int blend_mul255(int x, int y)
Definition blend.h:459
NOVASVG_INLINE void composition_solid_destination_atop(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:424
NOVASVG_INLINE void composition_destination_in(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:621
NOVASVG_INLINE void composition_solid_source(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:324
NOVASVG_INLINE void composition_solid_clear(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:312
void(* composition_function_t)(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:720
NOVASVG_INLINE void composition_destination_over(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:589
NOVASVG_INLINE void composition_source_out(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:636
NOVASVG_INLINE uint32_t INTERPOLATE_PIXEL_255(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
Definition blend.h:63
NOVASVG_INLINE void composition_solid_source_in(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:361
NOVASVG_INLINE void blend_transformed_bilinear_tiled_argb(surface_t *surface, operator_t op, const texture_data_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:1027
NOVASVG_INLINE bool matrix_invert(const matrix_t *matrix, matrix_t *inverse)
Calculates the inverse of matrix and stores it in inverse.
Definition matrix.h:81
NOVASVG_INLINE void composition_solid_destination_in(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:377
NOVASVG_INLINE void blend_linear_gradient(surface_t *surface, operator_t op, const gradient_data_t *gradient, const span_buffer_t *span_buffer)
Definition blend.h:756
NOVASVG_INLINE uint32_t INTERPOLATE_PIXEL_256(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
Definition blend.h:76
NOVASVG_INLINE void blend(canvas_t *canvas, const span_buffer_t *span_buffer)
Definition blend.h:1215
NOVASVG_INLINE void composition_xor(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:703
NOVASVG_INLINE void fetch_linear_gradient(uint32_t *buffer, const linear_gradient_values_t *v, const gradient_data_t *gradient, int y, int x, int length)
Definition blend.h:208
NOVASVG_INLINE void blend_transformed_argb(surface_t *surface, operator_t op, const texture_data_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:860
NOVASVG_INLINE void composition_source_atop(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:667
NOVASVG_INLINE void blend_radial_gradient(surface_t *surface, operator_t op, const gradient_data_t *gradient, const span_buffer_t *span_buffer)
Definition blend.h:790
NOVASVG_INLINE void composition_clear(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:542
operator_t
Defines compositing operations.
Definition render.h:1429
@ NOVASVG_OPERATOR_SRC
Source replaces destination.
Definition render.h:1431
@ NOVASVG_OPERATOR_MULTIPLY
Backdrop * source: darkens, white is neutral.
Definition render.h:1444
@ NOVASVG_OPERATOR_LIGHTEN
The lighter of backdrop and source, per channel.
Definition render.h:1447
@ NOVASVG_OPERATOR_DARKEN
The darker of backdrop and source, per channel.
Definition render.h:1446
@ NOVASVG_OPERATOR_SRC_OVER
Source is composited over destination.
Definition render.h:1433
@ NOVASVG_OPERATOR_DIFFERENCE
Absolute difference of backdrop and source, per channel.
Definition render.h:1448
@ NOVASVG_OPERATOR_SCREEN
Inverse of multiply: lightens, black is neutral.
Definition render.h:1445
NOVASVG_INLINE void memfill32(unsigned int *dest, int length, unsigned int value)
Definition blend.h:164
NOVASVG_INLINE void blend_transformed_tiled_argb(surface_t *surface, operator_t op, const texture_data_t *texture, const span_buffer_t *span_buffer)
Definition blend.h:960
NOVASVG_INLINE uint32_t BYTE_MUL(uint32_t x, uint32_t a)
Definition blend.h:88
NOVASVG_INLINE void matrix_multiply(matrix_t *matrix, const matrix_t *left, const matrix_t *right)
Multiplies left and right matrices and stores the result in matrix.
Definition matrix.h:70
spread_method_t
Defines the spread method for gradients.
Definition render.h:1302
@ NOVASVG_SPREAD_METHOD_REFLECT
Reflect the gradient beyond its bounds.
Definition render.h:1304
@ NOVASVG_SPREAD_METHOD_REPEAT
Repeat the gradient pattern.
Definition render.h:1305
NOVASVG_INLINE void composition_destination_out(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:652
NOVASVG_INLINE void composition_solid_xor(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:438
NOVASVG_INLINE void composition_blend_mode(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:502
NOVASVG_INLINE uint32_t interpolate_4_pixels(uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, uint32_t distx, uint32_t disty)
Definition blend.h:1017
void(* composition_solid_function_t)(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:519
NOVASVG_INLINE void composition_destination(uint32_t *dest, int length, const uint32_t *src, uint32_t const_alpha)
Definition blend.h:566
@ NOVASVG_PAINT_TYPE_GRADIENT
Definition private.h:75
@ NOVASVG_PAINT_TYPE_COLOR
Definition private.h:74
@ NOVASVG_GRADIENT_TYPE_LINEAR
Definition private.h:90
NOVASVG_INLINE void composition_solid_source_over(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
Definition blend.h:341
NOVASVG_INLINE const composition_solid_function_t composition_solid_table[]
Definition blend.h:521
Main namespace for the NovaSVG library.
Definition bitmap.h:23
#define NOVASVG_INLINE
Definition novasvg.h:42
surface_t * surface
Definition private.h:170
Represents a color with red, green, blue, and alpha components.
Definition render.h:1019
float b
Blue component (0 to 1).
Definition render.h:1022
float g
Green component (0 to 1).
Definition render.h:1021
float a
Alpha (opacity) component (0 to 1).
Definition render.h:1023
float r
Red component (0 to 1).
Definition render.h:1020
union novasvg::render::gradient_data_t::@0 values
struct novasvg::render::gradient_data_t::@0::@2 radial
struct novasvg::render::gradient_data_t::@0::@1 linear
uint32_t colortable[1024]
Definition blend.h:16
Represents a gradient stop.
Definition render.h:1311
color_t color
The color of the gradient stop.
Definition render.h:1313
float offset
The offset of the gradient stop, as a value between 0 and 1.
Definition render.h:1312
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 f
The vertical translation offset.
Definition render.h:144
float e
The horizontal translation offset.
Definition render.h:143
float b
The vertical shearing factor.
Definition render.h:140
float a
The horizontal scaling factor.
Definition render.h:139
paint_type_t type
Definition private.h:81
struct novasvg::render::span_buffer_t::@12 spans
unsigned char coverage
Definition private.h:116
unsigned char * data
Definition private.h:57
#define novasvg_alpha(c)
Definition utils.h:31