1 | /* |
2 | * Copyright (c) 2020 - 2023 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 "tvgMath.h" |
24 | #include "tvgSwCommon.h" |
25 | |
26 | |
27 | /************************************************************************/ |
28 | /* Internal Class Implementation */ |
29 | /************************************************************************/ |
30 | |
31 | #define GRADIENT_STOP_SIZE 1024 |
32 | #define FIXPT_BITS 8 |
33 | #define FIXPT_SIZE (1<<FIXPT_BITS) |
34 | |
35 | |
36 | static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity) |
37 | { |
38 | if (!fill->ctable) { |
39 | fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); |
40 | if (!fill->ctable) return false; |
41 | } |
42 | |
43 | const Fill::ColorStop* colors; |
44 | auto cnt = fdata->colorStops(&colors); |
45 | if (cnt == 0 || !colors) return false; |
46 | |
47 | auto pColors = colors; |
48 | |
49 | auto a = MULTIPLY(pColors->a, opacity); |
50 | if (a < 255) fill->translucent = true; |
51 | |
52 | auto r = pColors->r; |
53 | auto g = pColors->g; |
54 | auto b = pColors->b; |
55 | auto rgba = surface->join(r, g, b, a); |
56 | |
57 | auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE); |
58 | auto pos = 1.5f * inc; |
59 | uint32_t i = 0; |
60 | |
61 | fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a); |
62 | |
63 | while (pos <= pColors->offset) { |
64 | fill->ctable[i] = fill->ctable[i - 1]; |
65 | ++i; |
66 | pos += inc; |
67 | } |
68 | |
69 | for (uint32_t j = 0; j < cnt - 1; ++j) { |
70 | auto curr = colors + j; |
71 | auto next = curr + 1; |
72 | auto delta = 1.0f / (next->offset - curr->offset); |
73 | auto a2 = MULTIPLY(next->a, opacity); |
74 | if (!fill->translucent && a2 < 255) fill->translucent = true; |
75 | |
76 | auto rgba2 = surface->join(next->r, next->g, next->b, a2); |
77 | |
78 | while (pos < next->offset && i < GRADIENT_STOP_SIZE) { |
79 | auto t = (pos - curr->offset) * delta; |
80 | auto dist = static_cast<int32_t>(255 * t); |
81 | auto dist2 = 255 - dist; |
82 | |
83 | auto color = INTERPOLATE(rgba, rgba2, dist2); |
84 | fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); |
85 | |
86 | ++i; |
87 | pos += inc; |
88 | } |
89 | rgba = rgba2; |
90 | a = a2; |
91 | } |
92 | rgba = ALPHA_BLEND((rgba | 0xff000000), a); |
93 | |
94 | for (; i < GRADIENT_STOP_SIZE; ++i) |
95 | fill->ctable[i] = rgba; |
96 | |
97 | //Make sure the last color stop is represented at the end of the table |
98 | fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba; |
99 | |
100 | return true; |
101 | } |
102 | |
103 | |
104 | bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform) |
105 | { |
106 | float x1, x2, y1, y2; |
107 | if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; |
108 | |
109 | fill->linear.dx = x2 - x1; |
110 | fill->linear.dy = y2 - y1; |
111 | fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; |
112 | |
113 | if (fill->linear.len < FLT_EPSILON) return true; |
114 | |
115 | fill->linear.dx /= fill->linear.len; |
116 | fill->linear.dy /= fill->linear.len; |
117 | fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; |
118 | |
119 | auto gradTransform = linear->transform(); |
120 | bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); |
121 | |
122 | if (isTransformation) { |
123 | if (transform) gradTransform = mathMultiply(transform, &gradTransform); |
124 | } else if (transform) { |
125 | gradTransform = *transform; |
126 | isTransformation = true; |
127 | } |
128 | |
129 | if (isTransformation) { |
130 | Matrix invTransform; |
131 | if (!mathInverse(&gradTransform, &invTransform)) return false; |
132 | |
133 | fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23; |
134 | |
135 | auto dx = fill->linear.dx; |
136 | fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21; |
137 | fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22; |
138 | |
139 | fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; |
140 | if (fill->linear.len < FLT_EPSILON) return true; |
141 | } |
142 | |
143 | return true; |
144 | } |
145 | |
146 | |
147 | bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform) |
148 | { |
149 | float radius, cx, cy; |
150 | if (radial->radial(&cx, &cy, &radius) != Result::Success) return false; |
151 | if (radius < FLT_EPSILON) return true; |
152 | |
153 | float invR = 1.0f / radius; |
154 | fill->radial.shiftX = -cx; |
155 | fill->radial.shiftY = -cy; |
156 | fill->radial.a = radius; |
157 | |
158 | auto gradTransform = radial->transform(); |
159 | bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); |
160 | |
161 | if (isTransformation) { |
162 | if (transform) gradTransform = mathMultiply(transform, &gradTransform); |
163 | } else if (transform) { |
164 | gradTransform = *transform; |
165 | isTransformation = true; |
166 | } |
167 | |
168 | if (isTransformation) { |
169 | Matrix invTransform; |
170 | if (!mathInverse(&gradTransform, &invTransform)) return false; |
171 | |
172 | fill->radial.a11 = invTransform.e11 * invR; |
173 | fill->radial.a12 = invTransform.e12 * invR; |
174 | fill->radial.shiftX += invTransform.e13; |
175 | fill->radial.a21 = invTransform.e21 * invR; |
176 | fill->radial.a22 = invTransform.e22 * invR; |
177 | fill->radial.shiftY += invTransform.e23; |
178 | fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21; |
179 | |
180 | fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2)); |
181 | } else { |
182 | fill->radial.a11 = fill->radial.a22 = invR; |
183 | fill->radial.a12 = fill->radial.a21 = 0.0f; |
184 | fill->radial.detSecDeriv = 2.0f * invR * invR; |
185 | } |
186 | fill->radial.shiftX *= invR; |
187 | fill->radial.shiftY *= invR; |
188 | |
189 | return true; |
190 | } |
191 | |
192 | |
193 | static inline uint32_t _clamp(const SwFill* fill, int32_t pos) |
194 | { |
195 | switch (fill->spread) { |
196 | case FillSpread::Pad: { |
197 | if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1; |
198 | else if (pos < 0) pos = 0; |
199 | break; |
200 | } |
201 | case FillSpread::Repeat: { |
202 | pos = pos % GRADIENT_STOP_SIZE; |
203 | if (pos < 0) pos = GRADIENT_STOP_SIZE + pos; |
204 | break; |
205 | } |
206 | case FillSpread::Reflect: { |
207 | auto limit = GRADIENT_STOP_SIZE * 2; |
208 | pos = pos % limit; |
209 | if (pos < 0) pos = limit + pos; |
210 | if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1); |
211 | break; |
212 | } |
213 | } |
214 | return pos; |
215 | } |
216 | |
217 | |
218 | static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos) |
219 | { |
220 | int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; |
221 | return fill->ctable[_clamp(fill, i)]; |
222 | } |
223 | |
224 | |
225 | static inline uint32_t _pixel(const SwFill* fill, float pos) |
226 | { |
227 | auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f); |
228 | return fill->ctable[_clamp(fill, i)]; |
229 | } |
230 | |
231 | |
232 | /************************************************************************/ |
233 | /* External Class Implementation */ |
234 | /************************************************************************/ |
235 | |
236 | 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) |
237 | { |
238 | auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; |
239 | auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; |
240 | |
241 | // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx |
242 | auto detSecondDerivative = fill->radial.detSecDeriv; |
243 | // detFirstDerivative = d(det)/dx |
244 | auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; |
245 | auto det = rx * rx + ry * ry; |
246 | |
247 | if (opacity == 255) { |
248 | for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { |
249 | *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp)); |
250 | det += detFirstDerivative; |
251 | detFirstDerivative += detSecondDerivative; |
252 | } |
253 | } else { |
254 | for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { |
255 | *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp))); |
256 | det += detFirstDerivative; |
257 | detFirstDerivative += detSecondDerivative; |
258 | } |
259 | } |
260 | } |
261 | |
262 | |
263 | void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) |
264 | { |
265 | auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; |
266 | auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; |
267 | |
268 | // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx |
269 | auto detSecondDerivative = fill->radial.detSecDeriv; |
270 | // detFirstDerivative = d(det)/dx |
271 | auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; |
272 | auto det = rx * rx + ry * ry; |
273 | |
274 | for (uint32_t i = 0 ; i < len ; ++i, ++dst) { |
275 | *dst = op(_pixel(fill, sqrtf(det)), *dst, a); |
276 | det += detFirstDerivative; |
277 | detFirstDerivative += detSecondDerivative; |
278 | } |
279 | } |
280 | |
281 | |
282 | void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) |
283 | { |
284 | auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; |
285 | auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; |
286 | |
287 | // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx |
288 | auto detSecondDerivative = fill->radial.detSecDeriv; |
289 | // detFirstDerivative = d(det)/dx |
290 | auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; |
291 | auto det = rx * rx + ry * ry; |
292 | |
293 | if (a == 255) { |
294 | for (uint32_t i = 0 ; i < len ; ++i, ++dst) { |
295 | auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255); |
296 | *dst = op2(tmp, *dst, 255); |
297 | det += detFirstDerivative; |
298 | detFirstDerivative += detSecondDerivative; |
299 | } |
300 | } else { |
301 | for (uint32_t i = 0 ; i < len ; ++i, ++dst) { |
302 | auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255); |
303 | auto tmp2 = op2(tmp, *dst, 255); |
304 | *dst = INTERPOLATE(tmp2, *dst, a); |
305 | det += detFirstDerivative; |
306 | detFirstDerivative += detSecondDerivative; |
307 | } |
308 | } |
309 | } |
310 | |
311 | |
312 | 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) |
313 | { |
314 | //Rotation |
315 | float rx = x + 0.5f; |
316 | float ry = y + 0.5f; |
317 | float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); |
318 | float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); |
319 | |
320 | if (opacity == 255) { |
321 | if (mathZero(inc)) { |
322 | auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)); |
323 | for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { |
324 | *dst = opBlendNormal(color, *dst, alpha(cmp)); |
325 | } |
326 | return; |
327 | } |
328 | |
329 | auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1)); |
330 | auto vMin = -vMax; |
331 | auto v = t + (inc * len); |
332 | |
333 | //we can use fixed point math |
334 | if (v < vMax && v > vMin) { |
335 | auto t2 = static_cast<int32_t>(t * FIXPT_SIZE); |
336 | auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE); |
337 | for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { |
338 | *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp)); |
339 | t2 += inc2; |
340 | } |
341 | //we have to fallback to float math |
342 | } else { |
343 | uint32_t counter = 0; |
344 | while (counter++ < len) { |
345 | *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp)); |
346 | ++dst; |
347 | t += inc; |
348 | cmp += csize; |
349 | } |
350 | } |
351 | } else { |
352 | if (mathZero(inc)) { |
353 | auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)); |
354 | for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { |
355 | *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); |
356 | } |
357 | return; |
358 | } |
359 | |
360 | auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1)); |
361 | auto vMin = -vMax; |
362 | auto v = t + (inc * len); |
363 | |
364 | //we can use fixed point math |
365 | if (v < vMax && v > vMin) { |
366 | auto t2 = static_cast<int32_t>(t * FIXPT_SIZE); |
367 | auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE); |
368 | for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { |
369 | *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity)); |
370 | t2 += inc2; |
371 | } |
372 | //we have to fallback to float math |
373 | } else { |
374 | uint32_t counter = 0; |
375 | while (counter++ < len) { |
376 | *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp))); |
377 | ++dst; |
378 | t += inc; |
379 | cmp += csize; |
380 | } |
381 | } |
382 | } |
383 | } |
384 | |
385 | |
386 | void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) |
387 | { |
388 | //Rotation |
389 | float rx = x + 0.5f; |
390 | float ry = y + 0.5f; |
391 | float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); |
392 | float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); |
393 | |
394 | if (mathZero(inc)) { |
395 | auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)); |
396 | for (uint32_t i = 0; i < len; ++i, ++dst) { |
397 | *dst = op(color, *dst, a); |
398 | } |
399 | return; |
400 | } |
401 | |
402 | auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1)); |
403 | auto vMin = -vMax; |
404 | auto v = t + (inc * len); |
405 | |
406 | //we can use fixed point math |
407 | if (v < vMax && v > vMin) { |
408 | auto t2 = static_cast<int32_t>(t * FIXPT_SIZE); |
409 | auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE); |
410 | for (uint32_t j = 0; j < len; ++j, ++dst) { |
411 | *dst = op(_fixedPixel(fill, t2), *dst, a); |
412 | t2 += inc2; |
413 | } |
414 | //we have to fallback to float math |
415 | } else { |
416 | uint32_t counter = 0; |
417 | while (counter++ < len) { |
418 | *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a); |
419 | ++dst; |
420 | t += inc; |
421 | } |
422 | } |
423 | } |
424 | |
425 | |
426 | void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) |
427 | { |
428 | //Rotation |
429 | float rx = x + 0.5f; |
430 | float ry = y + 0.5f; |
431 | float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); |
432 | float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); |
433 | |
434 | if (mathZero(inc)) { |
435 | auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)); |
436 | if (a == 255) { |
437 | for (uint32_t i = 0; i < len; ++i, ++dst) { |
438 | auto tmp = op(color, *dst, a); |
439 | *dst = op2(tmp, *dst, 255); |
440 | } |
441 | } else { |
442 | for (uint32_t i = 0; i < len; ++i, ++dst) { |
443 | auto tmp = op(color, *dst, a); |
444 | auto tmp2 = op2(tmp, *dst, 255); |
445 | *dst = INTERPOLATE(tmp2, *dst, a); |
446 | } |
447 | } |
448 | return; |
449 | } |
450 | |
451 | auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1)); |
452 | auto vMin = -vMax; |
453 | auto v = t + (inc * len); |
454 | |
455 | if (a == 255) { |
456 | //we can use fixed point math |
457 | if (v < vMax && v > vMin) { |
458 | auto t2 = static_cast<int32_t>(t * FIXPT_SIZE); |
459 | auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE); |
460 | for (uint32_t j = 0; j < len; ++j, ++dst) { |
461 | auto tmp = op(_fixedPixel(fill, t2), *dst, 255); |
462 | *dst = op2(tmp, *dst, 255); |
463 | t2 += inc2; |
464 | } |
465 | //we have to fallback to float math |
466 | } else { |
467 | uint32_t counter = 0; |
468 | while (counter++ < len) { |
469 | auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); |
470 | *dst = op2(tmp, *dst, 255); |
471 | ++dst; |
472 | t += inc; |
473 | } |
474 | } |
475 | } else { |
476 | //we can use fixed point math |
477 | if (v < vMax && v > vMin) { |
478 | auto t2 = static_cast<int32_t>(t * FIXPT_SIZE); |
479 | auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE); |
480 | for (uint32_t j = 0; j < len; ++j, ++dst) { |
481 | auto tmp = op(_fixedPixel(fill, t2), *dst, 255); |
482 | auto tmp2 = op2(tmp, *dst, 255); |
483 | *dst = INTERPOLATE(tmp2, *dst, a); |
484 | t2 += inc2; |
485 | } |
486 | //we have to fallback to float math |
487 | } else { |
488 | uint32_t counter = 0; |
489 | while (counter++ < len) { |
490 | auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); |
491 | auto tmp2 = op2(tmp, *dst, 255); |
492 | *dst = INTERPOLATE(tmp2, *dst, a); |
493 | ++dst; |
494 | t += inc; |
495 | } |
496 | } |
497 | } |
498 | } |
499 | |
500 | |
501 | bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) |
502 | { |
503 | if (!fill) return false; |
504 | |
505 | fill->spread = fdata->spread(); |
506 | |
507 | if (ctable) { |
508 | if (!_updateColorTable(fill, fdata, surface, opacity)) return false; |
509 | } |
510 | |
511 | if (fdata->identifier() == TVG_CLASS_ID_LINEAR) { |
512 | return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform); |
513 | } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { |
514 | return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform); |
515 | } |
516 | |
517 | //LOG: What type of gradient?! |
518 | |
519 | return false; |
520 | } |
521 | |
522 | |
523 | void fillReset(SwFill* fill) |
524 | { |
525 | if (fill->ctable) { |
526 | free(fill->ctable); |
527 | fill->ctable = nullptr; |
528 | } |
529 | fill->translucent = false; |
530 | } |
531 | |
532 | |
533 | void fillFree(SwFill* fill) |
534 | { |
535 | if (!fill) return; |
536 | |
537 | if (fill->ctable) free(fill->ctable); |
538 | |
539 | free(fill); |
540 | } |
541 | |