aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2017-08-28 12:50:26 -0400
committerGravatar Florin Malita <fmalita@chromium.org>2017-08-29 17:10:36 +0000
commitaa0ce825b877871f73532beb6fde6bf2f80e99dd (patch)
tree6cc4c265c8ccb84e1408b5b9cfe6068e9abc6fec
parent9d7e57d509149dd2fcb3ba73ea8f4cdce11f84bd (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.cpp9
-rw-r--r--src/shaders/gradients/Sk4fGradientBase.h3
-rw-r--r--src/shaders/gradients/Sk4fGradientPriv.h71
-rw-r--r--src/shaders/gradients/Sk4fLinearGradient.cpp125
-rw-r--r--src/shaders/gradients/Sk4fLinearGradient.h11
-rw-r--r--src/shaders/gradients/SkLinearGradient.cpp4
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(