diff options
author | 2017-03-23 17:04:54 -0400 | |
---|---|---|
committer | 2017-03-23 23:06:07 +0000 | |
commit | da4545bfc58f8ec19df79f6ae75b7231477973d8 (patch) | |
tree | 210cc1abc6bbaedf81b5305109ec25dc9c37cb78 | |
parent | 4dc6474b73ec4e5d6a1a0070e50d3d6766e4c94a (diff) |
Extract 4f gradient interval functionality
... into structures usable outside Sk4fGradient classes.
Change-Id: Ifffdbe8bafa4f027f2016ce71eefede6034dd3ae
Reviewed-on: https://skia-review.googlesource.com/10060
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
-rw-r--r-- | src/effects/gradients/Sk4fGradientBase.cpp | 279 | ||||
-rw-r--r-- | src/effects/gradients/Sk4fGradientBase.h | 67 | ||||
-rw-r--r-- | src/effects/gradients/Sk4fLinearGradient.cpp | 55 | ||||
-rw-r--r-- | src/effects/gradients/Sk4fLinearGradient.h | 4 |
4 files changed, 205 insertions, 200 deletions
diff --git a/src/effects/gradients/Sk4fGradientBase.cpp b/src/effects/gradients/Sk4fGradientBase.cpp index e234e9bc36..b385d8cd23 100644 --- a/src/effects/gradients/Sk4fGradientBase.cpp +++ b/src/effects/gradients/Sk4fGradientBase.cpp @@ -95,11 +95,30 @@ private: const int fAdvance; }; +void addMirrorIntervals(const SkColor colors[], + const SkScalar pos[], int count, + const Sk4f& componentScale, + bool premulColors, bool reverse, + Sk4fGradientIntervalBuffer::BufferType* buffer) { + const IntervalIterator iter(colors, pos, count, reverse); + iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) { + SkASSERT(buffer->empty() || buffer->back().fP1 == 2 - p0); + + const auto mirror_p0 = 2 - p0; + const auto mirror_p1 = 2 - p1; + // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid + // triggering Interval asserts. + if (mirror_p0 != mirror_p1) { + buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_p0, + pack_color(c1, premulColors, componentScale), mirror_p1); + } + }); +} + } // anonymous namespace -SkGradientShaderBase::GradientShaderBase4fContext:: -Interval::Interval(const Sk4f& c0, SkScalar p0, - const Sk4f& c1, SkScalar p1) +Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar p0, + const Sk4f& c1, SkScalar p1) : fP0(p0) , fP1(p1) , fZeroRamp((c0 == c1).allTrue()) { @@ -117,39 +136,9 @@ Interval::Interval(const Sk4f& c0, SkScalar p0, dc.store(&fDc.fVec); } -SkGradientShaderBase:: -GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader, - const ContextRec& rec) - : INHERITED(shader, rec) - , fFlags(this->INHERITED::getFlags()) -#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING - , fDither(true) -#else - , fDither(rec.fPaint->isDither()) -#endif -{ - const SkMatrix& inverse = this->getTotalInverse(); - fDstToPos.setConcat(shader.fPtsToUnit, inverse); - fDstToPosProc = fDstToPos.getMapXYProc(); - fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos)); - - if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) { - fFlags |= kOpaqueAlpha_Flag; - } - - fColorsArePremul = - (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) - || shader.fColorsAreOpaque; -} - -bool SkGradientShaderBase:: -GradientShaderBase4fContext::isValid() const { - return fDstToPos.isFinite(); -} - -void SkGradientShaderBase:: -GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader, - const ContextRec& rec, bool reverse) { +void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode tileMode, bool premulColors, + SkScalar alpha, bool reverse) { // The main job here is to build a specialized interval list: a different // representation of the color stops data, optimized for efficient scan line // access during shading. @@ -191,77 +180,131 @@ GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader, // // TODO: investigate collapsing intervals << 1px. - SkASSERT(shader.fColorCount > 0); - SkASSERT(shader.fOrigColors); + SkASSERT(count > 0); + SkASSERT(colors); - const float paintAlpha = rec.fPaint->getAlpha() * (1.0f / 255); - const Sk4f componentScale = fColorsArePremul - ? Sk4f(paintAlpha) - : Sk4f(1.0f, 1.0f, 1.0f, paintAlpha); - const int first_index = reverse ? shader.fColorCount - 1 : 0; - const int last_index = shader.fColorCount - 1 - first_index; + fIntervals.reset(); + + const Sk4f componentScale = premulColors + ? Sk4f(alpha) + : Sk4f(1.0f, 1.0f, 1.0f, alpha); + const int first_index = reverse ? count - 1 : 0; + const int last_index = count - 1 - first_index; const SkScalar first_pos = reverse ? SK_Scalar1 : 0; const SkScalar last_pos = SK_Scalar1 - first_pos; - if (shader.fTileMode == SkShader::kClamp_TileMode) { + if (tileMode == SkShader::kClamp_TileMode) { // synthetic edge interval: -/+inf .. P0 - const Sk4f clamp_color = pack_color(shader.fOrigColors[first_index], - fColorsArePremul, componentScale); + const Sk4f clamp_color = pack_color(colors[first_index], + premulColors, componentScale); const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity; fIntervals.emplace_back(clamp_color, clamp_pos, clamp_color, first_pos); - } else if (shader.fTileMode == SkShader::kMirror_TileMode && reverse) { + } else if (tileMode == SkShader::kMirror_TileMode && reverse) { // synthetic mirror intervals injected before main intervals: (2 .. 1] - addMirrorIntervals(shader, componentScale, false); + addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals); } - const IntervalIterator iter(shader.fOrigColors, - shader.fOrigPos, - shader.fColorCount, - reverse); - iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) { + const IntervalIterator iter(colors, pos, count, reverse); + iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) { SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0); - fIntervals.emplace_back(pack_color(c0, fColorsArePremul, componentScale), - p0, - pack_color(c1, fColorsArePremul, componentScale), - p1); + fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), p0, + pack_color(c1, premulColors, componentScale), p1); }); - if (shader.fTileMode == SkShader::kClamp_TileMode) { + if (tileMode == SkShader::kClamp_TileMode) { // synthetic edge interval: Pn .. +/-inf - const Sk4f clamp_color = pack_color(shader.fOrigColors[last_index], - fColorsArePremul, componentScale); + const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale); const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity; fIntervals.emplace_back(clamp_color, last_pos, clamp_color, clamp_pos); - } else if (shader.fTileMode == SkShader::kMirror_TileMode && !reverse) { + } else if (tileMode == SkShader::kMirror_TileMode && !reverse) { // synthetic mirror intervals injected after main intervals: [1 .. 2) - addMirrorIntervals(shader, componentScale, true); + addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals); } } -void SkGradientShaderBase:: -GradientShaderBase4fContext::addMirrorIntervals(const SkGradientShaderBase& shader, - const Sk4f& componentScale, bool reverse) { - const IntervalIterator iter(shader.fOrigColors, - shader.fOrigPos, - shader.fColorCount, - reverse); - iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) { - SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == 2 - p0); +const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const { + // Binary search. + const auto* i0 = fIntervals.begin(); + const auto* i1 = fIntervals.end() - 1; - const auto mirror_p0 = 2 - p0; - const auto mirror_p1 = 2 - p1; - // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid - // triggering Interval asserts. - if (mirror_p0 != mirror_p1) { - fIntervals.emplace_back(pack_color(c0, fColorsArePremul, componentScale), - mirror_p0, - pack_color(c1, fColorsArePremul, componentScale), - mirror_p1); + while (i0 != i1) { + SkASSERT(i0 < i1); + SkASSERT(t >= i0->fP0 && t <= i1->fP1); + + const auto* i = i0 + ((i1 - i0) >> 1); + + if (t > i->fP1) { + i0 = i + 1; + } else { + i1 = i; } - }); + } + + SkASSERT(i0->contains(t)); + return i0; +} + +const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext( + SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const { + + SkASSERT(!prev->contains(t)); + SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end()); + SkASSERT(t >= fIntervals.front().fP0 && t <= fIntervals.back().fP1); + + const auto* i = prev; + + // Use the |increasing| signal to figure which direction we should search for + // the next interval, then perform a linear search. + if (increasing) { + do { + i += 1; + if (i >= fIntervals.end()) { + i = fIntervals.begin(); + } + } while (!i->contains(t)); + } else { + do { + i -= 1; + if (i < fIntervals.begin()) { + i = fIntervals.end() - 1; + } + } while (!i->contains(t)); + } + + return i; +} + +SkGradientShaderBase:: +GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader, + const ContextRec& rec) + : INHERITED(shader, rec) + , fFlags(this->INHERITED::getFlags()) +#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING + , fDither(true) +#else + , fDither(rec.fPaint->isDither()) +#endif +{ + const SkMatrix& inverse = this->getTotalInverse(); + fDstToPos.setConcat(shader.fPtsToUnit, inverse); + fDstToPosProc = fDstToPos.getMapXYProc(); + fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos)); + + if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) { + fFlags |= kOpaqueAlpha_Flag; + } + + fColorsArePremul = + (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) + || shader.fColorsAreOpaque; +} + +bool SkGradientShaderBase:: +GradientShaderBase4fContext::isValid() const { + return fDstToPos.isFinite(); } void SkGradientShaderBase:: @@ -335,10 +378,8 @@ template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> class SkGradientShaderBase::GradientShaderBase4fContext::TSampler { public: TSampler(const GradientShaderBase4fContext& ctx) - : fFirstInterval(ctx.fIntervals.begin()) - , fLastInterval(ctx.fIntervals.end() - 1) + : fCtx(ctx) , fInterval(nullptr) { - SkASSERT(fLastInterval >= fFirstInterval); switch (tileMode) { case kClamp_TileMode: fLargestIntervalValue = SK_ScalarInfinity; @@ -358,10 +399,10 @@ public: if (!fInterval) { // Very first sample => locate the initial interval. // TODO: maybe do this in ctor to remove a branch? - fInterval = this->findFirstInterval(tiled_t); + fInterval = fCtx.fIntervals.find(tiled_t); this->loadIntervalData(fInterval); } else if (!fInterval->contains(tiled_t)) { - fInterval = this->findNextInterval(t, tiled_t); + fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT); this->loadIntervalData(fInterval); } @@ -395,65 +436,15 @@ private: return fCc + fDc * (t - fInterval->fP0); } - const Interval* findFirstInterval(SkScalar t) const { - // Binary search. - const Interval* i0 = fFirstInterval; - const Interval* i1 = fLastInterval; - - while (i0 != i1) { - SkASSERT(i0 < i1); - SkASSERT(t >= i0->fP0 && t <= i1->fP1); - - const Interval* i = i0 + ((i1 - i0) >> 1); - - if (t > i->fP1) { - i0 = i + 1; - } else { - i1 = i; - } - } - - SkASSERT(i0->contains(t)); - return i0; - } - - const Interval* findNextInterval(SkScalar t, SkScalar tiled_t) const { - SkASSERT(!fInterval->contains(tiled_t)); - SkASSERT(tiled_t >= fFirstInterval->fP0 && tiled_t <= fLastInterval->fP1); - - const Interval* i = fInterval; - - // Use the t vs. prev_t signal to figure which direction we should search for - // the next interval, then perform a linear search. - if (t >= fPrevT) { - do { - i += 1; - if (i > fLastInterval) { - i = fFirstInterval; - } - } while (!i->contains(tiled_t)); - } else { - do { - i -= 1; - if (i < fFirstInterval) { - i = fLastInterval; - } - } while (!i->contains(tiled_t)); - } - - return i; - } - - void loadIntervalData(const Interval* i) { + void loadIntervalData(const Sk4fGradientInterval* i) { fCc = DstTraits<dstType, premul>::load(i->fC0); fDc = DstTraits<dstType, premul>::load(i->fDc); } - const Interval* fFirstInterval; - const Interval* fLastInterval; - const Interval* fInterval; - SkScalar fPrevT; - SkScalar fLargestIntervalValue; - Sk4f fCc; - Sk4f fDc; + const GradientShaderBase4fContext& fCtx; + const Sk4fGradientInterval* fInterval; + SkScalar fPrevT; + SkScalar fLargestIntervalValue; + Sk4f fCc; + Sk4f fDc; }; diff --git a/src/effects/gradients/Sk4fGradientBase.h b/src/effects/gradients/Sk4fGradientBase.h index 6d0c3b96ff..2826f9e7b7 100644 --- a/src/effects/gradients/Sk4fGradientBase.h +++ b/src/effects/gradients/Sk4fGradientBase.h @@ -17,6 +17,39 @@ #include "SkShader.h" #include "SkTArray.h" +struct Sk4fGradientInterval { + Sk4fGradientInterval(const Sk4f& c0, SkScalar p0, + const Sk4f& c1, SkScalar p1); + + bool contains(SkScalar t) const { + // True if t is in [p0,p1]. Note: this helper assumes a + // natural/increasing interval - so it's not usable in Sk4fLinearGradient. + SkASSERT(fP0 < fP1); + return t >= fP0 && t <= fP1; + } + + SkPM4f fC0, fDc; + SkScalar fP0, fP1; + bool fZeroRamp; +}; + +class Sk4fGradientIntervalBuffer { +public: + void init(const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode tileMode, bool premulColors, SkScalar alpha, bool reverse); + + const Sk4fGradientInterval* find(SkScalar t) const; + const Sk4fGradientInterval* findNext(SkScalar t, const Sk4fGradientInterval* prev, + bool increasing) const; + + using BufferType = SkSTArray<8, Sk4fGradientInterval, true>; + + const BufferType* operator->() const { return &fIntervals; } + +private: + BufferType fIntervals; +}; + class SkGradientShaderBase:: GradientShaderBase4fContext : public SkShader::Context { public: @@ -31,35 +64,15 @@ public: bool isValid() const; protected: - struct Interval { - Interval(const Sk4f& c0, SkScalar p0, - const Sk4f& c1, SkScalar p1); - - bool isZeroRamp() const { return fZeroRamp; } - - bool contains(SkScalar p) const { - // True if p is in [p0,p1]. Note: this helper assumes a - // natural/increasing interval - so it's not usable in Sk4fLinearGradient. - SkASSERT(fP0 < fP1); - return p >= fP0 && p <= fP1; - } - - SkPM4f fC0, fDc; - SkScalar fP0, fP1; - bool fZeroRamp; - }; - virtual void mapTs(int x, int y, SkScalar ts[], int count) const = 0; - void buildIntervals(const SkGradientShaderBase&, const ContextRec&, bool reverse); - - SkSTArray<8, Interval, true> fIntervals; - SkMatrix fDstToPos; - SkMatrix::MapXYProc fDstToPosProc; - uint8_t fDstToPosClass; - uint8_t fFlags; - bool fDither; - bool fColorsArePremul; + Sk4fGradientIntervalBuffer fIntervals; + SkMatrix fDstToPos; + SkMatrix::MapXYProc fDstToPosProc; + uint8_t fDstToPosClass; + uint8_t fFlags; + bool fDither; + bool fColorsArePremul; private: using INHERITED = SkShader::Context; diff --git a/src/effects/gradients/Sk4fLinearGradient.cpp b/src/effects/gradients/Sk4fLinearGradient.cpp index 354c941a86..1256b8f6da 100644 --- a/src/effects/gradients/Sk4fLinearGradient.cpp +++ b/src/effects/gradients/Sk4fLinearGradient.cpp @@ -129,40 +129,41 @@ LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader, // Our fast path expects interval points to be monotonically increasing in x. const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX()); - this->buildIntervals(shader, rec, reverseIntervals); + fIntervals.init(shader.fOrigColors, shader.fOrigPos, shader.fColorCount, shader.fTileMode, + fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals); - SkASSERT(fIntervals.count() > 0); - fCachedInterval = fIntervals.begin(); + SkASSERT(fIntervals->count() > 0); + fCachedInterval = fIntervals->begin(); } -const SkGradientShaderBase::GradientShaderBase4fContext::Interval* +const Sk4fGradientInterval* SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const { - SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1)); + SkASSERT(in_range(fx, fIntervals->front().fP0, fIntervals->back().fP1)); if (1) { // Linear search, using the last scanline interval as a starting point. - SkASSERT(fCachedInterval >= fIntervals.begin()); - SkASSERT(fCachedInterval < fIntervals.end()); + SkASSERT(fCachedInterval >= fIntervals->begin()); + SkASSERT(fCachedInterval < fIntervals->end()); const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1; while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) { fCachedInterval += search_dir; - if (fCachedInterval >= fIntervals.end()) { - fCachedInterval = fIntervals.begin(); - } else if (fCachedInterval < fIntervals.begin()) { - fCachedInterval = fIntervals.end() - 1; + if (fCachedInterval >= fIntervals->end()) { + fCachedInterval = fIntervals->begin(); + } else if (fCachedInterval < fIntervals->begin()) { + fCachedInterval = fIntervals->end() - 1; } } return fCachedInterval; } else { // Binary search. Seems less effective than linear + caching. - const Interval* i0 = fIntervals.begin(); - const Interval* i1 = fIntervals.end() - 1; + const auto* i0 = fIntervals->begin(); + const auto* i1 = fIntervals->end() - 1; while (i0 != i1) { SkASSERT(i0 < i1); SkASSERT(in_range(fx, i0->fP0, i1->fP1)); - const Interval* i = i0 + ((i1 - i0) >> 1); + const auto* i = i0 + ((i1 - i0) >> 1); if (in_range(fx, i0->fP0, i->fP1)) { i1 = i; @@ -251,8 +252,8 @@ LinearGradient4fContext::shadeSpanInternal(int x, int y, &pt); const SkScalar fx = pinFx<tileMode>(pt.x()); const SkScalar dx = fDstToPos.getScaleX(); - LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals.begin(), - fIntervals.end() - 1, + LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(), + fIntervals->end() - 1, this->findInterval(fx), fx, dx, @@ -289,9 +290,9 @@ template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> class SkLinearGradient:: LinearGradient4fContext::LinearIntervalProcessor { public: - LinearIntervalProcessor(const Interval* firstInterval, - const Interval* lastInterval, - const Interval* i, + LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval, + const Sk4fGradientInterval* lastInterval, + const Sk4fGradientInterval* i, SkScalar fx, SkScalar dx, bool is_vertical) @@ -349,10 +350,10 @@ public: private: void compute_interval_props(SkScalar t) { - fZeroRamp = fIsVertical || fInterval->isZeroRamp(); + fZeroRamp = fIsVertical || fInterval->fZeroRamp; fCc = DstTraits<dstType, premul>::load(fInterval->fC0); - if (fInterval->isZeroRamp()) { + if (fInterval->fZeroRamp) { fDcDx = 0; } else { const Sk4f dC = DstTraits<dstType, premul>::load(fInterval->fDc); @@ -384,7 +385,7 @@ private: } } - const Interval* next_interval(const Interval* i) const { + const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const { SkASSERT(i >= fFirstInterval); SkASSERT(i <= fLastInterval); i++; @@ -419,11 +420,11 @@ private: SkScalar fAdvX; // remaining interval advance in dst bool fZeroRamp; // current interval color grad is 0 - const Interval* fFirstInterval; - const Interval* fLastInterval; - const Interval* fInterval; // current interval - const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx - const bool fIsVertical; + const Sk4fGradientInterval* fFirstInterval; + const Sk4fGradientInterval* fLastInterval; + const Sk4fGradientInterval* fInterval; // current interval + const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx + const bool fIsVertical; }; void SkLinearGradient:: diff --git a/src/effects/gradients/Sk4fLinearGradient.h b/src/effects/gradients/Sk4fLinearGradient.h index 762b644465..eebd30fbf5 100644 --- a/src/effects/gradients/Sk4fLinearGradient.h +++ b/src/effects/gradients/Sk4fLinearGradient.h @@ -38,14 +38,14 @@ private: void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[], int count) const; - const Interval* findInterval(SkScalar fx) const; + const Sk4fGradientInterval* findInterval(SkScalar fx) const; bool isFast() const { return fDstToPosClass == kLinear_MatrixClass; } static void D32_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count); static void D64_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count); - mutable const Interval* fCachedInterval; + mutable const Sk4fGradientInterval* fCachedInterval; }; #endif // Sk4fLinearGradient_DEFINED |