57b7cd65964cb65bc9eb33d5840dfbd70f5e380c
[platform/core/uifw/lottie-player.git] / src / vector / vdrawhelper.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. 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 "vdrawhelper.h"
24 #include <algorithm>
25 #include <climits>
26 #include <cstring>
27 #include <mutex>
28 #include <unordered_map>
29 #include <array>
30
31 static RenderFuncTable RenderTable;
32
33 void VTextureData::setClip(const VRect &clip)
34 {
35     left = clip.left();
36     top = clip.top();
37     right = std::min(clip.right(), int(width())) - 1;
38     bottom = std::min(clip.bottom(), int(height())) - 1;
39 }
40
41 class VGradientCache {
42 public:
43     struct CacheInfo : public VColorTable {
44         inline CacheInfo(VGradientStops s) : stops(std::move(s)) {}
45         VGradientStops stops;
46     };
47     using VCacheData = std::shared_ptr<const CacheInfo>;
48     using VCacheKey = int64_t;
49     using VGradientColorTableHash =
50         std::unordered_multimap<VCacheKey, VCacheData>;
51
52     bool generateGradientColorTable(const VGradientStops &stops, float alpha,
53                                     uint32_t *colorTable, int size);
54     VCacheData getBuffer(const VGradient &gradient)
55     {
56         VCacheKey             hash_val = 0;
57         VCacheData            info;
58         const VGradientStops &stops = gradient.mStops;
59         for (uint i = 0; i < stops.size() && i <= 2; i++)
60             hash_val +=
61                 VCacheKey(stops[i].second.premulARGB() * gradient.alpha());
62
63         {
64             std::lock_guard<std::mutex> guard(mMutex);
65
66             size_t count = mCache.count(hash_val);
67             if (!count) {
68                 // key is not present in the hash
69                 info = addCacheElement(hash_val, gradient);
70             } else if (count == 1) {
71                 auto search = mCache.find(hash_val);
72                 if (search->second->stops == stops) {
73                     info = search->second;
74                 } else {
75                     // didn't find an exact match
76                     info = addCacheElement(hash_val, gradient);
77                 }
78             } else {
79                 // we have a multiple data with same key
80                 auto range = mCache.equal_range(hash_val);
81                 for (auto it = range.first; it != range.second; ++it) {
82                     if (it->second->stops == stops) {
83                         info = it->second;
84                         break;
85                     }
86                 }
87                 if (!info) {
88                     // didn't find an exact match
89                     info = addCacheElement(hash_val, gradient);
90                 }
91             }
92         }
93         return info;
94     }
95
96     static VGradientCache &instance()
97     {
98         static VGradientCache CACHE;
99         return CACHE;
100     }
101
102 protected:
103     uint       maxCacheSize() const { return 60; }
104     VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient)
105     {
106         if (mCache.size() == maxCacheSize()) {
107             uint count = maxCacheSize() / 10;
108             while (count--) {
109                 mCache.erase(mCache.begin());
110             }
111         }
112         auto cache_entry = std::make_shared<CacheInfo>(gradient.mStops);
113         cache_entry->alpha = generateGradientColorTable(
114             gradient.mStops, gradient.alpha(), cache_entry->buffer32,
115             VGradient::colorTableSize);
116         mCache.insert(std::make_pair(hash_val, cache_entry));
117         return cache_entry;
118     }
119
120 private:
121     VGradientCache() = default;
122
123     VGradientColorTableHash mCache;
124     std::mutex              mMutex;
125 };
126
127 bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
128                                                 float                 opacity,
129                                                 uint32_t *colorTable, int size)
130 {
131     int                  dist, idist, pos = 0;
132     size_t               i;
133     bool                 alpha = false;
134     size_t               stopCount = stops.size();
135     const VGradientStop *curr, *next, *start;
136     uint32_t             curColor, nextColor;
137     float                delta, t, incr, fpos;
138
139     if (!vCompare(opacity, 1.0f)) alpha = true;
140
141     start = stops.data();
142     curr = start;
143     if (!curr->second.isOpaque()) alpha = true;
144     curColor = curr->second.premulARGB(opacity);
145     incr = 1.0f / (float)size;
146     fpos = 1.5f * incr;
147
148     colorTable[pos++] = curColor;
149
150     while (fpos <= curr->first) {
151         colorTable[pos] = colorTable[pos - 1];
152         pos++;
153         fpos += incr;
154     }
155
156     for (i = 0; i < stopCount - 1; ++i) {
157         curr = (start + i);
158         next = (start + i + 1);
159         delta = 1 / (next->first - curr->first);
160         if (!next->second.isOpaque()) alpha = true;
161         nextColor = next->second.premulARGB(opacity);
162         while (fpos < next->first && pos < size) {
163             t = (fpos - curr->first) * delta;
164             dist = (int)(255 * t);
165             idist = 255 - dist;
166             colorTable[pos] =
167                 interpolate_pixel(curColor, idist, nextColor, dist);
168             ++pos;
169             fpos += incr;
170         }
171         curColor = nextColor;
172     }
173
174     for (; pos < size; ++pos) colorTable[pos] = curColor;
175
176     // Make sure the last color stop is represented at the end of the table
177     colorTable[size - 1] = curColor;
178     return alpha;
179 }
180
181 void VRasterBuffer::clear()
182 {
183     memset(mBuffer, 0, mHeight * mBytesPerLine);
184 }
185
186 VBitmap::Format VRasterBuffer::prepare(const VBitmap *image)
187 {
188     mBuffer = image->data();
189     mWidth = image->width();
190     mHeight = image->height();
191     mBytesPerPixel = 4;
192     mBytesPerLine = image->stride();
193
194     mFormat = image->format();
195     return mFormat;
196 }
197
198 void VSpanData::init(VRasterBuffer *image)
199 {
200     mRasterBuffer = image;
201     setDrawRegion(VRect(0, 0, int(image->width()), int(image->height())));
202     mType = VSpanData::Type::None;
203     mBlendFunc = nullptr;
204     mUnclippedBlendFunc = nullptr;
205 }
206
207 /*
208  *  Gradient Draw routines
209  *
210  */
211
212 #define FIXPT_BITS 8
213 #define FIXPT_SIZE (1 << FIXPT_BITS)
214 static inline void getLinearGradientValues(LinearGradientValues *v,
215                                            const VSpanData *     data)
216 {
217     const VGradientData *grad = &data->mGradient;
218     v->dx = grad->linear.x2 - grad->linear.x1;
219     v->dy = grad->linear.y2 - grad->linear.y1;
220     v->l = v->dx * v->dx + v->dy * v->dy;
221     v->off = 0;
222     if (v->l != 0) {
223         v->dx /= v->l;
224         v->dy /= v->l;
225         v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
226     }
227 }
228
229 static inline void getRadialGradientValues(RadialGradientValues *v,
230                                            const VSpanData *     data)
231 {
232     const VGradientData &gradient = data->mGradient;
233     v->dx = gradient.radial.cx - gradient.radial.fx;
234     v->dy = gradient.radial.cy - gradient.radial.fy;
235
236     v->dr = gradient.radial.cradius - gradient.radial.fradius;
237     v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
238
239     v->a = v->dr * v->dr - v->dx * v->dx - v->dy * v->dy;
240     v->inv2a = 1 / (2 * v->a);
241
242     v->extended = !vIsZero(gradient.radial.fradius) || v->a <= 0;
243 }
244
245 static inline int gradientClamp(const VGradientData *grad, int ipos)
246 {
247     int limit;
248
249     if (grad->mSpread == VGradient::Spread::Repeat) {
250         ipos = ipos % VGradient::colorTableSize;
251         ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
252     } else if (grad->mSpread == VGradient::Spread::Reflect) {
253         limit = VGradient::colorTableSize * 2;
254         ipos = ipos % limit;
255         ipos = ipos < 0 ? limit + ipos : ipos;
256         ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
257     } else {
258         if (ipos < 0)
259             ipos = 0;
260         else if (ipos >= VGradient::colorTableSize)
261             ipos = VGradient::colorTableSize - 1;
262     }
263     return ipos;
264 }
265
266 static uint32_t gradientPixelFixed(const VGradientData *grad, int fixed_pos)
267 {
268     int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
269
270     return grad->mColorTable[gradientClamp(grad, ipos)];
271 }
272
273 static inline uint32_t gradientPixel(const VGradientData *grad, float pos)
274 {
275     int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
276
277     return grad->mColorTable[gradientClamp(grad, ipos)];
278 }
279
280 void fetch_linear_gradient(uint32_t *buffer, const Operator *op,
281                            const VSpanData *data, int y, int x, int length)
282 {
283     float                t, inc;
284     const VGradientData *gradient = &data->mGradient;
285
286     bool  affine = true;
287     float rx = 0, ry = 0;
288     if (op->linear.l == 0) {
289         t = inc = 0;
290     } else {
291         rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) +
292              data->dx;
293         ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) +
294              data->dy;
295         t = op->linear.dx * rx + op->linear.dy * ry + op->linear.off;
296         inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
297         affine = !data->m13 && !data->m23;
298
299         if (affine) {
300             t *= (VGradient::colorTableSize - 1);
301             inc *= (VGradient::colorTableSize - 1);
302         }
303     }
304
305     const uint32_t *end = buffer + length;
306     if (affine) {
307         if (inc > float(-1e-5) && inc < float(1e-5)) {
308             memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)),
309                       length);
310         } else {
311             if (t + inc * length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
312                 t + inc * length > float(INT_MIN >> (FIXPT_BITS + 1))) {
313                 // we can use fixed point math
314                 int t_fixed = int(t * FIXPT_SIZE);
315                 int inc_fixed = int(inc * FIXPT_SIZE);
316                 while (buffer < end) {
317                     *buffer = gradientPixelFixed(gradient, t_fixed);
318                     t_fixed += inc_fixed;
319                     ++buffer;
320                 }
321             } else {
322                 // we have to fall back to float math
323                 while (buffer < end) {
324                     *buffer =
325                         gradientPixel(gradient, t / VGradient::colorTableSize);
326                     t += inc;
327                     ++buffer;
328                 }
329             }
330         }
331     } else {  // fall back to float math here as well
332         float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) +
333                    data->m33;
334         while (buffer < end) {
335             float xt = rx / rw;
336             float yt = ry / rw;
337             t = (op->linear.dx * xt + op->linear.dy * yt) + op->linear.off;
338
339             *buffer = gradientPixel(gradient, t);
340             rx += data->m11;
341             ry += data->m12;
342             rw += data->m13;
343             if (!rw) {
344                 rw += data->m13;
345             }
346             ++buffer;
347         }
348     }
349 }
350
351 static inline float radialDeterminant(float a, float b, float c)
352 {
353     return (b * b) - (4 * a * c);
354 }
355
356 static void fetch(uint32_t *buffer, uint32_t *end, const Operator *op,
357                   const VSpanData *data, float det, float delta_det,
358                   float delta_delta_det, float b, float delta_b)
359 {
360     if (op->radial.extended) {
361         while (buffer < end) {
362             uint32_t result = 0;
363             if (det >= 0) {
364                 float w = std::sqrt(det) - b;
365                 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
366                     result = gradientPixel(&data->mGradient, w);
367             }
368
369             *buffer = result;
370
371             det += delta_det;
372             delta_det += delta_delta_det;
373             b += delta_b;
374
375             ++buffer;
376         }
377     } else {
378         while (buffer < end) {
379             *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
380
381             det += delta_det;
382             delta_det += delta_delta_det;
383             b += delta_b;
384         }
385     }
386 }
387
388 void fetch_radial_gradient(uint32_t *buffer, const Operator *op,
389                            const VSpanData *data, int y, int x, int length)
390 {
391     // avoid division by zero
392     if (vIsZero(op->radial.a)) {
393         memfill32(buffer, 0, length);
394         return;
395     }
396
397     float rx =
398         data->m21 * (y + float(0.5)) + data->dx + data->m11 * (x + float(0.5));
399     float ry =
400         data->m22 * (y + float(0.5)) + data->dy + data->m12 * (x + float(0.5));
401     bool affine = !data->m13 && !data->m23;
402
403     uint32_t *end = buffer + length;
404     if (affine) {
405         rx -= data->mGradient.radial.fx;
406         ry -= data->mGradient.radial.fy;
407
408         float inv_a = 1 / float(2 * op->radial.a);
409
410         const float delta_rx = data->m11;
411         const float delta_ry = data->m12;
412
413         float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
414                        rx * op->radial.dx + ry * op->radial.dy);
415         float delta_b =
416             2 * (delta_rx * op->radial.dx + delta_ry * op->radial.dy);
417         const float b_delta_b = 2 * b * delta_b;
418         const float delta_b_delta_b = 2 * delta_b * delta_b;
419
420         const float bb = b * b;
421         const float delta_bb = delta_b * delta_b;
422
423         b *= inv_a;
424         delta_b *= inv_a;
425
426         const float rxrxryry = rx * rx + ry * ry;
427         const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
428         const float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
429         const float delta_rx_plus_ry = 2 * delta_rxrxryry;
430
431         inv_a *= inv_a;
432
433         float det =
434             (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
435         float delta_det = (b_delta_b + delta_bb +
436                            4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) *
437                           inv_a;
438         const float delta_delta_det =
439             (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
440
441         fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b,
442               delta_b);
443     } else {
444         float rw = data->m23 * (y + float(0.5)) + data->m33 +
445                    data->m13 * (x + float(0.5));
446
447         while (buffer < end) {
448             if (rw == 0) {
449                 *buffer = 0;
450             } else {
451                 float invRw = 1 / rw;
452                 float gx = rx * invRw - data->mGradient.radial.fx;
453                 float gy = ry * invRw - data->mGradient.radial.fy;
454                 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
455                                gx * op->radial.dx + gy * op->radial.dy);
456                 float det = radialDeterminant(
457                     op->radial.a, b, op->radial.sqrfr - (gx * gx + gy * gy));
458
459                 uint32_t result = 0;
460                 if (det >= 0) {
461                     float detSqrt = std::sqrt(det);
462
463                     float s0 = (-b - detSqrt) * op->radial.inv2a;
464                     float s1 = (-b + detSqrt) * op->radial.inv2a;
465
466                     float s = vMax(s0, s1);
467
468                     if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
469                         result = gradientPixel(&data->mGradient, s);
470                 }
471
472                 *buffer = result;
473             }
474
475             rx += data->m11;
476             ry += data->m12;
477             rw += data->m13;
478
479             ++buffer;
480         }
481     }
482 }
483
484 static inline Operator getOperator(const VSpanData *data)
485 {
486     Operator op;
487     bool     solidSource = false;
488
489     switch (data->mType) {
490     case VSpanData::Type::Solid:
491         solidSource = (vAlpha(data->mSolid) == 255);
492         op.srcFetch = nullptr;
493         break;
494     case VSpanData::Type::LinearGradient:
495         solidSource = false;
496         getLinearGradientValues(&op.linear, data);
497         op.srcFetch = &fetch_linear_gradient;
498         break;
499     case VSpanData::Type::RadialGradient:
500         solidSource = false;
501         getRadialGradientValues(&op.radial, data);
502         op.srcFetch = &fetch_radial_gradient;
503         break;
504     default:
505         op.srcFetch = nullptr;
506         break;
507     }
508
509     op.mode = data->mBlendMode;
510     if (op.mode == BlendMode::SrcOver && solidSource) op.mode = BlendMode::Src;
511
512     op.funcSolid = RenderTable.color(op.mode);
513     op.func = RenderTable.src(op.mode);
514
515     return op;
516 }
517
518 static void blend_color(size_t size, const VRle::Span *array, void *userData)
519 {
520     VSpanData *data = (VSpanData *)(userData);
521     Operator   op = getOperator(data);
522     const uint color = data->mSolid;
523
524     for (size_t i = 0 ; i < size; ++i) {
525         const auto &span = array[i];
526         op.funcSolid(data->buffer(span.x, span.y), span.len, color, span.coverage);
527     }
528 }
529
530 // Signature of Process Object
531 //  void Pocess(uint* scratchBuffer, size_t x, size_t y, uchar cov)
532 template <class Process>
533 static inline void process_in_chunk(const VRle::Span *array, size_t size,
534                                     Process process)
535 {
536     std::array<uint, 2048> buf;
537     for (size_t i = 0; i < size; i++) {
538         const auto &span = array[i];
539         size_t      len = span.len;
540         size_t      x = span.x;
541         while (len) {
542             auto l = std::min(len, buf.size());
543             process(buf.data(), x, span.y, l, span.coverage);
544             x += l;
545             len -= l;
546         }
547     }
548 }
549
550 static void blend_gradient(size_t size, const VRle::Span *array,
551                            void *userData)
552 {
553     VSpanData *data = (VSpanData *)(userData);
554     Operator   op = getOperator(data);
555
556     if (!op.srcFetch) return;
557
558     process_in_chunk(
559         array, size,
560         [&](uint *scratch, size_t x, size_t y, size_t len, uchar cov) {
561             op.srcFetch(scratch, &op, data, y, x, len);
562             op.func(data->buffer(x, y), len, scratch, cov);
563         });
564 }
565
566 template <class T>
567 constexpr const T &clamp(const T &v, const T &lo, const T &hi)
568 {
569     return v < lo ? lo : hi < v ? hi : v;
570 }
571
572 static constexpr inline uchar alpha_mul(uchar a, uchar b)
573 {
574     return ((a * b) >> 8);
575 }
576
577 static void blend_image_xform(size_t size, const VRle::Span *array,
578                               void *userData)
579 {
580     const auto  data = reinterpret_cast<const VSpanData *>(userData);
581     const auto &src = data->texture();
582
583     if (src.format() != VBitmap::Format::ARGB32_Premultiplied &&
584         src.format() != VBitmap::Format::ARGB32) {
585         //@TODO other formats not yet handled.
586         return;
587     }
588
589     Operator op = getOperator(data);
590
591     process_in_chunk(
592         array, size,
593         [&](uint *scratch, size_t x, size_t y, size_t len, uchar cov) {
594             const auto  coverage = (cov * src.alpha()) >> 8;
595             const float xfactor = y * data->m21 + data->dx + data->m11;
596             const float yfactor = y * data->m22 + data->dy + data->m12;
597             for (size_t i = 0; i < len; i++) {
598                 const float fx = (x + i) * data->m11 + xfactor;
599                 const float fy = (x + i) * data->m12 + yfactor;
600                 const int   px = clamp(int(fx), src.left, src.right);
601                 const int   py = clamp(int(fy), src.top, src.bottom);
602                 scratch[i] = src.pixel(px, py);
603             }
604             op.func(data->buffer(x, y), len, scratch, coverage);
605         });
606 }
607
608 static void blend_image(size_t size, const VRle::Span *array, void *userData)
609 {
610     const auto  data = reinterpret_cast<const VSpanData *>(userData);
611     const auto &src = data->texture();
612
613     if (src.format() != VBitmap::Format::ARGB32_Premultiplied &&
614         src.format() != VBitmap::Format::ARGB32) {
615         //@TODO other formats not yet handled.
616         return;
617     }
618
619     Operator op = getOperator(data);
620
621     for (size_t i = 0; i < size; i++) {
622         const auto &span = array[i];
623         int         x = span.x;
624         int         length = span.len;
625         int         sx = x + int(data->dx);
626         int         sy = span.y + int(data->dy);
627
628         // notyhing to copy.
629         if (sy < 0 || sy >= int(src.height()) || sx >= int(src.width()) ||
630             (sx + length) <= 0)
631             continue;
632
633         // intersecting left edge of image
634         if (sx < 0) {
635             x -= sx;
636             length += sx;
637             sx = 0;
638         }
639         // intersecting right edge of image
640         if (sx + length > int(src.width())) length = src.width() - sx;
641
642         op.func(data->buffer(x, span.y), length, src.pixelRef(sx, sy),
643                 alpha_mul(span.coverage, src.alpha()));
644     }
645 }
646
647 void VSpanData::setup(const VBrush &brush, BlendMode /*mode*/, int /*alpha*/)
648 {
649     transformType = VMatrix::MatrixType::None;
650
651     switch (brush.type()) {
652     case VBrush::Type::NoBrush:
653         mType = VSpanData::Type::None;
654         break;
655     case VBrush::Type::Solid:
656         mType = VSpanData::Type::Solid;
657         mSolid = brush.mColor.premulARGB();
658         break;
659     case VBrush::Type::LinearGradient: {
660         mType = VSpanData::Type::LinearGradient;
661         mColorTable = VGradientCache::instance().getBuffer(*brush.mGradient);
662         mGradient.mColorTable = mColorTable->buffer32;
663         mGradient.mColorTableAlpha = mColorTable->alpha;
664         mGradient.linear.x1 = brush.mGradient->linear.x1;
665         mGradient.linear.y1 = brush.mGradient->linear.y1;
666         mGradient.linear.x2 = brush.mGradient->linear.x2;
667         mGradient.linear.y2 = brush.mGradient->linear.y2;
668         mGradient.mSpread = brush.mGradient->mSpread;
669         setupMatrix(brush.mGradient->mMatrix);
670         break;
671     }
672     case VBrush::Type::RadialGradient: {
673         mType = VSpanData::Type::RadialGradient;
674         mColorTable = VGradientCache::instance().getBuffer(*brush.mGradient);
675         mGradient.mColorTable = mColorTable->buffer32;
676         mGradient.mColorTableAlpha = mColorTable->alpha;
677         mGradient.radial.cx = brush.mGradient->radial.cx;
678         mGradient.radial.cy = brush.mGradient->radial.cy;
679         mGradient.radial.fx = brush.mGradient->radial.fx;
680         mGradient.radial.fy = brush.mGradient->radial.fy;
681         mGradient.radial.cradius = brush.mGradient->radial.cradius;
682         mGradient.radial.fradius = brush.mGradient->radial.fradius;
683         mGradient.mSpread = brush.mGradient->mSpread;
684         setupMatrix(brush.mGradient->mMatrix);
685         break;
686     }
687     case VBrush::Type::Texture: {
688         mType = VSpanData::Type::Texture;
689         initTexture(&brush.mTexture->mBitmap, brush.mTexture->mAlpha,
690                     brush.mTexture->mBitmap.rect());
691         setupMatrix(brush.mTexture->mMatrix);
692         break;
693     }
694     default:
695         break;
696     }
697     updateSpanFunc();
698 }
699
700 void VSpanData::setupMatrix(const VMatrix &matrix)
701 {
702     VMatrix inv = matrix.inverted();
703     m11 = inv.m11;
704     m12 = inv.m12;
705     m13 = inv.m13;
706     m21 = inv.m21;
707     m22 = inv.m22;
708     m23 = inv.m23;
709     m33 = inv.m33;
710     dx = inv.mtx;
711     dy = inv.mty;
712     transformType = inv.type();
713
714     const bool  affine = inv.isAffine();
715     const float f1 = m11 * m11 + m21 * m21;
716     const float f2 = m12 * m12 + m22 * m22;
717     fast_matrix = affine && f1 < 1e4 && f2 < 1e4 && f1 > (1.0 / 65536) &&
718                   f2 > (1.0 / 65536) && fabs(dx) < 1e4 && fabs(dy) < 1e4;
719 }
720
721 void VSpanData::initTexture(const VBitmap *bitmap, int alpha,
722                             const VRect &sourceRect)
723 {
724     mType = VSpanData::Type::Texture;
725     mTexture.prepare(bitmap);
726     mTexture.setClip(sourceRect);
727     mTexture.setAlpha(alpha);
728     updateSpanFunc();
729 }
730
731 void VSpanData::updateSpanFunc()
732 {
733     switch (mType) {
734     case VSpanData::Type::None:
735         mUnclippedBlendFunc = nullptr;
736         break;
737     case VSpanData::Type::Solid:
738         mUnclippedBlendFunc = &blend_color;
739         break;
740     case VSpanData::Type::LinearGradient:
741     case VSpanData::Type::RadialGradient: {
742         mUnclippedBlendFunc = &blend_gradient;
743         break;
744     }
745     case VSpanData::Type::Texture: {
746         //@TODO update proper image function.
747         if (transformType <= VMatrix::MatrixType::Translate) {
748             mUnclippedBlendFunc = &blend_image;
749         } else {
750             mUnclippedBlendFunc = &blend_image_xform;
751         }
752         break;
753     }
754     }
755 }
756
757 #if !defined(__SSE2__) && !defined(__ARM_NEON__)
758 void memfill32(uint32_t *dest, uint32_t value, int length)
759 {
760     // let compiler do the auto vectorization.
761     for (int i = 0 ; i < length; i++) {
762         *dest++ = value;
763     }
764 }
765 #endif
766