1 /*
2 * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
3
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25
26 #include "tvgMath.h"
27 #include "tvgSwCommon.h"
28 #include "tvgFill.h"
29
30 /************************************************************************/
31 /* Internal Class Implementation */
32 /************************************************************************/
33
34 #define RADIAL_A_THRESHOLD 0.0005f
35 #define GRADIENT_STOP_SIZE 1024
36 #define FIXPT_BITS 8
37 #define FIXPT_SIZE (1<<FIXPT_BITS)
38
39 /*
40 * quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
41 * A = a // fill->radial.a
42 * B = 2 * (dr * fr + rx * dx + ry * dy)
43 * C = fr^2 - rx^2 - ry^2
44 * Derivatives are computed with respect to dx.
45 * This procedure aims to optimize and eliminate the need to calculate all values from the beginning
46 * for consecutive x values with a constant y. The Taylor series expansions are computed as long as
47 * its terms are non-zero.
48 */
_calculateCoefficients(const SwFill * fill,uint32_t x,uint32_t y,float & b,float & deltaB,float & det,float & deltaDet,float & deltaDeltaDet)49 static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
50 {
51 auto radial = &fill->radial;
52
53 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
54 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
55
56 b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
57 deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
58
59 auto rr = rx * rx + ry * ry;
60 auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
61 auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
62
63 det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
64 deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr * 0.5f;
65 deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
66 }
67
68
_estimateAAMargin(const Fill * fdata)69 static uint32_t _estimateAAMargin(const Fill* fdata)
70 {
71 constexpr float marginScalingFactor = 800.0f;
72 if (fdata->type() == Type::RadialGradient) {
73 auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
74 return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
75 }
76 auto grad = P(static_cast<const LinearGradient*>(fdata));
77 Point p1 {grad->x1, grad->y1};
78 Point p2 {grad->x2, grad->y2};
79 auto len = length(&p1, &p2);
80 return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / len);
81 }
82
83
_adjustAAMargin(uint32_t & iMargin,uint32_t index)84 static void _adjustAAMargin(uint32_t& iMargin, uint32_t index)
85 {
86 constexpr float threshold = 0.1f;
87 constexpr uint32_t iMarginMax = 40;
88
89 auto iThreshold = static_cast<uint32_t>(index * threshold);
90 if (iMargin > iThreshold) iMargin = iThreshold;
91 if (iMargin > iMarginMax) iMargin = iMarginMax;
92 }
93
94
_alphaUnblend(uint32_t c)95 static inline uint32_t _alphaUnblend(uint32_t c)
96 {
97 auto a = (c >> 24);
98 if (a == 255 || a == 0) return c;
99 auto invA = 255.0f / static_cast<float>(a);
100 auto c0 = static_cast<uint8_t>(static_cast<float>((c >> 16) & 0xFF) * invA);
101 auto c1 = static_cast<uint8_t>(static_cast<float>((c >> 8) & 0xFF) * invA);
102 auto c2 = static_cast<uint8_t>(static_cast<float>(c & 0xFF) * invA);
103
104 return (a << 24) | (c0 << 16) | (c1 << 8) | c2;
105 }
106
107
_applyAA(const SwFill * fill,uint32_t begin,uint32_t end)108 static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
109 {
110 if (begin == 0 || end == 0) return;
111
112 auto i = GRADIENT_STOP_SIZE - end;
113 auto rgbaEnd = _alphaUnblend(fill->ctable[i]);
114 auto rgbaBegin = _alphaUnblend(fill->ctable[begin]);
115
116 auto dt = 1.0f / (begin + end + 1.0f);
117 float t = dt;
118 while (i != begin) {
119 auto dist = 255 - static_cast<int32_t>(255 * t);
120 auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist);
121 fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
122
123 if (i == GRADIENT_STOP_SIZE) i = 0;
124 t += dt;
125 }
126 }
127
128
_updateColorTable(SwFill * fill,const Fill * fdata,const SwSurface * surface,uint8_t opacity)129 static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
130 {
131 if (fill->solid) return true;
132
133 if (!fill->ctable) {
134 fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
135 if (!fill->ctable) return false;
136 }
137
138 const Fill::ColorStop* colors;
139 auto cnt = fdata->colorStops(&colors);
140 if (cnt == 0 || !colors) return false;
141
142 auto pColors = colors;
143
144 auto a = MULTIPLY(pColors->a, opacity);
145 if (a < 255) fill->translucent = true;
146
147 auto r = pColors->r;
148 auto g = pColors->g;
149 auto b = pColors->b;
150 auto rgba = surface->join(r, g, b, a);
151
152 auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
153 auto pos = 1.5f * inc;
154 uint32_t i = 0;
155
156 //If repeat is true, anti-aliasing must be applied between the last and the first colors.
157 auto repeat = fill->spread == FillSpread::Repeat;
158 uint32_t iAABegin = repeat ? _estimateAAMargin(fdata) : 0;
159 uint32_t iAAEnd = 0;
160
161 fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a);
162
163 while (pos <= pColors->offset) {
164 fill->ctable[i] = fill->ctable[i - 1];
165 ++i;
166 pos += inc;
167 }
168
169 for (uint32_t j = 0; j < cnt - 1; ++j) {
170 if (repeat && j == cnt - 2 && iAAEnd == 0) {
171 iAAEnd = iAABegin;
172 _adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i);
173 }
174
175 auto curr = colors + j;
176 auto next = curr + 1;
177 auto delta = 1.0f / (next->offset - curr->offset);
178 auto a2 = MULTIPLY(next->a, opacity);
179 if (!fill->translucent && a2 < 255) fill->translucent = true;
180
181 auto rgba2 = surface->join(next->r, next->g, next->b, a2);
182
183 while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
184 auto t = (pos - curr->offset) * delta;
185 auto dist = static_cast<int32_t>(255 * t);
186 auto dist2 = 255 - dist;
187
188 auto color = INTERPOLATE(rgba, rgba2, dist2);
189 fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
190
191 ++i;
192 pos += inc;
193 }
194 rgba = rgba2;
195 a = a2;
196
197 if (repeat && j == 0) _adjustAAMargin(iAABegin, i - 1);
198 }
199 rgba = ALPHA_BLEND((rgba | 0xff000000), a);
200
201 for (; i < GRADIENT_STOP_SIZE; ++i)
202 fill->ctable[i] = rgba;
203
204 //For repeat fill spread apply anti-aliasing between the last and first colors,
205 //othewise make sure the last color stop is represented at the end of the table.
206 if (repeat) _applyAA(fill, iAABegin, iAAEnd);
207 else fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
208
209 return true;
210 }
211
212
_prepareLinear(SwFill * fill,const LinearGradient * linear,const Matrix & transform)213 bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& transform)
214 {
215 float x1, x2, y1, y2;
216 if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
217
218 fill->linear.dx = x2 - x1;
219 fill->linear.dy = y2 - y1;
220 auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
221
222 if (len < FLOAT_EPSILON) {
223 if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) {
224 fill->solid = true;
225 }
226 return true;
227 }
228
229 fill->linear.dx /= len;
230 fill->linear.dy /= len;
231 fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
232
233 auto gradTransform = linear->transform();
234 bool isTransformation = !identity((const Matrix*)(&gradTransform));
235
236 if (isTransformation) {
237 gradTransform = transform * gradTransform;
238 } else {
239 gradTransform = transform;
240 isTransformation = true;
241 }
242
243 if (isTransformation) {
244 Matrix invTransform;
245 if (!inverse(&gradTransform, &invTransform)) return false;
246
247 fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
248
249 auto dx = fill->linear.dx;
250 fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
251 fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
252 }
253
254 return true;
255 }
256
257
_prepareRadial(SwFill * fill,const RadialGradient * radial,const Matrix & transform)258 bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& transform)
259 {
260 auto cx = P(radial)->cx;
261 auto cy = P(radial)->cy;
262 auto r = P(radial)->r;
263 auto fx = P(radial)->fx;
264 auto fy = P(radial)->fy;
265 auto fr = P(radial)->fr;
266
267 if (tvg::zero(r)) {
268 fill->solid = true;
269 return true;
270 }
271
272 fill->radial.dr = r - fr;
273 fill->radial.dx = cx - fx;
274 fill->radial.dy = cy - fy;
275 fill->radial.fr = fr;
276 fill->radial.fx = fx;
277 fill->radial.fy = fy;
278 fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
279
280 //This condition fulfills the SVG 1.1 std:
281 //the focal point, if outside the end circle, is moved to be on the end circle
282 //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
283 if (fill->radial.a < 0) {
284 auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
285 fill->radial.fx = cx + r * (fx - cx) / dist;
286 fill->radial.fy = cy + r * (fy - cy) / dist;
287 fill->radial.dx = cx - fill->radial.fx;
288 fill->radial.dy = cy - fill->radial.fy;
289 // Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA
290 // https://github.com/thorvg/thorvg/issues/2014
291 auto dr2 = fill->radial.dr * fill->radial.dr;
292 auto dx2 = fill->radial.dx * fill->radial.dx;
293 auto dy2 = fill->radial.dy * fill->radial.dy;
294
295 fill->radial.a = dr2 - dx2 - dy2;
296 }
297
298 if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
299
300 auto gradTransform = radial->transform();
301 bool isTransformation = !identity((const Matrix*)(&gradTransform));
302
303 if (isTransformation) gradTransform = transform * gradTransform;
304 else {
305 gradTransform = transform;
306 isTransformation = true;
307 }
308
309 if (isTransformation) {
310 Matrix invTransform;
311 if (!inverse(&gradTransform, &invTransform)) return false;
312 fill->radial.a11 = invTransform.e11;
313 fill->radial.a12 = invTransform.e12;
314 fill->radial.a13 = invTransform.e13;
315 fill->radial.a21 = invTransform.e21;
316 fill->radial.a22 = invTransform.e22;
317 fill->radial.a23 = invTransform.e23;
318 } else {
319 fill->radial.a11 = fill->radial.a22 = 1.0f;
320 fill->radial.a12 = fill->radial.a13 = 0.0f;
321 fill->radial.a21 = fill->radial.a23 = 0.0f;
322 }
323 return true;
324 }
325
326
_clamp(const SwFill * fill,int32_t pos)327 static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
328 {
329 switch (fill->spread) {
330 case FillSpread::Pad: {
331 if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
332 else if (pos < 0) pos = 0;
333 break;
334 }
335 case FillSpread::Repeat: {
336 pos = pos % GRADIENT_STOP_SIZE;
337 if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
338 break;
339 }
340 case FillSpread::Reflect: {
341 auto limit = GRADIENT_STOP_SIZE * 2;
342 pos = pos % limit;
343 if (pos < 0) pos = limit + pos;
344 if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
345 break;
346 }
347 }
348 return pos;
349 }
350
351
_fixedPixel(const SwFill * fill,int32_t pos)352 static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
353 {
354 int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
355 return fill->ctable[_clamp(fill, i)];
356 }
357
358
_pixel(const SwFill * fill,float pos)359 static inline uint32_t _pixel(const SwFill* fill, float pos)
360 {
361 auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
362 return fill->ctable[_clamp(fill, i)];
363 }
364
365
366 /************************************************************************/
367 /* External Class Implementation */
368 /************************************************************************/
369
370
fillRadial(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,uint8_t * cmp,SwAlpha alpha,uint8_t csize,uint8_t opacity)371 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
372 {
373 //edge case
374 if (fill->radial.a < RADIAL_A_THRESHOLD) {
375 auto radial = &fill->radial;
376 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
377 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
378
379 if (opacity == 255) {
380 for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
381 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
382 *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
383 rx += radial->a11;
384 ry += radial->a21;
385 }
386 } else {
387 for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
388 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
389 *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
390 rx += radial->a11;
391 ry += radial->a21;
392 }
393 }
394 } else {
395 float b, deltaB, det, deltaDet, deltaDeltaDet;
396 _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
397
398 if (opacity == 255) {
399 for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
400 *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
401 det += deltaDet;
402 deltaDet += deltaDeltaDet;
403 b += deltaB;
404 }
405 } else {
406 for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
407 *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
408 det += deltaDet;
409 deltaDet += deltaDeltaDet;
410 b += deltaB;
411 }
412 }
413 }
414 }
415
416
fillRadial(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,SwBlender op,uint8_t a)417 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
418 {
419 if (fill->radial.a < RADIAL_A_THRESHOLD) {
420 auto radial = &fill->radial;
421 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
422 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
423 for (uint32_t i = 0; i < len; ++i, ++dst) {
424 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
425 *dst = op(_pixel(fill, x0), *dst, a);
426 rx += radial->a11;
427 ry += radial->a21;
428 }
429 } else {
430 float b, deltaB, det, deltaDet, deltaDeltaDet;
431 _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
432
433 for (uint32_t i = 0; i < len; ++i, ++dst) {
434 *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
435 det += deltaDet;
436 deltaDet += deltaDeltaDet;
437 b += deltaB;
438 }
439 }
440 }
441
442
fillRadial(const SwFill * fill,uint8_t * dst,uint32_t y,uint32_t x,uint32_t len,SwMask maskOp,uint8_t a)443 void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
444 {
445 if (fill->radial.a < RADIAL_A_THRESHOLD) {
446 auto radial = &fill->radial;
447 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
448 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
449 for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
450 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
451 auto src = MULTIPLY(a, A(_pixel(fill, x0)));
452 *dst = maskOp(src, *dst, ~src);
453 rx += radial->a11;
454 ry += radial->a21;
455 }
456 } else {
457 float b, deltaB, det, deltaDet, deltaDeltaDet;
458 _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
459
460 for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
461 auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
462 *dst = maskOp(src, *dst, ~src);
463 det += deltaDet;
464 deltaDet += deltaDeltaDet;
465 b += deltaB;
466 }
467 }
468 }
469
470
fillRadial(const SwFill * fill,uint8_t * dst,uint32_t y,uint32_t x,uint32_t len,uint8_t * cmp,SwMask maskOp,uint8_t a)471 void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
472 {
473 if (fill->radial.a < RADIAL_A_THRESHOLD) {
474 auto radial = &fill->radial;
475 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
476 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
477 for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
478 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
479 auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
480 auto tmp = maskOp(src, *cmp, 0);
481 *dst = tmp + MULTIPLY(*dst, ~tmp);
482 rx += radial->a11;
483 ry += radial->a21;
484 }
485 } else {
486 float b, deltaB, det, deltaDet, deltaDeltaDet;
487 _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
488
489 for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
490 auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
491 auto tmp = maskOp(src, *cmp, 0);
492 *dst = tmp + MULTIPLY(*dst, ~tmp);
493 deltaDet += deltaDeltaDet;
494 b += deltaB;
495 }
496 }
497 }
498
499
fillRadial(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,SwBlender op,SwBlender op2,uint8_t a)500 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
501 {
502 if (fill->radial.a < RADIAL_A_THRESHOLD) {
503 auto radial = &fill->radial;
504 auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
505 auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
506
507 if (a == 255) {
508 for (uint32_t i = 0; i < len; ++i, ++dst) {
509 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
510 auto tmp = op(_pixel(fill, x0), *dst, 255);
511 *dst = op2(tmp, *dst, 255);
512 rx += radial->a11;
513 ry += radial->a21;
514 }
515 } else {
516 for (uint32_t i = 0; i < len; ++i, ++dst) {
517 auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
518 auto tmp = op(_pixel(fill, x0), *dst, 255);
519 auto tmp2 = op2(tmp, *dst, 255);
520 *dst = INTERPOLATE(tmp2, *dst, a);
521 rx += radial->a11;
522 ry += radial->a21;
523 }
524 }
525 } else {
526 float b, deltaB, det, deltaDet, deltaDeltaDet;
527 _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
528 if (a == 255) {
529 for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
530 auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
531 *dst = op2(tmp, *dst, 255);
532 det += deltaDet;
533 deltaDet += deltaDeltaDet;
534 b += deltaB;
535 }
536 } else {
537 for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
538 auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
539 auto tmp2 = op2(tmp, *dst, 255);
540 *dst = INTERPOLATE(tmp2, *dst, a);
541 det += deltaDet;
542 deltaDet += deltaDeltaDet;
543 b += deltaB;
544 }
545 }
546 }
547 }
548
549
fillLinear(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,uint8_t * cmp,SwAlpha alpha,uint8_t csize,uint8_t opacity)550 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
551 {
552 //Rotation
553 float rx = x + 0.5f;
554 float ry = y + 0.5f;
555 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
556 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
557
558 if (opacity == 255) {
559 if (tvg::zero(inc)) {
560 auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
561 for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
562 *dst = opBlendNormal(color, *dst, alpha(cmp));
563 }
564 return;
565 }
566
567 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
568 auto vMin = -vMax;
569 auto v = t + (inc * len);
570
571 //we can use fixed point math
572 if (v < vMax && v > vMin) {
573 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
574 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
575 for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) {
576 *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp));
577 t2 += inc2;
578 }
579 //we have to fallback to float math
580 } else {
581 uint32_t counter = 0;
582 while (counter++ < len) {
583 *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp));
584 ++dst;
585 t += inc;
586 cmp += csize;
587 }
588 }
589 } else {
590 if (tvg::zero(inc)) {
591 auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
592 for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
593 *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
594 }
595 return;
596 }
597
598 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
599 auto vMin = -vMax;
600 auto v = t + (inc * len);
601
602 //we can use fixed point math
603 if (v < vMax && v > vMin) {
604 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
605 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
606 for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) {
607 *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity));
608 t2 += inc2;
609 }
610 //we have to fallback to float math
611 } else {
612 uint32_t counter = 0;
613 while (counter++ < len) {
614 *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp)));
615 ++dst;
616 t += inc;
617 cmp += csize;
618 }
619 }
620 }
621 }
622
623
fillLinear(const SwFill * fill,uint8_t * dst,uint32_t y,uint32_t x,uint32_t len,SwMask maskOp,uint8_t a)624 void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
625 {
626 //Rotation
627 float rx = x + 0.5f;
628 float ry = y + 0.5f;
629 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
630 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
631
632 if (tvg::zero(inc)) {
633 auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
634 for (uint32_t i = 0; i < len; ++i, ++dst) {
635 *dst = maskOp(src, *dst, ~src);
636 }
637 return;
638 }
639
640 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
641 auto vMin = -vMax;
642 auto v = t + (inc * len);
643
644 //we can use fixed point math
645 if (v < vMax && v > vMin) {
646 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
647 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
648 for (uint32_t j = 0; j < len; ++j, ++dst) {
649 auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a);
650 *dst = maskOp(src, *dst, ~src);
651 t2 += inc2;
652 }
653 //we have to fallback to float math
654 } else {
655 uint32_t counter = 0;
656 while (counter++ < len) {
657 auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
658 *dst = maskOp(src, *dst, ~src);
659 ++dst;
660 t += inc;
661 }
662 }
663 }
664
665
fillLinear(const SwFill * fill,uint8_t * dst,uint32_t y,uint32_t x,uint32_t len,uint8_t * cmp,SwMask maskOp,uint8_t a)666 void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
667 {
668 //Rotation
669 float rx = x + 0.5f;
670 float ry = y + 0.5f;
671 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
672 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
673
674 if (tvg::zero(inc)) {
675 auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
676 src = MULTIPLY(src, a);
677 for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
678 auto tmp = maskOp(src, *cmp, 0);
679 *dst = tmp + MULTIPLY(*dst, ~tmp);
680 }
681 return;
682 }
683
684 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
685 auto vMin = -vMax;
686 auto v = t + (inc * len);
687
688 //we can use fixed point math
689 if (v < vMax && v > vMin) {
690 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
691 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
692 for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) {
693 auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
694 auto tmp = maskOp(src, *cmp, 0);
695 *dst = tmp + MULTIPLY(*dst, ~tmp);
696 t2 += inc2;
697 }
698 //we have to fallback to float math
699 } else {
700 uint32_t counter = 0;
701 while (counter++ < len) {
702 auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
703 auto tmp = maskOp(src, *cmp, 0);
704 *dst = tmp + MULTIPLY(*dst, ~tmp);
705 ++dst;
706 ++cmp;
707 t += inc;
708 }
709 }
710 }
711
712
fillLinear(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,SwBlender op,uint8_t a)713 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
714 {
715 //Rotation
716 float rx = x + 0.5f;
717 float ry = y + 0.5f;
718 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
719 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
720
721 if (tvg::zero(inc)) {
722 auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
723 for (uint32_t i = 0; i < len; ++i, ++dst) {
724 *dst = op(color, *dst, a);
725 }
726 return;
727 }
728
729 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
730 auto vMin = -vMax;
731 auto v = t + (inc * len);
732
733 //we can use fixed point math
734 if (v < vMax && v > vMin) {
735 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
736 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
737 for (uint32_t j = 0; j < len; ++j, ++dst) {
738 *dst = op(_fixedPixel(fill, t2), *dst, a);
739 t2 += inc2;
740 }
741 //we have to fallback to float math
742 } else {
743 uint32_t counter = 0;
744 while (counter++ < len) {
745 *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a);
746 ++dst;
747 t += inc;
748 }
749 }
750 }
751
752
fillLinear(const SwFill * fill,uint32_t * dst,uint32_t y,uint32_t x,uint32_t len,SwBlender op,SwBlender op2,uint8_t a)753 void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
754 {
755 //Rotation
756 float rx = x + 0.5f;
757 float ry = y + 0.5f;
758 float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
759 float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
760
761 if (tvg::zero(inc)) {
762 auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
763 if (a == 255) {
764 for (uint32_t i = 0; i < len; ++i, ++dst) {
765 auto tmp = op(color, *dst, a);
766 *dst = op2(tmp, *dst, 255);
767 }
768 } else {
769 for (uint32_t i = 0; i < len; ++i, ++dst) {
770 auto tmp = op(color, *dst, a);
771 auto tmp2 = op2(tmp, *dst, 255);
772 *dst = INTERPOLATE(tmp2, *dst, a);
773 }
774 }
775 return;
776 }
777
778 auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
779 auto vMin = -vMax;
780 auto v = t + (inc * len);
781
782 if (a == 255) {
783 //we can use fixed point math
784 if (v < vMax && v > vMin) {
785 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
786 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
787 for (uint32_t j = 0; j < len; ++j, ++dst) {
788 auto tmp = op(_fixedPixel(fill, t2), *dst, 255);
789 *dst = op2(tmp, *dst, 255);
790 t2 += inc2;
791 }
792 //we have to fallback to float math
793 } else {
794 uint32_t counter = 0;
795 while (counter++ < len) {
796 auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
797 *dst = op2(tmp, *dst, 255);
798 ++dst;
799 t += inc;
800 }
801 }
802 } else {
803 //we can use fixed point math
804 if (v < vMax && v > vMin) {
805 auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
806 auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
807 for (uint32_t j = 0; j < len; ++j, ++dst) {
808 auto tmp = op(_fixedPixel(fill, t2), *dst, 255);
809 auto tmp2 = op2(tmp, *dst, 255);
810 *dst = INTERPOLATE(tmp2, *dst, a);
811 t2 += inc2;
812 }
813 //we have to fallback to float math
814 } else {
815 uint32_t counter = 0;
816 while (counter++ < len) {
817 auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
818 auto tmp2 = op2(tmp, *dst, 255);
819 *dst = INTERPOLATE(tmp2, *dst, a);
820 ++dst;
821 t += inc;
822 }
823 }
824 }
825 }
826
827
fillGenColorTable(SwFill * fill,const Fill * fdata,const Matrix & transform,SwSurface * surface,uint8_t opacity,bool ctable)828 bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
829 {
830 if (!fill) return false;
831
832 fill->spread = fdata->spread();
833
834 if (fdata->type() == Type::LinearGradient) {
835 if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
836 } else if (fdata->type() == Type::RadialGradient) {
837 if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
838 }
839
840 if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
841 return true;
842 }
843
844
fillFetchSolid(const SwFill * fill,const Fill * fdata)845 const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
846 {
847 if (!fill->solid) return nullptr;
848
849 const Fill::ColorStop* colors;
850 auto cnt = fdata->colorStops(&colors);
851 if (cnt == 0 || !colors) return nullptr;
852
853 return colors + cnt - 1;
854 }
855
856
fillReset(SwFill * fill)857 void fillReset(SwFill* fill)
858 {
859 if (fill->ctable) {
860 free(fill->ctable);
861 fill->ctable = nullptr;
862 }
863 fill->translucent = false;
864 fill->solid = false;
865 }
866
867
fillFree(SwFill * fill)868 void fillFree(SwFill* fill)
869 {
870 if (!fill) return;
871
872 if (fill->ctable) free(fill->ctable);
873
874 free(fill);
875 }
876
877 #endif /* LV_USE_THORVG_INTERNAL */
878
879