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