diff options
author | Florin Malita <fmalita@chromium.org> | 2017-08-28 12:50:26 -0400 |
---|---|---|
committer | Florin Malita <fmalita@chromium.org> | 2017-08-29 17:10:36 +0000 |
commit | aa0ce825b877871f73532beb6fde6bf2f80e99dd (patch) | |
tree | 6cc4c265c8ccb84e1408b5b9cfe6068e9abc6fec | |
parent | 9d7e57d509149dd2fcb3ba73ea8f4cdce11f84bd (diff) |
Use the 4f impl for legacy/8888 linear gradients
This is a partial revert of https://skia-review.googlesource.com/c/20280,
adding back L32 support for 4f gradients and switching off the legacy
impl.
If it sticks, we should be able to completely delete the legacy gradient
code.
Change-Id: Iaa6d722ea4dfd8e4d959bb76eca815a30ca7098f
Reviewed-on: https://skia-review.googlesource.com/31425
Reviewed-by: Herb Derby <herb@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
-rw-r--r-- | src/shaders/gradients/Sk4fGradientBase.cpp | 9 | ||||
-rw-r--r-- | src/shaders/gradients/Sk4fGradientBase.h | 3 | ||||
-rw-r--r-- | src/shaders/gradients/Sk4fGradientPriv.h | 71 | ||||
-rw-r--r-- | src/shaders/gradients/Sk4fLinearGradient.cpp | 125 | ||||
-rw-r--r-- | src/shaders/gradients/Sk4fLinearGradient.h | 11 | ||||
-rw-r--r-- | src/shaders/gradients/SkLinearGradient.cpp | 4 |
6 files changed, 165 insertions, 58 deletions
diff --git a/src/shaders/gradients/Sk4fGradientBase.cpp b/src/shaders/gradients/Sk4fGradientBase.cpp index 0d9e6d0836..60f8ced4b0 100644 --- a/src/shaders/gradients/Sk4fGradientBase.cpp +++ b/src/shaders/gradients/Sk4fGradientBase.cpp @@ -288,9 +288,8 @@ GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderB const ContextRec& rec) : INHERITED(shader, rec) , fFlags(this->INHERITED::getFlags()) + , fDither(rec.fPaint->isDither()) { - SkASSERT(rec.fPreferredDstType == ContextRec::kPM4f_DstType); - const SkMatrix& inverse = this->getTotalInverse(); fDstToPos.setConcat(shader.fPtsToUnit, inverse); SkASSERT(!fDstToPos.hasPerspective()); @@ -309,9 +308,3 @@ bool SkGradientShaderBase:: GradientShaderBase4fContext::isValid() const { return fDstToPos.isFinite(); } - -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { - // This impl only shades to 4f. - SkASSERT(false); -} diff --git a/src/shaders/gradients/Sk4fGradientBase.h b/src/shaders/gradients/Sk4fGradientBase.h index 629b0650f6..b2c2b3762c 100644 --- a/src/shaders/gradients/Sk4fGradientBase.h +++ b/src/shaders/gradients/Sk4fGradientBase.h @@ -60,8 +60,6 @@ public: uint32_t getFlags() const override { return fFlags; } - void shadeSpan(int x, int y, SkPMColor dst[], int count) final; - bool isValid() const; protected: @@ -70,6 +68,7 @@ protected: SkMatrix::MapXYProc fDstToPosProc; uint8_t fFlags; bool fColorsArePremul; + bool fDither; private: using INHERITED = Context; diff --git a/src/shaders/gradients/Sk4fGradientPriv.h b/src/shaders/gradients/Sk4fGradientPriv.h index 138bd170c9..2a654ba96e 100644 --- a/src/shaders/gradients/Sk4fGradientPriv.h +++ b/src/shaders/gradients/Sk4fGradientPriv.h @@ -51,15 +51,66 @@ struct PremulTraits<ApplyPremul::True> { // // - store4x() Store 4 Sk4f values to dest (opportunistic optimization). // +template <typename dst, ApplyPremul premul> +struct DstTraits; + +template <ApplyPremul premul> +struct DstTraits<SkPMColor, premul> { + using PM = PremulTraits<premul>; + + // For L32, prescaling by 255 saves a per-pixel multiplication when premul is not needed. + static Sk4f load(const SkPM4f& c) { + return premul == ApplyPremul::False + ? c.to4f_pmorder() * Sk4f(255) + : c.to4f_pmorder(); + } + + static void store(const Sk4f& c, SkPMColor* dst, const Sk4f& bias) { + if (premul == ApplyPremul::False) { + // c is pre-scaled by 255 and pre-biased, just store. + SkNx_cast<uint8_t>(c).store(dst); + } else { + *dst = Sk4f_toL32(PM::apply(c) + bias); + } + } + + static void store(const Sk4f& c, SkPMColor* dst, int n) { + SkPMColor pmc; + store(c, &pmc, Sk4f(0)); + sk_memset32(dst, pmc, n); + } + + static void store4x(const Sk4f& c0, const Sk4f& c1, + const Sk4f& c2, const Sk4f& c3, + SkPMColor* dst, + const Sk4f& bias0, + const Sk4f& bias1) { + if (premul == ApplyPremul::False) { + // colors are pre-scaled and pre-biased. + Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3); + } else { + store(c0, dst + 0, bias0); + store(c1, dst + 1, bias1); + store(c2, dst + 2, bias0); + store(c3, dst + 3, bias1); + } + } + + static Sk4f pre_lerp_bias(const Sk4f& bias) { + // We can apply the bias before interpolation when the colors are premultiplied. + return premul == ApplyPremul::False ? bias : 0; + } +}; + template <ApplyPremul premul> -struct DstTraits { +struct DstTraits<SkPM4f, premul> { using PM = PremulTraits<premul>; static Sk4f load(const SkPM4f& c) { return c.to4f(); } - static void store(const Sk4f& c, SkPM4f* dst) { + static void store(const Sk4f& c, SkPM4f* dst, const Sk4f& /*bias*/) { PM::apply(c).store(dst->fVec); } @@ -72,11 +123,17 @@ struct DstTraits { static void store4x(const Sk4f& c0, const Sk4f& c1, const Sk4f& c2, const Sk4f& c3, - SkPM4f* dst) { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); + SkPM4f* dst, + const Sk4f& bias0, const Sk4f& bias1) { + store(c0, dst + 0, bias0); + store(c1, dst + 1, bias1); + store(c2, dst + 2, bias0); + store(c3, dst + 3, bias1); + } + + static Sk4f pre_lerp_bias(const Sk4f& /*bias*/) { + // For 4f dests we never bias. + return 0; } }; diff --git a/src/shaders/gradients/Sk4fLinearGradient.cpp b/src/shaders/gradients/Sk4fLinearGradient.cpp index 700b7fb13e..11bf96d98f 100644 --- a/src/shaders/gradients/Sk4fLinearGradient.cpp +++ b/src/shaders/gradients/Sk4fLinearGradient.cpp @@ -12,20 +12,21 @@ namespace { -template<ApplyPremul premul> -void ramp(const Sk4f& c, const Sk4f& dc, SkPM4f dst[], int n) { +template<typename dstType, ApplyPremul premul> +void ramp(const Sk4f& c, const Sk4f& dc, dstType dst[], int n, + const Sk4f& bias0, const Sk4f& bias1) { SkASSERT(n > 0); - const Sk4f dc2 = dc + dc; - const Sk4f dc4 = dc2 + dc2; + const Sk4f dc2 = dc + dc, + dc4 = dc2 + dc2; - Sk4f c0 = c ; - Sk4f c1 = c + dc; - Sk4f c2 = c0 + dc2; - Sk4f c3 = c1 + dc2; + Sk4f c0 = c + DstTraits<dstType, premul>::pre_lerp_bias(bias0), + c1 = c + dc + DstTraits<dstType, premul>::pre_lerp_bias(bias1), + c2 = c0 + dc2, + c3 = c1 + dc2; while (n >= 4) { - DstTraits<premul>::store4x(c0, c1, c2, c3, dst); + DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst, bias0, bias1); dst += 4; c0 = c0 + dc4; @@ -35,12 +36,12 @@ void ramp(const Sk4f& c, const Sk4f& dc, SkPM4f dst[], int n) { n -= 4; } if (n & 2) { - DstTraits<premul>::store(c0, dst++); - DstTraits<premul>::store(c1, dst++); + DstTraits<dstType, premul>::store(c0, dst++, bias0); + DstTraits<dstType, premul>::store(c1, dst++, bias1); c0 = c0 + dc2; } if (n & 1) { - DstTraits<premul>::store(c0, dst); + DstTraits<dstType, premul>::store(c0, dst, bias0); } } @@ -141,37 +142,81 @@ SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const { } } + +void SkLinearGradient:: +LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { + SkASSERT(count > 0); + + float bias0 = 0, + bias1 = 0; + + if (fDither) { + static constexpr float dither_cell[] = { + -3/8.0f, 1/8.0f, + 3/8.0f, -1/8.0f, + }; + + const int rowIndex = (y & 1) << 1; + bias0 = dither_cell[rowIndex + 0]; + bias1 = dither_cell[rowIndex + 1]; + + if (x & 1) { + SkTSwap(bias0, bias1); + } + } + + if (fColorsArePremul) { + // In premul interpolation mode, components are pre-scaled by 255 and the store + // op is truncating. We pre-bias here to achieve rounding. + bias0 += 0.5f; + bias1 += 0.5f; + + this->shadePremulSpan<SkPMColor, ApplyPremul::False>(x, y, dst, count, bias0, bias1); + } else { + // In unpremul interpolation mode, Components are not pre-scaled. + bias0 *= 1/255.0f; + bias1 *= 1/255.0f; + + this->shadePremulSpan<SkPMColor, ApplyPremul::True >(x, y, dst, count, bias0, bias1); + } +} + void SkLinearGradient:: LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { SkASSERT(count > 0); + + // 4f dests are dithered at a later stage, if needed. + static constexpr float bias0 = 0, + bias1 = 0; if (fColorsArePremul) { - this->shadePremulSpan<ApplyPremul::False>(x, y, dst, count); + this->shadePremulSpan<SkPM4f, ApplyPremul::False>(x, y, dst, count, bias0, bias1); } else { - this->shadePremulSpan<ApplyPremul::True>(x, y, dst, count); + this->shadePremulSpan<SkPM4f, ApplyPremul::True >(x, y, dst, count, bias0, bias1); } } -template<ApplyPremul premul> +template<typename dstType, ApplyPremul premul> void SkLinearGradient:: -LinearGradient4fContext::shadePremulSpan(int x, int y, SkPM4f dst[], int count) const { - const SkLinearGradient& shader = - static_cast<const SkLinearGradient&>(fShader); +LinearGradient4fContext::shadePremulSpan(int x, int y, dstType dst[], int count, + float bias0, float bias1) const { + const SkLinearGradient& shader = static_cast<const SkLinearGradient&>(fShader); switch (shader.fTileMode) { case kClamp_TileMode: - this->shadeSpanInternal<premul, kClamp_TileMode>(x, y, dst, count); + this->shadeSpanInternal<dstType, premul, kClamp_TileMode >(x, y, dst, count, bias0, bias1); break; case kRepeat_TileMode: - this->shadeSpanInternal<premul, kRepeat_TileMode>(x, y, dst, count); + this->shadeSpanInternal<dstType, premul, kRepeat_TileMode>(x, y, dst, count, bias0, bias1); break; case kMirror_TileMode: - this->shadeSpanInternal<premul, kMirror_TileMode>(x, y, dst, count); + this->shadeSpanInternal<dstType, premul, kMirror_TileMode>(x, y, dst, count, bias0, bias1); break; } } -template<ApplyPremul premul, SkShader::TileMode tileMode> +template<typename dstType, ApplyPremul premul, SkShader::TileMode tileMode> void SkLinearGradient:: -LinearGradient4fContext::shadeSpanInternal(int x, int y, SkPM4f dst[], int count) const { +LinearGradient4fContext::shadeSpanInternal(int x, int y, dstType dst[], int count, + float bias0, float bias1) const { SkPoint pt; fDstToPosProc(fDstToPos, x + SK_ScalarHalf, @@ -179,12 +224,15 @@ LinearGradient4fContext::shadeSpanInternal(int x, int y, SkPM4f dst[], int count &pt); const SkScalar fx = pinFx<tileMode>(pt.x()); const SkScalar dx = fDstToPos.getScaleX(); - LinearIntervalProcessor<premul, tileMode> proc(fIntervals->begin(), - fIntervals->end() - 1, - this->findInterval(fx), - fx, - dx, - SkScalarNearlyZero(dx * count)); + LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(), + fIntervals->end() - 1, + this->findInterval(fx), + fx, + dx, + SkScalarNearlyZero(dx * count)); + Sk4f bias4f0(bias0), + bias4f1(bias1); + while (count > 0) { // What we really want here is SkTPin(advance, 1, count) // but that's a significant perf hit for >> stops; investigate. @@ -199,18 +247,23 @@ LinearGradient4fContext::shadeSpanInternal(int x, int y, SkPM4f dst[], int count || (n == count && proc.currentRampIsZero())); if (proc.currentRampIsZero()) { - DstTraits<premul>::store(proc.currentColor(), dst, n); + DstTraits<dstType, premul>::store(proc.currentColor(), dst, n); } else { - ramp<premul>(proc.currentColor(), proc.currentColorGrad(), dst, n); + ramp<dstType, premul>(proc.currentColor(), proc.currentColorGrad(), dst, n, + bias4f0, bias4f1); } proc.advance(SkIntToScalar(n)); count -= n; dst += n; + + if (n & 1) { + SkTSwap(bias4f0, bias4f1); + } } } -template<ApplyPremul premul, SkShader::TileMode tileMode> +template<typename dstType, ApplyPremul premul, SkShader::TileMode tileMode> class SkLinearGradient:: LinearGradient4fContext::LinearIntervalProcessor { public: @@ -275,8 +328,8 @@ private: void compute_interval_props(SkScalar t) { SkASSERT(in_range(t, fInterval->fT0, fInterval->fT1)); - const Sk4f dc = DstTraits<premul>::load(fInterval->fCg); - fCc = DstTraits<premul>::load(fInterval->fCb) + dc * Sk4f(t); + const Sk4f dc = DstTraits<dstType, premul>::load(fInterval->fCg); + fCc = DstTraits<dstType, premul>::load(fInterval->fCb) + dc * Sk4f(t); fDcDx = dc * fDx; fZeroRamp = fIsVertical || (dc == 0).allTrue(); } @@ -295,8 +348,8 @@ private: // // Avg += C * (t1 - t0) // - const auto c = DstTraits<premul>::load(i->fCb) - + DstTraits<premul>::load(i->fCg) * (i->fT0 + i->fT1) * 0.5f; + const auto c = DstTraits<dstType, premul>::load(i->fCb) + + DstTraits<dstType, premul>::load(i->fCg) * (i->fT0 + i->fT1) * 0.5f; fCc = fCc + c * (i->fT1 - i->fT0); } } diff --git a/src/shaders/gradients/Sk4fLinearGradient.h b/src/shaders/gradients/Sk4fLinearGradient.h index 11606e1c80..27c14e0637 100644 --- a/src/shaders/gradients/Sk4fLinearGradient.h +++ b/src/shaders/gradients/Sk4fLinearGradient.h @@ -16,19 +16,20 @@ LinearGradient4fContext final : public GradientShaderBase4fContext { public: LinearGradient4fContext(const SkLinearGradient&, const ContextRec&); + void shadeSpan(int x, int y, SkPMColor dst[], int count) override; void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override; private: using INHERITED = GradientShaderBase4fContext; - template<ApplyPremul, TileMode> + template<typename dstType, ApplyPremul, TileMode> class LinearIntervalProcessor; - template <ApplyPremul premul> - void shadePremulSpan(int x, int y, SkPM4f[], int count) const; + template <typename dstType, ApplyPremul premul> + void shadePremulSpan(int x, int y, dstType dst[], int count, float bias0, float bias1) const; - template <ApplyPremul premul, SkShader::TileMode tileMode> - void shadeSpanInternal(int x, int y, SkPM4f[], int count) const; + template <typename dstType, ApplyPremul premul, SkShader::TileMode tileMode> + void shadeSpanInternal(int x, int y, dstType dst[], int count, float bias0, float bias1) const; const Sk4fGradientInterval* findInterval(SkScalar fx) const; diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp index 551368b94d..4d3213b47b 100644 --- a/src/shaders/gradients/SkLinearGradient.cpp +++ b/src/shaders/gradients/SkLinearGradient.cpp @@ -66,9 +66,13 @@ void SkLinearGradient::flatten(SkWriteBuffer& buffer) const { SkShaderBase::Context* SkLinearGradient::onMakeContext( const ContextRec& rec, SkArenaAlloc* alloc) const { +#ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT + return CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec); +#else return rec.fPreferredDstType == ContextRec::kPM4f_DstType ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec) : CheckedMakeContext< LinearGradientContext>(alloc, *this, rec); +#endif } SkShaderBase::Context* SkLinearGradient::onMakeBurstPipelineContext( |