1 #include "vdrawhelper.h"
5 #include <unordered_map>
9 struct CacheInfo : public VSpanData::Pinnable {
10 inline CacheInfo(VGradientStops s) : stops(s) {}
11 uint32_t buffer32[VGradient::colorTableSize];
16 typedef std::unordered_multimap<uint64_t, std::shared_ptr<const CacheInfo>>
17 VGradientColorTableHash;
18 bool generateGradientColorTable(const VGradientStops &stops,
19 uint32_t *colorTable, int size);
20 inline const std::shared_ptr<const CacheInfo> getBuffer(
21 const VGradient &gradient)
23 uint64_t hash_val = 0;
24 std::shared_ptr<const CacheInfo> info;
26 const VGradientStops &stops = gradient.mStops;
27 for (uint i = 0; i < stops.size() && i <= 2; i++)
28 hash_val += stops[i].second.premulARGB();
32 int count = cache.count(hash_val);
34 // key is not present in the hash
35 info = addCacheElement(hash_val, gradient);
36 } else if (count == 1) {
37 VGradientColorTableHash::const_iterator it = cache.find(hash_val);
38 if (it->second->stops == stops) {
41 // didn't find an exact match
42 info = addCacheElement(hash_val, gradient);
45 // we have a multiple data with same key
46 auto range = cache.equal_range(hash_val);
47 for (auto i = range.first; i != range.second; ++i) {
48 if (i->second->stops == stops) {
54 // didn't find an exact match
55 info = addCacheElement(hash_val, gradient);
63 inline uint maxCacheSize() const { return 60; }
64 const std::shared_ptr<const CacheInfo> addCacheElement(
65 uint64_t hash_val, const VGradient &gradient)
67 if (cache.size() == maxCacheSize()) {
68 int count = rand() % maxCacheSize();
70 cache.erase(cache.begin());
73 auto cache_entry = std::make_shared<CacheInfo>(gradient.mStops);
74 cache_entry->alpha = generateGradientColorTable(
75 gradient.mStops, cache_entry->buffer32, VGradient::colorTableSize);
76 cache.insert(std::make_pair(hash_val, cache_entry));
80 VGradientColorTableHash cache;
81 std::mutex cacheAccess;
84 bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
85 uint32_t *colorTable, int size)
87 int dist, idist, pos = 0, i;
89 int stopCount = stops.size();
90 const VGradientStop *curr, *next, *start;
91 uint32_t curColor, nextColor;
92 float delta, t, incr, fpos;
96 if (!curr->second.isOpaque()) alpha = true;
97 curColor = curr->second.premulARGB();
98 incr = 1.0 / (float)size;
101 colorTable[pos++] = curColor;
103 while (fpos <= curr->first) {
104 colorTable[pos] = colorTable[pos - 1];
109 for (i = 0; i < stopCount - 1; ++i) {
111 next = (start + i + 1);
112 delta = 1 / (next->first - curr->first);
113 if (!next->second.isOpaque()) alpha = true;
114 nextColor = next->second.premulARGB();
115 while (fpos < next->first && pos < size) {
116 t = (fpos - curr->first) * delta;
117 dist = (int)(255 * t);
120 INTERPOLATE_PIXEL_255(curColor, idist, nextColor, dist);
124 curColor = nextColor;
127 for (; pos < size; ++pos) colorTable[pos] = curColor;
129 // Make sure the last color stop is represented at the end of the table
130 colorTable[size - 1] = curColor;
134 static VGradientCache VGradientCacheInstance;
136 void VRasterBuffer::init()
141 mCompositionMode = VPainter::CompModeSrcOver;
144 void VRasterBuffer::clear()
146 memset(mBuffer, 0, mHeight * mBytesPerLine);
149 VBitmap::Format VRasterBuffer::prepare(VBitmap *image)
151 mBuffer = (uchar *)image->bits();
152 mWidth = image->width();
153 mHeight = image->height();
155 mBytesPerLine = image->stride();
157 mFormat = image->format();
158 // drawHelper = qDrawHelper + format;
162 void VSpanData::init(VRasterBuffer *image)
164 mRasterBuffer = image;
165 mSystemClip = VRect(0, 0, image->width(), image->height());
166 mType = VSpanData::Type::None;
167 mBlendFunc = nullptr;
168 mUnclippedBlendFunc = nullptr;
171 extern CompositionFunction COMP_functionForMode_C[];
172 extern CompositionFunctionSolid COMP_functionForModeSolid_C[];
173 static const CompositionFunction * functionForMode = COMP_functionForMode_C;
174 static const CompositionFunctionSolid *functionForModeSolid =
175 COMP_functionForModeSolid_C;
178 * Gradient Draw routines
183 #define FIXPT_SIZE (1 << FIXPT_BITS)
184 static inline void getLinearGradientValues(LinearGradientValues *v,
185 const VSpanData * data)
187 const VGradientData *grad = &data->mGradient;
188 v->dx = grad->linear.x2 - grad->linear.x1;
189 v->dy = grad->linear.y2 - grad->linear.y1;
190 v->l = v->dx * v->dx + v->dy * v->dy;
195 v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
199 static inline void getRadialGradientValues(RadialGradientValues *v,
200 const VSpanData * data)
202 const VGradientData &gradient = data->mGradient;
203 v->dx = gradient.radial.cx - gradient.radial.fx;
204 v->dy = gradient.radial.cy - gradient.radial.fy;
206 v->dr = gradient.radial.cradius - gradient.radial.fradius;
207 v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
209 v->a = v->dr * v->dr - v->dx * v->dx - v->dy * v->dy;
210 v->inv2a = 1 / (2 * v->a);
212 v->extended = !vIsZero(gradient.radial.fradius) || v->a <= 0;
215 static inline int gradientClamp(const VGradientData *grad, int ipos)
219 if (grad->mSpread == VGradient::Spread::Repeat) {
220 ipos = ipos % VGradient::colorTableSize;
221 ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
222 } else if (grad->mSpread == VGradient::Spread::Reflect) {
223 limit = VGradient::colorTableSize * 2;
225 ipos = ipos < 0 ? limit + ipos : ipos;
226 ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
230 else if (ipos >= VGradient::colorTableSize)
231 ipos = VGradient::colorTableSize - 1;
236 static uint32_t gradientPixelFixed(const VGradientData *grad, int fixed_pos)
238 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
240 return grad->mColorTable[gradientClamp(grad, ipos)];
243 static inline uint32_t gradientPixel(const VGradientData *grad, float pos)
245 int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
247 return grad->mColorTable[gradientClamp(grad, ipos)];
250 void fetch_linear_gradient(uint32_t *buffer, const Operator *op,
251 const VSpanData *data, int y, int x, int length)
254 const VGradientData *gradient = &data->mGradient;
257 float rx = 0, ry = 0;
258 if (op->linear.l == 0) {
261 rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) +
263 ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) +
265 t = op->linear.dx * rx + op->linear.dy * ry + op->linear.off;
266 inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
267 affine = !data->m13 && !data->m23;
270 t *= (VGradient::colorTableSize - 1);
271 inc *= (VGradient::colorTableSize - 1);
275 const uint32_t *end = buffer + length;
277 if (inc > float(-1e-5) && inc < float(1e-5)) {
278 memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)),
281 if (t + inc * length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
282 t + inc * length > float(INT_MIN >> (FIXPT_BITS + 1))) {
283 // we can use fixed point math
284 int t_fixed = int(t * FIXPT_SIZE);
285 int inc_fixed = int(inc * FIXPT_SIZE);
286 while (buffer < end) {
287 *buffer = gradientPixelFixed(gradient, t_fixed);
288 t_fixed += inc_fixed;
292 // we have to fall back to float math
293 while (buffer < end) {
295 gradientPixel(gradient, t / VGradient::colorTableSize);
301 } else { // fall back to float math here as well
302 float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) +
304 while (buffer < end) {
307 t = (op->linear.dx * x + op->linear.dy * y) + op->linear.off;
309 *buffer = gradientPixel(gradient, t);
321 static inline float radialDeterminant(float a, float b, float c)
323 return (b * b) - (4 * a * c);
326 static void fetch(uint32_t *buffer, uint32_t *end, const Operator *op,
327 const VSpanData *data, float det, float delta_det,
328 float delta_delta_det, float b, float delta_b)
330 if (op->radial.extended) {
331 while (buffer < end) {
334 float w = std::sqrt(det) - b;
335 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
336 result = gradientPixel(&data->mGradient, w);
342 delta_det += delta_delta_det;
348 while (buffer < end) {
349 *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
352 delta_det += delta_delta_det;
358 void fetch_radial_gradient(uint32_t *buffer, const Operator *op,
359 const VSpanData *data, int y, int x, int length)
361 // avoid division by zero
362 if (vIsZero(op->radial.a)) {
363 memfill32(buffer, 0, length);
368 data->m21 * (y + float(0.5)) + data->dx + data->m11 * (x + float(0.5));
370 data->m22 * (y + float(0.5)) + data->dy + data->m12 * (x + float(0.5));
371 bool affine = !data->m13 && !data->m23;
373 uint32_t *end = buffer + length;
375 rx -= data->mGradient.radial.fx;
376 ry -= data->mGradient.radial.fy;
378 float inv_a = 1 / float(2 * op->radial.a);
380 const float delta_rx = data->m11;
381 const float delta_ry = data->m12;
383 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
384 rx * op->radial.dx + ry * op->radial.dy);
386 2 * (delta_rx * op->radial.dx + delta_ry * op->radial.dy);
387 const float b_delta_b = 2 * b * delta_b;
388 const float delta_b_delta_b = 2 * delta_b * delta_b;
390 const float bb = b * b;
391 const float delta_bb = delta_b * delta_b;
396 const float rxrxryry = rx * rx + ry * ry;
397 const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
398 const float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
399 const float delta_rx_plus_ry = 2 * delta_rxrxryry;
404 (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
405 float delta_det = (b_delta_b + delta_bb +
406 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) *
408 const float delta_delta_det =
409 (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
411 fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b,
414 float rw = data->m23 * (y + float(0.5)) + data->m33 +
415 data->m13 * (x + float(0.5));
417 while (buffer < end) {
421 float invRw = 1 / rw;
422 float gx = rx * invRw - data->mGradient.radial.fx;
423 float gy = ry * invRw - data->mGradient.radial.fy;
424 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
425 gx * op->radial.dx + gy * op->radial.dy);
426 float det = radialDeterminant(
427 op->radial.a, b, op->radial.sqrfr - (gx * gx + gy * gy));
431 float detSqrt = std::sqrt(det);
433 float s0 = (-b - detSqrt) * op->radial.inv2a;
434 float s1 = (-b + detSqrt) * op->radial.inv2a;
436 float s = vMax(s0, s1);
438 if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
439 result = gradientPixel(&data->mGradient, s);
454 static inline Operator getOperator(const VSpanData * data,
455 const VRle::Span *spans, int spanCount)
458 bool solidSource = false;
460 switch (data->mType) {
461 case VSpanData::Type::Solid:
462 solidSource = vAlpha(data->mSolid) & 0xFF;
463 op.srcFetch = nullptr;
465 case VSpanData::Type::LinearGradient:
467 getLinearGradientValues(&op.linear, data);
468 op.srcFetch = &fetch_linear_gradient;
470 case VSpanData::Type::RadialGradient:
472 getRadialGradientValues(&op.radial, data);
473 op.srcFetch = &fetch_radial_gradient;
476 op.srcFetch = nullptr;
480 op.mode = data->mRasterBuffer->mCompositionMode;
481 if (op.mode == VPainter::CompModeSrcOver && solidSource)
482 op.mode = VPainter::CompModeSrc;
484 op.funcSolid = functionForModeSolid[op.mode];
485 op.func = functionForMode[op.mode];
490 static void blendColorARGB(int count, const VRle::Span *spans, void *userData)
492 VSpanData *data = (VSpanData *)(userData);
493 Operator op = getOperator(data, spans, count);
494 const uint color = data->mSolid;
496 if (op.mode == VPainter::CompModeSrc) {
497 // inline for performance
500 ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
501 if (spans->coverage == 255) {
502 memfill32(target, color, spans->len);
504 uint c = BYTE_MUL(color, spans->coverage);
505 int ialpha = 255 - spans->coverage;
506 for (int i = 0; i < spans->len; ++i)
507 target[i] = c + BYTE_MUL(target[i], ialpha);
516 ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
517 op.funcSolid(target, spans->len, color, spans->coverage);
522 #define BLEND_GRADIENT_BUFFER_SIZE 2048
523 static void blendGradientARGB(int count, const VRle::Span *spans,
526 VSpanData *data = (VSpanData *)(userData);
527 Operator op = getOperator(data, spans, count);
529 unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
531 if (!op.srcFetch) return;
535 ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
536 int length = spans->len;
538 int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
539 op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
540 op.func(target, buffer, l, spans->coverage);
548 void VSpanData::setup(const VBrush &brush, VPainter::CompositionMode mode,
551 switch (brush.type()) {
552 case VBrush::Type::NoBrush:
553 mType = VSpanData::Type::None;
555 case VBrush::Type::Solid:
556 mType = VSpanData::Type::Solid;
557 mSolid = brush.mColor.premulARGB();
559 case VBrush::Type::LinearGradient: {
560 mType = VSpanData::Type::LinearGradient;
561 auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
562 mGradient.mColorTable = cacheInfo->buffer32;
563 mGradient.mColorTableAlpha = cacheInfo->alpha;
564 mGradient.linear.x1 = brush.mGradient->linear.x1;
565 mGradient.linear.y1 = brush.mGradient->linear.y1;
566 mGradient.linear.x2 = brush.mGradient->linear.x2;
567 mGradient.linear.y2 = brush.mGradient->linear.y2;
568 mGradient.mSpread = brush.mGradient->mSpread;
569 setupMatrix(brush.mGradient->mMatrix);
572 case VBrush::Type::RadialGradient: {
573 mType = VSpanData::Type::RadialGradient;
574 auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
575 mGradient.mColorTable = cacheInfo->buffer32;
576 mGradient.mColorTableAlpha = cacheInfo->alpha;
577 mGradient.radial.cx = brush.mGradient->radial.cx;
578 mGradient.radial.cy = brush.mGradient->radial.cy;
579 mGradient.radial.fx = brush.mGradient->radial.fx;
580 mGradient.radial.fy = brush.mGradient->radial.fy;
581 mGradient.radial.cradius = brush.mGradient->radial.cradius;
582 mGradient.radial.fradius = brush.mGradient->radial.fradius;
583 mGradient.mSpread = brush.mGradient->mSpread;
584 setupMatrix(brush.mGradient->mMatrix);
593 void VSpanData::setupMatrix(const VMatrix &matrix)
595 VMatrix inv = matrix.inverted();
606 // const bool affine = inv.isAffine();
607 // fast_matrix = affine
608 // && m11 * m11 + m21 * m21 < 1e4
609 // && m12 * m12 + m22 * m22 < 1e4
611 // && fabs(dy) < 1e4;
614 void VSpanData::updateSpanFunc()
617 case VSpanData::Type::None:
618 mUnclippedBlendFunc = nullptr;
620 case VSpanData::Type::Solid:
621 mUnclippedBlendFunc = &blendColorARGB;
623 case VSpanData::Type::LinearGradient:
624 case VSpanData::Type::RadialGradient: {
625 mUnclippedBlendFunc = &blendGradientARGB;
633 #if !defined(__SSE2__)
634 void memfill32(uint32_t *dest, uint32_t value, int length)
638 if (length <= 0) return;
640 // Cute hack to align future memcopy operation
641 // and do unroll the loop a bit. Not sure it is
642 // the most efficient, but will do for now.
643 n = (length + 7) / 8;
644 switch (length & 0x07) {
674 void vInitDrawhelperFunctions()
676 vInitBlendFunctions();
678 #if defined(__ARM_NEON__)
679 // update fast path for NEON
680 // extern void comp_func_solid_SourceOver_neon(
681 // uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
682 // extern void comp_func_solid_Source_neon(
683 // uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
685 // COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
686 // comp_func_solid_Source_neon;
687 // COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
688 // comp_func_solid_SourceOver_neon;
691 #if defined(__SSE2__)
692 // update fast path for SSE2
693 extern void comp_func_solid_SourceOver_sse2(
694 uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
695 extern void comp_func_solid_Source_sse2(
696 uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
697 extern void comp_func_Source_sse2(uint32_t * dest, const uint32_t *src,
698 int length, uint32_t const_alpha);
699 extern void comp_func_SourceOver_sse2(uint32_t * dest, const uint32_t *src,
700 int length, uint32_t const_alpha);
702 COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
703 comp_func_solid_Source_sse2;
704 COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
705 comp_func_solid_SourceOver_sse2;
707 COMP_functionForMode_C[VPainter::CompModeSrc] = comp_func_Source_sse2;
708 // COMP_functionForMode_C[VPainter::CompModeSrcOver] =
709 // comp_func_SourceOver_sse2;
713 V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)