2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
23 #include "vdrawhelper.h"
28 #include <unordered_map>
31 static RenderFuncTable RenderTable;
33 void VTextureData::setClip(const VRect &clip)
37 right = std::min(clip.right(), int(width())) - 1;
38 bottom = std::min(clip.bottom(), int(height())) - 1;
41 class VGradientCache {
43 struct CacheInfo : public VColorTable {
44 inline CacheInfo(VGradientStops s) : stops(std::move(s)) {}
47 using VCacheData = std::shared_ptr<const CacheInfo>;
48 using VCacheKey = int64_t;
49 using VGradientColorTableHash =
50 std::unordered_multimap<VCacheKey, VCacheData>;
52 bool generateGradientColorTable(const VGradientStops &stops, float alpha,
53 uint32_t *colorTable, int size);
54 VCacheData getBuffer(const VGradient &gradient)
56 VCacheKey hash_val = 0;
58 const VGradientStops &stops = gradient.mStops;
59 for (uint i = 0; i < stops.size() && i <= 2; i++)
61 VCacheKey(stops[i].second.premulARGB() * gradient.alpha());
64 std::lock_guard<std::mutex> guard(mMutex);
66 size_t count = mCache.count(hash_val);
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;
75 // didn't find an exact match
76 info = addCacheElement(hash_val, gradient);
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) {
88 // didn't find an exact match
89 info = addCacheElement(hash_val, gradient);
96 static VGradientCache &instance()
98 static VGradientCache CACHE;
103 uint maxCacheSize() const { return 60; }
104 VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient)
106 if (mCache.size() == maxCacheSize()) {
107 uint count = maxCacheSize() / 10;
109 mCache.erase(mCache.begin());
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));
121 VGradientCache() = default;
123 VGradientColorTableHash mCache;
127 bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
129 uint32_t *colorTable, int size)
131 int dist, idist, pos = 0;
134 size_t stopCount = stops.size();
135 const VGradientStop *curr, *next, *start;
136 uint32_t curColor, nextColor;
137 float delta, t, incr, fpos;
139 if (!vCompare(opacity, 1.0f)) alpha = true;
141 start = stops.data();
143 if (!curr->second.isOpaque()) alpha = true;
144 curColor = curr->second.premulARGB(opacity);
145 incr = 1.0f / (float)size;
148 colorTable[pos++] = curColor;
150 while (fpos <= curr->first) {
151 colorTable[pos] = colorTable[pos - 1];
156 for (i = 0; i < stopCount - 1; ++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);
167 interpolate_pixel(curColor, idist, nextColor, dist);
171 curColor = nextColor;
174 for (; pos < size; ++pos) colorTable[pos] = curColor;
176 // Make sure the last color stop is represented at the end of the table
177 colorTable[size - 1] = curColor;
181 void VRasterBuffer::clear()
183 memset(mBuffer, 0, mHeight * mBytesPerLine);
186 VBitmap::Format VRasterBuffer::prepare(const VBitmap *image)
188 mBuffer = image->data();
189 mWidth = image->width();
190 mHeight = image->height();
192 mBytesPerLine = image->stride();
194 mFormat = image->format();
198 void VSpanData::init(VRasterBuffer *image)
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;
208 * Gradient Draw routines
213 #define FIXPT_SIZE (1 << FIXPT_BITS)
214 static inline void getLinearGradientValues(LinearGradientValues *v,
215 const VSpanData * data)
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;
225 v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
229 static inline void getRadialGradientValues(RadialGradientValues *v,
230 const VSpanData * data)
232 const VGradientData &gradient = data->mGradient;
233 v->dx = gradient.radial.cx - gradient.radial.fx;
234 v->dy = gradient.radial.cy - gradient.radial.fy;
236 v->dr = gradient.radial.cradius - gradient.radial.fradius;
237 v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
239 v->a = v->dr * v->dr - v->dx * v->dx - v->dy * v->dy;
240 v->inv2a = 1 / (2 * v->a);
242 v->extended = !vIsZero(gradient.radial.fradius) || v->a <= 0;
245 static inline int gradientClamp(const VGradientData *grad, int ipos)
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;
255 ipos = ipos < 0 ? limit + ipos : ipos;
256 ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
260 else if (ipos >= VGradient::colorTableSize)
261 ipos = VGradient::colorTableSize - 1;
266 static uint32_t gradientPixelFixed(const VGradientData *grad, int fixed_pos)
268 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
270 return grad->mColorTable[gradientClamp(grad, ipos)];
273 static inline uint32_t gradientPixel(const VGradientData *grad, float pos)
275 int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
277 return grad->mColorTable[gradientClamp(grad, ipos)];
280 void fetch_linear_gradient(uint32_t *buffer, const Operator *op,
281 const VSpanData *data, int y, int x, int length)
284 const VGradientData *gradient = &data->mGradient;
287 float rx = 0, ry = 0;
288 if (op->linear.l == 0) {
291 rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) +
293 ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) +
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;
300 t *= (VGradient::colorTableSize - 1);
301 inc *= (VGradient::colorTableSize - 1);
305 const uint32_t *end = buffer + length;
307 if (inc > float(-1e-5) && inc < float(1e-5)) {
308 memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)),
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;
322 // we have to fall back to float math
323 while (buffer < end) {
325 gradientPixel(gradient, t / VGradient::colorTableSize);
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)) +
334 while (buffer < end) {
337 t = (op->linear.dx * xt + op->linear.dy * yt) + op->linear.off;
339 *buffer = gradientPixel(gradient, t);
351 static inline float radialDeterminant(float a, float b, float c)
353 return (b * b) - (4 * a * c);
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)
360 if (op->radial.extended) {
361 while (buffer < end) {
364 float w = std::sqrt(det) - b;
365 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
366 result = gradientPixel(&data->mGradient, w);
372 delta_det += delta_delta_det;
378 while (buffer < end) {
379 *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
382 delta_det += delta_delta_det;
388 void fetch_radial_gradient(uint32_t *buffer, const Operator *op,
389 const VSpanData *data, int y, int x, int length)
391 // avoid division by zero
392 if (vIsZero(op->radial.a)) {
393 memfill32(buffer, 0, length);
398 data->m21 * (y + float(0.5)) + data->dx + data->m11 * (x + float(0.5));
400 data->m22 * (y + float(0.5)) + data->dy + data->m12 * (x + float(0.5));
401 bool affine = !data->m13 && !data->m23;
403 uint32_t *end = buffer + length;
405 rx -= data->mGradient.radial.fx;
406 ry -= data->mGradient.radial.fy;
408 float inv_a = 1 / float(2 * op->radial.a);
410 const float delta_rx = data->m11;
411 const float delta_ry = data->m12;
413 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
414 rx * op->radial.dx + ry * op->radial.dy);
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;
420 const float bb = b * b;
421 const float delta_bb = delta_b * delta_b;
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;
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)) *
438 const float delta_delta_det =
439 (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
441 fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b,
444 float rw = data->m23 * (y + float(0.5)) + data->m33 +
445 data->m13 * (x + float(0.5));
447 while (buffer < end) {
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));
461 float detSqrt = std::sqrt(det);
463 float s0 = (-b - detSqrt) * op->radial.inv2a;
464 float s1 = (-b + detSqrt) * op->radial.inv2a;
466 float s = vMax(s0, s1);
468 if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
469 result = gradientPixel(&data->mGradient, s);
484 static inline Operator getOperator(const VSpanData *data)
487 bool solidSource = false;
489 switch (data->mType) {
490 case VSpanData::Type::Solid:
491 solidSource = (vAlpha(data->mSolid) == 255);
492 op.srcFetch = nullptr;
494 case VSpanData::Type::LinearGradient:
496 getLinearGradientValues(&op.linear, data);
497 op.srcFetch = &fetch_linear_gradient;
499 case VSpanData::Type::RadialGradient:
501 getRadialGradientValues(&op.radial, data);
502 op.srcFetch = &fetch_radial_gradient;
505 op.srcFetch = nullptr;
509 op.mode = data->mBlendMode;
510 if (op.mode == BlendMode::SrcOver && solidSource) op.mode = BlendMode::Src;
512 op.funcSolid = RenderTable.color(op.mode);
513 op.func = RenderTable.src(op.mode);
518 static void blend_color(size_t size, const VRle::Span *array, void *userData)
520 VSpanData *data = (VSpanData *)(userData);
521 Operator op = getOperator(data);
522 const uint color = data->mSolid;
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);
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,
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;
542 auto l = std::min(len, buf.size());
543 process(buf.data(), x, span.y, l, span.coverage);
550 static void blend_gradient(size_t size, const VRle::Span *array,
553 VSpanData *data = (VSpanData *)(userData);
554 Operator op = getOperator(data);
556 if (!op.srcFetch) return;
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);
567 constexpr const T &clamp(const T &v, const T &lo, const T &hi)
569 return v < lo ? lo : hi < v ? hi : v;
572 static constexpr inline uchar alpha_mul(uchar a, uchar b)
574 return ((a * b) >> 8);
577 static void blend_image_xform(size_t size, const VRle::Span *array,
580 const auto data = reinterpret_cast<const VSpanData *>(userData);
581 const auto &src = data->texture();
583 if (src.format() != VBitmap::Format::ARGB32_Premultiplied &&
584 src.format() != VBitmap::Format::ARGB32) {
585 //@TODO other formats not yet handled.
589 Operator op = getOperator(data);
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);
604 op.func(data->buffer(x, y), len, scratch, coverage);
608 static void blend_image(size_t size, const VRle::Span *array, void *userData)
610 const auto data = reinterpret_cast<const VSpanData *>(userData);
611 const auto &src = data->texture();
613 if (src.format() != VBitmap::Format::ARGB32_Premultiplied &&
614 src.format() != VBitmap::Format::ARGB32) {
615 //@TODO other formats not yet handled.
619 Operator op = getOperator(data);
621 for (size_t i = 0; i < size; i++) {
622 const auto &span = array[i];
624 int length = span.len;
625 int sx = x + int(data->dx);
626 int sy = span.y + int(data->dy);
629 if (sy < 0 || sy >= int(src.height()) || sx >= int(src.width()) ||
633 // intersecting left edge of image
639 // intersecting right edge of image
640 if (sx + length > int(src.width())) length = src.width() - sx;
642 op.func(data->buffer(x, span.y), length, src.pixelRef(sx, sy),
643 alpha_mul(span.coverage, src.alpha()));
647 void VSpanData::setup(const VBrush &brush, BlendMode /*mode*/, int /*alpha*/)
649 transformType = VMatrix::MatrixType::None;
651 switch (brush.type()) {
652 case VBrush::Type::NoBrush:
653 mType = VSpanData::Type::None;
655 case VBrush::Type::Solid:
656 mType = VSpanData::Type::Solid;
657 mSolid = brush.mColor.premulARGB();
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);
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);
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);
700 void VSpanData::setupMatrix(const VMatrix &matrix)
702 VMatrix inv = matrix.inverted();
712 transformType = inv.type();
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;
721 void VSpanData::initTexture(const VBitmap *bitmap, int alpha,
722 const VRect &sourceRect)
724 mType = VSpanData::Type::Texture;
725 mTexture.prepare(bitmap);
726 mTexture.setClip(sourceRect);
727 mTexture.setAlpha(alpha);
731 void VSpanData::updateSpanFunc()
734 case VSpanData::Type::None:
735 mUnclippedBlendFunc = nullptr;
737 case VSpanData::Type::Solid:
738 mUnclippedBlendFunc = &blend_color;
740 case VSpanData::Type::LinearGradient:
741 case VSpanData::Type::RadialGradient: {
742 mUnclippedBlendFunc = &blend_gradient;
745 case VSpanData::Type::Texture: {
746 //@TODO update proper image function.
747 if (transformType <= VMatrix::MatrixType::Translate) {
748 mUnclippedBlendFunc = &blend_image;
750 mUnclippedBlendFunc = &blend_image_xform;
757 #if !defined(__SSE2__) && !defined(__ARM_NEON__)
758 void memfill32(uint32_t *dest, uint32_t value, int length)
760 // let compiler do the auto vectorization.
761 for (int i = 0 ; i < length; i++) {