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