1 #include"vdrawhelper.h"
4 #include<unordered_map>
11 struct CacheInfo : public VSpanData::Pinnable
13 inline CacheInfo(VGradientStops s):stops(s) {}
14 uint32_t buffer32[VGradient::colorTableSize];
19 typedef std::unordered_multimap<uint64_t, std::shared_ptr<const CacheInfo>> VGradientColorTableHash;
20 bool generateGradientColorTable(const VGradientStops &stops,
21 uint32_t *colorTable, int size);
22 inline const std::shared_ptr<const CacheInfo> getBuffer(const VGradient &gradient)
24 uint64_t hash_val = 0;
25 std::shared_ptr<const CacheInfo> info;
27 const VGradientStops &stops = gradient.mStops;
28 for (uint i = 0; i < stops.size() && i <= 2; i++)
29 hash_val += stops[i].second.premulARGB();
33 int count = cache.count(hash_val);
35 // key is not present in the hash
36 info = addCacheElement(hash_val, gradient);
37 } else if (count == 1) {
38 VGradientColorTableHash::const_iterator it = cache.find(hash_val);
39 if (it->second->stops == stops) {
42 // didn't find an exact match
43 info = addCacheElement(hash_val, gradient);
46 // we have a multiple data with same key
47 auto range = cache.equal_range(hash_val);
48 for (auto i = range.first; i != range.second; ++i) {
49 if (i->second->stops == stops) {
55 // didn't find an exact match
56 info = addCacheElement(hash_val, gradient);
64 inline uint maxCacheSize() const { return 60; }
65 const std::shared_ptr<const CacheInfo> addCacheElement(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(gradient.mStops, cache_entry->buffer32, VGradient::colorTableSize);
75 cache.insert(std::make_pair(hash_val, cache_entry));
79 VGradientColorTableHash cache;
80 std::mutex cacheAccess;
84 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)
117 t = (fpos - curr->first) * delta;
118 dist = (int)(255 * t);
120 colorTable[pos] = INTERPOLATE_PIXEL_255(curColor, idist, nextColor, dist);
124 curColor = nextColor;
127 for (;pos < size; ++pos)
128 colorTable[pos] = curColor;
130 // Make sure the last color stop is represented at the end of the table
131 colorTable[size-1] = curColor;
135 static VGradientCache VGradientCacheInstance;
137 void VRasterBuffer::init()
142 mCompositionMode = VPainter::CompModeSrcOver;
145 void VRasterBuffer::clear()
147 memset(mBuffer, 0, mHeight * mBytesPerLine);
150 VBitmap::Format VRasterBuffer::prepare(VBitmap *image)
152 mBuffer = (uchar *)image->bits();
153 mWidth = image->width();
154 mHeight = image->height();
156 mBytesPerLine = image->stride();
158 mFormat = image->format();
159 //drawHelper = qDrawHelper + format;
163 void VSpanData::init(VRasterBuffer* image)
165 mRasterBuffer = image;
166 mSystemClip = VRect(0,0, image->width(), image->height());
167 mType = VSpanData::Type::None;
168 mBlendFunc = nullptr;
169 mUnclippedBlendFunc = nullptr;
172 extern CompositionFunction COMP_functionForMode_C[];
173 extern CompositionFunctionSolid COMP_functionForModeSolid_C[];
174 static const CompositionFunction *functionForMode = COMP_functionForMode_C;
175 static const CompositionFunctionSolid *functionForModeSolid = COMP_functionForModeSolid_C;
178 * Gradient Draw routines
183 #define FIXPT_SIZE (1<<FIXPT_BITS)
185 getLinearGradientValues(LinearGradientValues *v, 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;
200 getRadialGradientValues(RadialGradientValues *v, 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;
216 gradientClamp(const VGradientData *grad, int ipos)
220 if (grad->mSpread == VGradient::Spread::Repeat)
222 ipos = ipos % VGradient::colorTableSize;
223 ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
225 else if (grad->mSpread == VGradient::Spread::Reflect)
227 limit = VGradient::colorTableSize * 2;
229 ipos = ipos < 0 ? limit + ipos : ipos;
230 ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
234 if (ipos < 0) ipos = 0;
235 else if (ipos >= VGradient::colorTableSize)
236 ipos = VGradient::colorTableSize - 1;
242 gradientPixelFixed(const VGradientData *grad, int fixed_pos)
244 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
246 return grad->mColorTable[gradientClamp(grad, ipos)];
249 static inline uint32_t
250 gradientPixel(const VGradientData *grad, float pos)
252 int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
254 return grad->mColorTable[gradientClamp(grad, ipos)];
258 fetch_linear_gradient(uint32_t *buffer, const Operator *op, const VSpanData *data, int y, int x, int length)
261 const VGradientData *gradient = &data->mGradient;
265 if (op->linear.l == 0) {
268 rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) + data->dx;
269 ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) + data->dy;
270 t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
271 inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
272 affine = !data->m13 && !data->m23;
275 t *= (VGradient::colorTableSize - 1);
276 inc *= (VGradient::colorTableSize - 1);
280 const uint32_t *end = buffer + length;
282 if (inc > float(-1e-5) && inc < float(1e-5)) {
283 memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)), length);
285 if (t+inc*length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
286 t+inc*length > float(INT_MIN >> (FIXPT_BITS + 1))) {
287 // we can use fixed point math
288 int t_fixed = int(t * FIXPT_SIZE);
289 int inc_fixed = int(inc * FIXPT_SIZE);
290 while (buffer < end) {
291 *buffer = gradientPixelFixed(gradient, t_fixed);
292 t_fixed += inc_fixed;
296 // we have to fall back to float math
297 while (buffer < end) {
298 *buffer = gradientPixel(gradient, t/VGradient::colorTableSize);
304 } else { // fall back to float math here as well
305 float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) + data->m33;
306 while (buffer < end) {
309 t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
311 *buffer = gradientPixel(gradient, t);
323 static inline float radialDeterminant(float a, float b, float c)
325 return (b * b) - (4 * a * c);
328 static void fetch(uint32_t *buffer, uint32_t *end,
329 const Operator *op, const VSpanData *data, float det,
330 float delta_det, float delta_delta_det, float b, float delta_b)
332 if (op->radial.extended) {
333 while (buffer < end) {
336 float w = std::sqrt(det) - b;
337 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
338 result = gradientPixel(&data->mGradient, w);
344 delta_det += delta_delta_det;
350 while (buffer < end) {
351 *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
354 delta_det += delta_delta_det;
360 void fetch_radial_gradient(uint32_t *buffer, const Operator *op, const VSpanData *data, int y, int x, int length)
362 // avoid division by zero
363 if (vIsZero(op->radial.a)) {
364 memfill32(buffer, 0, length);
368 float rx = data->m21 * (y + float(0.5))
369 + data->dx + data->m11 * (x + float(0.5));
370 float ry = data->m22 * (y + float(0.5))
371 + data->dy + data->m12 * (x + float(0.5));
372 bool affine = !data->m13 && !data->m23;
374 uint32_t *end = buffer + length;
376 rx -= data->mGradient.radial.fx;
377 ry -= data->mGradient.radial.fy;
379 float inv_a = 1 / float(2 * op->radial.a);
381 const float delta_rx = data->m11;
382 const float delta_ry = data->m12;
384 float b = 2*(op->radial.dr*data->mGradient.radial.fradius + rx * op->radial.dx + ry * op->radial.dy);
385 float delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
386 const float b_delta_b = 2 * b * delta_b;
387 const float delta_b_delta_b = 2 * delta_b * delta_b;
389 const float bb = b * b;
390 const float delta_bb = delta_b * delta_b;
395 const float rxrxryry = rx * rx + ry * ry;
396 const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
397 const float rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
398 const float delta_rx_plus_ry = 2 * delta_rxrxryry;
402 float det = (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
403 float delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
404 const float delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
406 fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b, delta_b);
408 float rw = data->m23 * (y + float(0.5))
409 + data->m33 + data->m13 * (x + float(0.5));
411 while (buffer < end) {
415 float invRw = 1 / rw;
416 float gx = rx * invRw - data->mGradient.radial.fx;
417 float gy = ry * invRw - data->mGradient.radial.fy;
418 float b = 2*(op->radial.dr*data->mGradient.radial.fradius + gx*op->radial.dx + gy*op->radial.dy);
419 float det = radialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy));
423 float detSqrt = std::sqrt(det);
425 float s0 = (-b - detSqrt) * op->radial.inv2a;
426 float s1 = (-b + detSqrt) * op->radial.inv2a;
428 float s = vMax(s0, s1);
430 if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
431 result = gradientPixel(&data->mGradient, s);
447 static inline Operator getOperator(const VSpanData *data, const VRle::Span *spans, int spanCount)
450 bool solidSource = false;
452 switch(data->mType) {
453 case VSpanData::Type::Solid:
454 solidSource = vAlpha(data->mSolid) & 0xFF;
455 op.srcFetch = nullptr;
457 case VSpanData::Type::LinearGradient:
459 getLinearGradientValues(&op.linear, data);
460 op.srcFetch = &fetch_linear_gradient;
462 case VSpanData::Type::RadialGradient:
464 getRadialGradientValues(&op.radial, data);
465 op.srcFetch = &fetch_radial_gradient;
471 op.mode = data->mRasterBuffer->mCompositionMode;
472 if (op.mode == VPainter::CompModeSrcOver && solidSource)
473 op.mode = VPainter::CompModeSrc;
475 op.funcSolid = functionForModeSolid[op.mode];
476 op.func = functionForMode[op.mode];
482 blendColorARGB(int count, const VRle::Span *spans, void *userData)
484 VSpanData *data = (VSpanData *)(userData);
485 Operator op = getOperator(data, spans, count);
486 const uint color = data->mSolid;
488 if (op.mode == VPainter::CompModeSrc) {
489 // inline for performance
491 uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
492 if (spans->coverage == 255) {
493 memfill32(target, color, spans->len);
495 uint c = BYTE_MUL(color, spans->coverage);
496 int ialpha = 255 - spans->coverage;
497 for (int i = 0; i < spans->len; ++i)
498 target[i] = c + BYTE_MUL(target[i], ialpha);
506 uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
507 op.funcSolid(target, spans->len, color, spans->coverage);
512 #define BLEND_GRADIENT_BUFFER_SIZE 2048
514 blendGradientARGB(int count, const VRle::Span *spans, void *userData)
516 VSpanData *data = (VSpanData *)(userData);
517 Operator op = getOperator(data, spans, count);
519 unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
525 uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
526 int length = spans->len;
528 int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
529 op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
530 op.func(target, buffer, l, spans->coverage);
539 VSpanData::setup(const VBrush &brush, VPainter::CompositionMode mode, int alpha)
541 switch (brush.type()) {
542 case VBrush::Type::NoBrush:
543 mType = VSpanData::Type::None;
545 case VBrush::Type::Solid:
546 mType = VSpanData::Type::Solid;
547 mSolid = brush.mColor.premulARGB();
549 case VBrush::Type::LinearGradient: {
550 mType = VSpanData::Type::LinearGradient;
551 auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
552 mGradient.mColorTable = cacheInfo->buffer32;
553 mGradient.mColorTableAlpha = cacheInfo->alpha;
554 mGradient.linear.x1 = brush.mGradient->linear.x1;
555 mGradient.linear.y1 = brush.mGradient->linear.y1;
556 mGradient.linear.x2 = brush.mGradient->linear.x2;
557 mGradient.linear.y2 = brush.mGradient->linear.y2;
558 mGradient.mSpread = brush.mGradient->mSpread;
559 setupMatrix(brush.mGradient->mMatrix);
562 case VBrush::Type::RadialGradient: {
563 mType = VSpanData::Type::RadialGradient;
564 auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
565 mGradient.mColorTable = cacheInfo->buffer32;
566 mGradient.mColorTableAlpha = cacheInfo->alpha;
567 mGradient.radial.cx = brush.mGradient->radial.cx;
568 mGradient.radial.cy = brush.mGradient->radial.cy;
569 mGradient.radial.fx = brush.mGradient->radial.fx;
570 mGradient.radial.fy = brush.mGradient->radial.fy;
571 mGradient.radial.cradius = brush.mGradient->radial.cradius;
572 mGradient.radial.fradius = brush.mGradient->radial.fradius;
573 mGradient.mSpread = brush.mGradient->mSpread;
574 setupMatrix(brush.mGradient->mMatrix);
583 void VSpanData::setupMatrix(const VMatrix &matrix)
585 VMatrix inv = matrix.inverted();
596 //const bool affine = inv.isAffine();
597 // fast_matrix = affine
598 // && m11 * m11 + m21 * m21 < 1e4
599 // && m12 * m12 + m22 * m22 < 1e4
601 // && fabs(dy) < 1e4;
605 VSpanData::updateSpanFunc()
608 case VSpanData::Type::None:
609 mUnclippedBlendFunc = nullptr;
611 case VSpanData::Type::Solid:
612 mUnclippedBlendFunc = &blendColorARGB;
614 case VSpanData::Type::LinearGradient:
615 case VSpanData::Type::RadialGradient: {
616 mUnclippedBlendFunc = &blendGradientARGB;
624 #if !defined(__SSE2__) && !defined(__ARM_NEON__)
626 memfill32(uint32_t *dest, uint32_t value, int length)
633 // Cute hack to align future memcopy operation
634 // and do unroll the loop a bit. Not sure it is
635 // the most efficient, but will do for now.
636 n = (length + 7) / 8;
637 switch (length & 0x07)
639 case 0: do { *dest++ = value;
641 case 7: *dest++ = value;
643 case 6: *dest++ = value;
645 case 5: *dest++ = value;
647 case 4: *dest++ = value;
649 case 3: *dest++ = value;
651 case 2: *dest++ = value;
653 case 1: *dest++ = value;
659 void vInitDrawhelperFunctions()
661 vInitBlendFunctions();
663 #if defined(__ARM_NEON__)
664 // update fast path for NEON
665 extern void comp_func_solid_SourceOver_neon(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
666 extern void comp_func_solid_Source_neon(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
668 COMP_functionForModeSolid_C[VPainter::CompModeSrc] = comp_func_solid_Source_neon;
669 COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] = comp_func_solid_SourceOver_neon;
672 #if defined(__SSE2__)
673 // update fast path for SSE2
674 extern void comp_func_solid_SourceOver_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
675 extern void comp_func_solid_Source_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
676 extern void comp_func_Source_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha);
677 extern void comp_func_SourceOver_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha);
679 COMP_functionForModeSolid_C[VPainter::CompModeSrc] = comp_func_solid_Source_sse2;
680 COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] = comp_func_solid_SourceOver_sse2;
682 COMP_functionForMode_C[VPainter::CompModeSrc] = comp_func_Source_sse2;
683 COMP_functionForMode_C[VPainter::CompModeSrcOver] = comp_func_SourceOver_sse2;
687 V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)