1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18 
19 /****************************************************************************
20 **
21 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
22 ** All rights reserved.
23 ** Contact: Nokia Corporation (qt-info@nokia.com)
24 **
25 ** This file is part of the QtGui module of the Qt Toolkit.
26 **
27 ** $QT_BEGIN_LICENSE:LGPL$
28 ** No Commercial Usage
29 ** This file contains pre-release code and may not be distributed.
30 ** You may use this file in accordance with the terms and conditions
31 ** contained in the Technology Preview License Agreement accompanying
32 ** this package.
33 **
34 ** GNU Lesser General Public License Usage
35 ** Alternatively, this file may be used under the terms of the GNU Lesser
36 ** General Public License version 2.1 as published by the Free Software
37 ** Foundation and appearing in the file LICENSE.LGPL included in the
38 ** packaging of this file.  Please review the following information to
39 ** ensure the GNU Lesser General Public License version 2.1 requirements
40 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
41 **
42 ** In addition, as a special exception, Nokia gives you certain additional
43 ** rights.  These rights are described in the Nokia Qt LGPL Exception
44 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
45 **
46 ** If you have questions regarding the use of this file, please contact
47 ** Nokia at qt-info@nokia.com.
48 **
49 ** $QT_END_LICENSE$
50 **
51 ****************************************************************************/
52 
53 #include "vdrawhelper.h"
54 #include <algorithm>
55 #include <climits>
56 #include <cstring>
57 #include <mutex>
58 #include <unordered_map>
59 
60 class VGradientCache {
61 public:
62     struct CacheInfo : public VColorTable {
CacheInfoVGradientCache::CacheInfo63         inline CacheInfo(VGradientStops s) : stops(std::move(s)) {}
64         VGradientStops stops;
65     };
66     using VCacheData = std::shared_ptr<const CacheInfo>;
67     using VCacheKey = int64_t;
68     using VGradientColorTableHash =
69         std::unordered_multimap<VCacheKey, VCacheData>;
70 
71     bool generateGradientColorTable(const VGradientStops &stops, float alpha,
72                                     uint32_t *colorTable, int size);
getBuffer(const VGradient & gradient)73     VCacheData getBuffer(const VGradient &gradient)
74     {
75         VCacheKey             hash_val = 0;
76         VCacheData            info;
77         const VGradientStops &stops = gradient.mStops;
78         for (uint i = 0; i < stops.size() && i <= 2; i++)
79             hash_val += VCacheKey(stops[i].second.premulARGB() * gradient.alpha());
80 
81         {
82             std::lock_guard<std::mutex> guard(mMutex);
83 
84             size_t count = mCache.count(hash_val);
85             if (!count) {
86                 // key is not present in the hash
87                 info = addCacheElement(hash_val, gradient);
88             } else if (count == 1) {
89                 auto search = mCache.find(hash_val);
90                 if (search->second->stops == stops) {
91                     info = search->second;
92                 } else {
93                     // didn't find an exact match
94                     info = addCacheElement(hash_val, gradient);
95                 }
96             } else {
97                 // we have a multiple data with same key
98                 auto range = mCache.equal_range(hash_val);
99                 for (auto it = range.first; it != range.second; ++it) {
100                     if (it->second->stops == stops) {
101                         info = it->second;
102                         break;
103                     }
104                 }
105                 if (!info) {
106                     // didn't find an exact match
107                     info = addCacheElement(hash_val, gradient);
108                 }
109             }
110         }
111         return info;
112     }
113 
114 protected:
maxCacheSize() const115     uint       maxCacheSize() const { return 60; }
addCacheElement(VCacheKey hash_val,const VGradient & gradient)116     VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient)
117     {
118         if (mCache.size() == maxCacheSize()) {
119             uint count = maxCacheSize() / 10;
120             while (count--) {
121                 mCache.erase(mCache.begin());
122             }
123         }
124         auto cache_entry = std::make_shared<CacheInfo>(gradient.mStops);
125         cache_entry->alpha = generateGradientColorTable(
126             gradient.mStops, gradient.alpha(), cache_entry->buffer32,
127             VGradient::colorTableSize);
128         mCache.insert(std::make_pair(hash_val, cache_entry));
129         return cache_entry;
130     }
131 
132     VGradientColorTableHash mCache;
133     std::mutex              mMutex;
134 };
135 
generateGradientColorTable(const VGradientStops & stops,float opacity,uint32_t * colorTable,int size)136 bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
137                                                 float                 opacity,
138                                                 uint32_t *colorTable, int size)
139 {
140     if (stops.empty()) {
141         return false;
142     }
143     int                  dist, idist, pos = 0;
144     size_t i;
145     bool                 alpha = false;
146     size_t               stopCount = stops.size();
147     const VGradientStop *curr, *next, *start;
148     uint32_t             curColor, nextColor;
149     float                delta, t, incr, fpos;
150 
151     if (!vCompare(opacity, 1.0f)) alpha = true;
152 
153     start = stops.data();
154     curr = start;
155     if (!curr->second.isOpaque()) alpha = true;
156     curColor = curr->second.premulARGB(opacity);
157     incr = 1.0f / (float)size;
158     fpos = 1.5f * incr;
159 
160     colorTable[pos++] = curColor;
161 
162     while (fpos <= curr->first && pos < size) {
163         colorTable[pos] = colorTable[pos - 1];
164         pos++;
165         fpos += incr;
166     }
167 
168     for (i = 0; i < stopCount - 1; ++i) {
169         curr = (start + i);
170         next = (start + i + 1);
171         delta = 1 / (next->first - curr->first);
172         if (!next->second.isOpaque()) alpha = true;
173         nextColor = next->second.premulARGB(opacity);
174         while (fpos < next->first && pos < size) {
175             t = (fpos - curr->first) * delta;
176             dist = (int)(255 * t);
177             idist = 255 - dist;
178             colorTable[pos] =
179                 INTERPOLATE_PIXEL_255(curColor, idist, nextColor, dist);
180             ++pos;
181             fpos += incr;
182         }
183         curColor = nextColor;
184     }
185 
186     for (; pos < size; ++pos) colorTable[pos] = curColor;
187 
188     // Make sure the last color stop is represented at the end of the table
189     colorTable[size - 1] = curColor;
190     return alpha;
191 }
192 
193 static VGradientCache VGradientCacheInstance;
194 
clear()195 void VRasterBuffer::clear()
196 {
197     memset(mBuffer, 0, mHeight * mBytesPerLine);
198 }
199 
prepare(VBitmap * image)200 VBitmap::Format VRasterBuffer::prepare(VBitmap *image)
201 {
202     mBuffer = image->data();
203     mWidth = image->width();
204     mHeight = image->height();
205     mBytesPerPixel = 4;
206     mBytesPerLine = image->stride();
207 
208     mFormat = image->format();
209     return mFormat;
210 }
211 
init(VRasterBuffer * image)212 void VSpanData::init(VRasterBuffer *image)
213 {
214     mRasterBuffer = image;
215     setDrawRegion(VRect(0, 0, int(image->width()), int(image->height())));
216     mType = VSpanData::Type::None;
217     mBlendFunc = nullptr;
218     mUnclippedBlendFunc = nullptr;
219 }
220 
221 extern CompositionFunction             COMP_functionForMode_C[];
222 extern CompositionFunctionSolid        COMP_functionForModeSolid_C[];
223 static const CompositionFunction *     functionForMode = COMP_functionForMode_C;
224 static const CompositionFunctionSolid *functionForModeSolid =
225     COMP_functionForModeSolid_C;
226 
227 /*
228  *  Gradient Draw routines
229  *
230  */
231 
232 #define FIXPT_BITS 8
233 #define FIXPT_SIZE (1 << FIXPT_BITS)
getLinearGradientValues(LinearGradientValues * v,const VSpanData * data)234 static inline void getLinearGradientValues(LinearGradientValues *v,
235                                            const VSpanData *     data)
236 {
237     const VGradientData *grad = &data->mGradient;
238     v->dx = grad->linear.x2 - grad->linear.x1;
239     v->dy = grad->linear.y2 - grad->linear.y1;
240     v->l = v->dx * v->dx + v->dy * v->dy;
241     v->off = 0;
242     if (v->l != 0) {
243         v->dx /= v->l;
244         v->dy /= v->l;
245         v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
246     }
247 }
248 
getRadialGradientValues(RadialGradientValues * v,const VSpanData * data)249 static inline void getRadialGradientValues(RadialGradientValues *v,
250                                            const VSpanData *     data)
251 {
252     const VGradientData &gradient = data->mGradient;
253     v->dx = gradient.radial.cx - gradient.radial.fx;
254     v->dy = gradient.radial.cy - gradient.radial.fy;
255 
256     v->dr = gradient.radial.cradius - gradient.radial.fradius;
257     v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
258 
259     v->a = v->dr * v->dr - v->dx * v->dx - v->dy * v->dy;
260     v->inv2a = 1 / (2 * v->a);
261 
262     v->extended = !vIsZero(gradient.radial.fradius) || v->a <= 0;
263 }
264 
gradientClamp(const VGradientData * grad,int ipos)265 static inline int gradientClamp(const VGradientData *grad, int ipos)
266 {
267     int limit;
268 
269     if (grad->mSpread == VGradient::Spread::Repeat) {
270         ipos = ipos % VGradient::colorTableSize;
271         ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
272     } else if (grad->mSpread == VGradient::Spread::Reflect) {
273         limit = VGradient::colorTableSize * 2;
274         ipos = ipos % limit;
275         ipos = ipos < 0 ? limit + ipos : ipos;
276         ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
277     } else {
278         if (ipos < 0)
279             ipos = 0;
280         else if (ipos >= VGradient::colorTableSize)
281             ipos = VGradient::colorTableSize - 1;
282     }
283     return ipos;
284 }
285 
gradientPixelFixed(const VGradientData * grad,int fixed_pos)286 static uint32_t gradientPixelFixed(const VGradientData *grad, int fixed_pos)
287 {
288     int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
289 
290     return grad->mColorTable[gradientClamp(grad, ipos)];
291 }
292 
gradientPixel(const VGradientData * grad,float pos)293 static inline uint32_t gradientPixel(const VGradientData *grad, float pos)
294 {
295     int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
296 
297     return grad->mColorTable[gradientClamp(grad, ipos)];
298 }
299 
fetch_linear_gradient(uint32_t * buffer,const Operator * op,const VSpanData * data,int y,int x,int length)300 void fetch_linear_gradient(uint32_t *buffer, const Operator *op,
301                            const VSpanData *data, int y, int x, int length)
302 {
303     float                t, inc;
304     const VGradientData *gradient = &data->mGradient;
305 
306     bool  affine = true;
307     float rx = 0, ry = 0;
308     if (op->linear.l == 0) {
309         t = inc = 0;
310     } else {
311         rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) +
312              data->dx;
313         ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) +
314              data->dy;
315         t = op->linear.dx * rx + op->linear.dy * ry + op->linear.off;
316         inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
317         affine = !data->m13 && !data->m23;
318 
319         if (affine) {
320             t *= (VGradient::colorTableSize - 1);
321             inc *= (VGradient::colorTableSize - 1);
322         }
323     }
324 
325     const uint32_t *end = buffer + length;
326     if (affine) {
327         if (inc > float(-1e-5) && inc < float(1e-5)) {
328             memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)),
329                       length);
330         } else {
331             if (t + inc * length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
332                 t + inc * length > float(INT_MIN >> (FIXPT_BITS + 1))) {
333                 // we can use fixed point math
334                 int t_fixed = int(t * FIXPT_SIZE);
335                 int inc_fixed = int(inc * FIXPT_SIZE);
336                 while (buffer < end) {
337                     *buffer = gradientPixelFixed(gradient, t_fixed);
338                     t_fixed += inc_fixed;
339                     ++buffer;
340                 }
341             } else {
342                 // we have to fall back to float math
343                 while (buffer < end) {
344                     *buffer =
345                         gradientPixel(gradient, t / VGradient::colorTableSize);
346                     t += inc;
347                     ++buffer;
348                 }
349             }
350         }
351     } else {  // fall back to float math here as well
352         float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) +
353                    data->m33;
354         while (buffer < end) {
355             float xt = rx / rw;
356             float yt = ry / rw;
357             t = (op->linear.dx * xt + op->linear.dy * yt) + op->linear.off;
358 
359             *buffer = gradientPixel(gradient, t);
360             rx += data->m11;
361             ry += data->m12;
362             rw += data->m13;
363             if (!rw) {
364                 rw += data->m13;
365             }
366             ++buffer;
367         }
368     }
369 }
370 
radialDeterminant(float a,float b,float c)371 static inline float radialDeterminant(float a, float b, float c)
372 {
373     return (b * b) - (4 * a * c);
374 }
375 
fetch(uint32_t * buffer,uint32_t * end,const Operator * op,const VSpanData * data,float det,float delta_det,float delta_delta_det,float b,float delta_b)376 static void fetch(uint32_t *buffer, uint32_t *end, const Operator *op,
377                   const VSpanData *data, float det, float delta_det,
378                   float delta_delta_det, float b, float delta_b)
379 {
380     if (op->radial.extended) {
381         while (buffer < end) {
382             uint32_t result = 0;
383             if (det >= 0) {
384                 float w = std::sqrt(det) - b;
385                 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
386                     result = gradientPixel(&data->mGradient, w);
387             }
388 
389             *buffer = result;
390 
391             det += delta_det;
392             delta_det += delta_delta_det;
393             b += delta_b;
394 
395             ++buffer;
396         }
397     } else {
398         while (buffer < end) {
399             *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
400 
401             det += delta_det;
402             delta_det += delta_delta_det;
403             b += delta_b;
404         }
405     }
406 }
407 
fetch_radial_gradient(uint32_t * buffer,const Operator * op,const VSpanData * data,int y,int x,int length)408 void fetch_radial_gradient(uint32_t *buffer, const Operator *op,
409                            const VSpanData *data, int y, int x, int length)
410 {
411     // avoid division by zero
412     if (vIsZero(op->radial.a)) {
413         memfill32(buffer, 0, length);
414         return;
415     }
416 
417     float rx =
418         data->m21 * (y + float(0.5)) + data->dx + data->m11 * (x + float(0.5));
419     float ry =
420         data->m22 * (y + float(0.5)) + data->dy + data->m12 * (x + float(0.5));
421     bool affine = !data->m13 && !data->m23;
422 
423     uint32_t *end = buffer + length;
424     if (affine) {
425         rx -= data->mGradient.radial.fx;
426         ry -= data->mGradient.radial.fy;
427 
428         float inv_a = 1 / float(2 * op->radial.a);
429 
430         const float delta_rx = data->m11;
431         const float delta_ry = data->m12;
432 
433         float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
434                        rx * op->radial.dx + ry * op->radial.dy);
435         float delta_b =
436             2 * (delta_rx * op->radial.dx + delta_ry * op->radial.dy);
437         const float b_delta_b = 2 * b * delta_b;
438         const float delta_b_delta_b = 2 * delta_b * delta_b;
439 
440         const float bb = b * b;
441         const float delta_bb = delta_b * delta_b;
442 
443         b *= inv_a;
444         delta_b *= inv_a;
445 
446         const float rxrxryry = rx * rx + ry * ry;
447         const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
448         const float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
449         const float delta_rx_plus_ry = 2 * delta_rxrxryry;
450 
451         inv_a *= inv_a;
452 
453         float det =
454             (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
455         float delta_det = (b_delta_b + delta_bb +
456                            4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) *
457                           inv_a;
458         const float delta_delta_det =
459             (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
460 
461         fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b,
462               delta_b);
463     } else {
464         float rw = data->m23 * (y + float(0.5)) + data->m33 +
465                    data->m13 * (x + float(0.5));
466 
467         while (buffer < end) {
468             if (rw == 0) {
469                 *buffer = 0;
470             } else {
471                 float invRw = 1 / rw;
472                 float gx = rx * invRw - data->mGradient.radial.fx;
473                 float gy = ry * invRw - data->mGradient.radial.fy;
474                 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
475                                gx * op->radial.dx + gy * op->radial.dy);
476                 float det = radialDeterminant(
477                     op->radial.a, b, op->radial.sqrfr - (gx * gx + gy * gy));
478 
479                 uint32_t result = 0;
480                 if (det >= 0) {
481                     float detSqrt = std::sqrt(det);
482 
483                     float s0 = (-b - detSqrt) * op->radial.inv2a;
484                     float s1 = (-b + detSqrt) * op->radial.inv2a;
485 
486                     float s = vMax(s0, s1);
487 
488                     if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
489                         result = gradientPixel(&data->mGradient, s);
490                 }
491 
492                 *buffer = result;
493             }
494 
495             rx += data->m11;
496             ry += data->m12;
497             rw += data->m13;
498 
499             ++buffer;
500         }
501     }
502 }
503 
getOperator(const VSpanData * data,const VRle::Span *,size_t)504 static inline Operator getOperator(const VSpanData *data, const VRle::Span *,
505                                    size_t)
506 {
507     Operator op;
508     bool     solidSource = false;
509 
510     switch (data->mType) {
511     case VSpanData::Type::Solid:
512         solidSource = (vAlpha(data->mSolid) == 255);
513         op.srcFetch = nullptr;
514         break;
515     case VSpanData::Type::LinearGradient:
516         solidSource = false;
517         getLinearGradientValues(&op.linear, data);
518         op.srcFetch = &fetch_linear_gradient;
519         break;
520     case VSpanData::Type::RadialGradient:
521         solidSource = false;
522         getRadialGradientValues(&op.radial, data);
523         op.srcFetch = &fetch_radial_gradient;
524         break;
525     default:
526         op.srcFetch = nullptr;
527         break;
528     }
529 
530     op.mode = data->mCompositionMode;
531     if (op.mode == VPainter::CompModeSrcOver && solidSource)
532         op.mode = VPainter::CompModeSrc;
533 
534     op.funcSolid = functionForModeSolid[op.mode];
535     op.func = functionForMode[op.mode];
536 
537     return op;
538 }
539 
blendColorARGB(size_t count,const VRle::Span * spans,void * userData)540 static void blendColorARGB(size_t count, const VRle::Span *spans,
541                            void *userData)
542 {
543     VSpanData *data = (VSpanData *)(userData);
544     Operator   op = getOperator(data, spans, count);
545     const uint color = data->mSolid;
546 
547     if (op.mode == VPainter::CompModeSrc) {
548         // inline for performance
549         while (count--) {
550             uint *target = data->buffer(spans->x, spans->y);
551             if (spans->coverage == 255) {
552                 memfill32(target, color, spans->len);
553             } else {
554                 uint c = BYTE_MUL(color, spans->coverage);
555                 int  ialpha = 255 - spans->coverage;
556                 for (int i = 0; i < spans->len; ++i)
557                     target[i] = c + BYTE_MUL(target[i], ialpha);
558             }
559             ++spans;
560         }
561         return;
562     }
563 
564     while (count--) {
565         uint *target = data->buffer(spans->x, spans->y);
566         op.funcSolid(target, spans->len, color, spans->coverage);
567         ++spans;
568     }
569 }
570 
571 #define BLEND_GRADIENT_BUFFER_SIZE 2048
blendGradientARGB(size_t count,const VRle::Span * spans,void * userData)572 static void blendGradientARGB(size_t count, const VRle::Span *spans,
573                               void *userData)
574 {
575     VSpanData *data = (VSpanData *)(userData);
576     Operator   op = getOperator(data, spans, count);
577 
578     unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
579 
580     if (!op.srcFetch) return;
581 
582     while (count--) {
583         uint *target = data->buffer(spans->x, spans->y);
584         int   length = spans->len;
585         while (length) {
586             int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
587             op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
588             op.func(target, buffer, l, spans->coverage);
589             target += l;
590             length -= l;
591         }
592         ++spans;
593     }
594 }
595 
596 template <class T>
clamp(const T & v,const T & lo,const T & hi)597 constexpr const T &clamp(const T &v, const T &lo, const T &hi)
598 {
599     return v < lo ? lo : hi < v ? hi : v;
600 }
601 
602 static const int buffer_size = 1024;
603 static const int fixed_scale = 1 << 16;
blend_transformed_argb(size_t count,const VRle::Span * spans,void * userData)604 static void      blend_transformed_argb(size_t count, const VRle::Span *spans,
605                                         void *userData)
606 {
607     VSpanData *data = reinterpret_cast<VSpanData *>(userData);
608     if (data->mBitmap.format != VBitmap::Format::ARGB32_Premultiplied &&
609         data->mBitmap.format != VBitmap::Format::ARGB32) {
610         //@TODO other formats not yet handled.
611         return;
612     }
613 
614     Operator op = getOperator(data, spans, count);
615     uint     buffer[buffer_size];
616 
617     const int image_x1 = data->mBitmap.x1;
618     const int image_y1 = data->mBitmap.y1;
619     const int image_x2 = data->mBitmap.x2 - 1;
620     const int image_y2 = data->mBitmap.y2 - 1;
621 
622     if (data->fast_matrix) {
623         // The increment pr x in the scanline
624         int fdx = (int)(data->m11 * fixed_scale);
625         int fdy = (int)(data->m12 * fixed_scale);
626 
627         while (count--) {
628             uint *target = data->buffer(spans->x, spans->y);
629 
630             const float cx = spans->x + float(0.5);
631             const float cy = spans->y + float(0.5);
632 
633             int x =
634                 int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
635             int y =
636                 int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
637 
638             int       length = spans->len;
639             const int coverage =
640                 (spans->coverage * data->mBitmap.const_alpha) >> 8;
641             while (length) {
642                 int         l = std::min(length, buffer_size);
643                 const uint *end = buffer + l;
644                 uint *      b = buffer;
645                 while (b < end) {
646                     int px = clamp(x >> 16, image_x1, image_x2);
647                     int py = clamp(y >> 16, image_y1, image_y2);
648                     *b = reinterpret_cast<const uint *>(
649                         data->mBitmap.scanLine(py))[px];
650 
651                     x += fdx;
652                     y += fdy;
653                     ++b;
654                 }
655                 op.func(target, buffer, l, coverage);
656                 target += l;
657                 length -= l;
658             }
659             ++spans;
660         }
661     } else {
662         const float fdx = data->m11;
663         const float fdy = data->m12;
664         const float fdw = data->m13;
665         while (count--) {
666             uint *target = data->buffer(spans->x, spans->y);
667 
668             const float cx = spans->x + float(0.5);
669             const float cy = spans->y + float(0.5);
670 
671             float x = data->m21 * cy + data->m11 * cx + data->dx;
672             float y = data->m22 * cy + data->m12 * cx + data->dy;
673             float w = data->m23 * cy + data->m13 * cx + data->m33;
674 
675             int       length = spans->len;
676             const int coverage =
677                 (spans->coverage * data->mBitmap.const_alpha) >> 8;
678             while (length) {
679                 int         l = std::min(length, buffer_size);
680                 const uint *end = buffer + l;
681                 uint *      b = buffer;
682                 while (b < end) {
683                     const float iw = w == 0 ? 1 : 1 / w;
684                     const float tx = x * iw;
685                     const float ty = y * iw;
686                     const int   px =
687                         clamp(int(tx) - (tx < 0), image_x1, image_x2);
688                     const int py =
689                         clamp(int(ty) - (ty < 0), image_y1, image_y2);
690 
691                     *b = reinterpret_cast<const uint *>(
692                         data->mBitmap.scanLine(py))[px];
693                     x += fdx;
694                     y += fdy;
695                     w += fdw;
696 
697                     ++b;
698                 }
699                 op.func(target, buffer, l, coverage);
700                 target += l;
701                 length -= l;
702             }
703             ++spans;
704         }
705     }
706 }
707 
blend_untransformed_argb(size_t count,const VRle::Span * spans,void * userData)708 static void blend_untransformed_argb(size_t count, const VRle::Span *spans,
709                                      void *userData)
710 {
711     VSpanData *data = reinterpret_cast<VSpanData *>(userData);
712     if (data->mBitmap.format != VBitmap::Format::ARGB32_Premultiplied &&
713         data->mBitmap.format != VBitmap::Format::ARGB32) {
714         //@TODO other formats not yet handled.
715         return;
716     }
717 
718     Operator op = getOperator(data, spans, count);
719 
720     const int image_width = data->mBitmap.width;
721     const int image_height = data->mBitmap.height;
722 
723     int xoff = int(data->dx);
724     int yoff = int(data->dy);
725 
726     while (count--) {
727         int x = spans->x;
728         int length = spans->len;
729         int sx = xoff + x;
730         int sy = yoff + spans->y;
731         if (sy >= 0 && sy < image_height && sx < image_width) {
732             if (sx < 0) {
733                 x -= sx;
734                 length += sx;
735                 sx = 0;
736             }
737             if (sx + length > image_width) length = image_width - sx;
738             if (length > 0) {
739                 const int coverage =
740                     (spans->coverage * data->mBitmap.const_alpha) >> 8;
741                 const uint *src = (const uint *)data->mBitmap.scanLine(sy) + sx;
742                 uint *      dest = data->buffer(x, spans->y);
743                 op.func(dest, src, length, coverage);
744             }
745         }
746         ++spans;
747     }
748 }
749 
setup(const VBrush & brush,VPainter::CompositionMode,int)750 void VSpanData::setup(const VBrush &brush, VPainter::CompositionMode /*mode*/,
751                       int /*alpha*/)
752 {
753     transformType = VMatrix::MatrixType::None;
754 
755     switch (brush.type()) {
756     case VBrush::Type::NoBrush:
757         mType = VSpanData::Type::None;
758         break;
759     case VBrush::Type::Solid:
760         mType = VSpanData::Type::Solid;
761         mSolid = brush.mColor.premulARGB();
762         break;
763     case VBrush::Type::LinearGradient: {
764         mType = VSpanData::Type::LinearGradient;
765         mColorTable = VGradientCacheInstance.getBuffer(*brush.mGradient);
766         mGradient.mColorTable = mColorTable->buffer32;
767         mGradient.mColorTableAlpha = mColorTable->alpha;
768         mGradient.linear.x1 = brush.mGradient->linear.x1;
769         mGradient.linear.y1 = brush.mGradient->linear.y1;
770         mGradient.linear.x2 = brush.mGradient->linear.x2;
771         mGradient.linear.y2 = brush.mGradient->linear.y2;
772         mGradient.mSpread = brush.mGradient->mSpread;
773         setupMatrix(brush.mGradient->mMatrix);
774         break;
775     }
776     case VBrush::Type::RadialGradient: {
777         mType = VSpanData::Type::RadialGradient;
778         mColorTable = VGradientCacheInstance.getBuffer(*brush.mGradient);
779         mGradient.mColorTable = mColorTable->buffer32;
780         mGradient.mColorTableAlpha = mColorTable->alpha;
781         mGradient.radial.cx = brush.mGradient->radial.cx;
782         mGradient.radial.cy = brush.mGradient->radial.cy;
783         mGradient.radial.fx = brush.mGradient->radial.fx;
784         mGradient.radial.fy = brush.mGradient->radial.fy;
785         mGradient.radial.cradius = brush.mGradient->radial.cradius;
786         mGradient.radial.fradius = brush.mGradient->radial.fradius;
787         mGradient.mSpread = brush.mGradient->mSpread;
788         setupMatrix(brush.mGradient->mMatrix);
789         break;
790     }
791     case VBrush::Type::Texture: {
792         mType = VSpanData::Type::Texture;
793         initTexture(
794             &brush.mTexture, 255, VBitmapData::Plain,
795             brush.mTexture.rect());
796         setupMatrix(brush.mMatrix);
797         break;
798     }
799     default:
800         break;
801     }
802     updateSpanFunc();
803 }
804 
setupMatrix(const VMatrix & matrix)805 void VSpanData::setupMatrix(const VMatrix &matrix)
806 {
807     VMatrix inv = matrix.inverted();
808     m11 = inv.m11;
809     m12 = inv.m12;
810     m13 = inv.m13;
811     m21 = inv.m21;
812     m22 = inv.m22;
813     m23 = inv.m23;
814     m33 = inv.m33;
815     dx = inv.mtx;
816     dy = inv.mty;
817     transformType = inv.type();
818 
819     const bool  affine = inv.isAffine();
820     const float f1 = m11 * m11 + m21 * m21;
821     const float f2 = m12 * m12 + m22 * m22;
822     fast_matrix = affine && f1 < 1e4 && f2 < 1e4 && f1 > (1.0 / 65536) &&
823                   f2 > (1.0 / 65536) && fabs(dx) < 1e4 && fabs(dy) < 1e4;
824 }
825 
initTexture(const VBitmap * bitmap,int alpha,VBitmapData::Type type,const VRect & sourceRect)826 void VSpanData::initTexture(const VBitmap *bitmap, int alpha,
827                             VBitmapData::Type type, const VRect &sourceRect)
828 {
829     mType = VSpanData::Type::Texture;
830 
831     mBitmap.imageData = bitmap->data();
832     mBitmap.width = int(bitmap->width());
833     mBitmap.height = int(bitmap->height());
834     mBitmap.bytesPerLine = bitmap->stride();
835     mBitmap.format = bitmap->format();
836     mBitmap.x1 = sourceRect.x();
837     mBitmap.y1 = sourceRect.y();
838     mBitmap.x2 = std::min(mBitmap.x1 + sourceRect.width(), mBitmap.width);
839     mBitmap.y2 = std::min(mBitmap.y1 + sourceRect.height(), mBitmap.height);
840 
841     mBitmap.const_alpha = alpha;
842     mBitmap.type = type;
843 
844     updateSpanFunc();
845 }
846 
updateSpanFunc()847 void VSpanData::updateSpanFunc()
848 {
849     switch (mType) {
850     case VSpanData::Type::None:
851         mUnclippedBlendFunc = nullptr;
852         break;
853     case VSpanData::Type::Solid:
854         mUnclippedBlendFunc = &blendColorARGB;
855         break;
856     case VSpanData::Type::LinearGradient:
857     case VSpanData::Type::RadialGradient: {
858         mUnclippedBlendFunc = &blendGradientARGB;
859         break;
860     }
861     case VSpanData::Type::Texture: {
862         //@TODO update proper image function.
863         if (transformType <= VMatrix::MatrixType::Translate) {
864             mUnclippedBlendFunc = &blend_untransformed_argb;
865         } else {
866             mUnclippedBlendFunc = &blend_transformed_argb;
867         }
868         break;
869     }
870     }
871 }
872 
873 #if !defined(__SSE2__) && (!defined(__ARM_NEON__) || defined(LOTTIE_DISABLE_ARM_NEON))
memfill32(uint32_t * dest,uint32_t value,int length)874 void memfill32(uint32_t *dest, uint32_t value, int length)
875 {
876     int n;
877 
878     if (length <= 0) return;
879 
880     // Cute hack to align future memcopy operation
881     // and do unroll the loop a bit. Not sure it is
882     // the most efficient, but will do for now.
883     n = (length + 7) / 8;
884     switch (length & 0x07) {
885     case 0:
886         do {
887             *dest++ = value;
888             VECTOR_FALLTHROUGH;
889         case 7:
890             *dest++ = value;
891             VECTOR_FALLTHROUGH;
892         case 6:
893             *dest++ = value;
894             VECTOR_FALLTHROUGH;
895         case 5:
896             *dest++ = value;
897             VECTOR_FALLTHROUGH;
898         case 4:
899             *dest++ = value;
900             VECTOR_FALLTHROUGH;
901         case 3:
902             *dest++ = value;
903             VECTOR_FALLTHROUGH;
904         case 2:
905             *dest++ = value;
906             VECTOR_FALLTHROUGH;
907         case 1:
908             *dest++ = value;
909         } while (--n > 0);
910     }
911 }
912 #endif
913 
vInitDrawhelperFunctions()914 void vInitDrawhelperFunctions()
915 {
916     vInitBlendFunctions();
917 
918 #if defined(__ARM_NEON__) && !defined(LOTTIE_DISABLE_ARM_NEON)
919     // update fast path for NEON
920     extern void Vcomp_func_solid_SourceOver_neon(
921         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
922 
923     COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
924         Vcomp_func_solid_SourceOver_neon;
925 #endif
926 
927 #if defined(__SSE2__)
928     // update fast path for SSE2
929     extern void Vcomp_func_solid_SourceOver_sse2(
930         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
931     extern void Vcomp_func_solid_Source_sse2(
932         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
933     extern void Vcomp_func_Source_sse2(uint32_t * dest, const uint32_t *src,
934                                       int length, uint32_t const_alpha);
935     extern void Vcomp_func_SourceOver_sse2(uint32_t * dest, const uint32_t *src,
936                                           int length, uint32_t const_alpha);
937 
938     COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
939         Vcomp_func_solid_Source_sse2;
940     COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
941         Vcomp_func_solid_SourceOver_sse2;
942 
943     COMP_functionForMode_C[VPainter::CompModeSrc] = Vcomp_func_Source_sse2;
944     // COMP_functionForMode_C[VPainter::CompModeSrcOver] =
945     // Vcomp_func_SourceOver_sse2;
946 #endif
947 }
948 
949 V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)
950