| Index: src/effects/gradients/Sk4fGradientBase.cpp
|
| diff --git a/src/effects/gradients/Sk4fGradientBase.cpp b/src/effects/gradients/Sk4fGradientBase.cpp
|
| index 442916638bc7642d1f9110c30b70abc28ace0a86..b3bc24658b7c5a6ed5bcab6c2e2f71694dbefe19 100644
|
| --- a/src/effects/gradients/Sk4fGradientBase.cpp
|
| +++ b/src/effects/gradients/Sk4fGradientBase.cpp
|
| @@ -25,6 +25,83 @@ bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
|
| : (x >= k2 && x < k1);
|
| }
|
|
|
| +class IntervalBuilder {
|
| +public:
|
| + IntervalBuilder(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
|
| + : fColors(colors)
|
| + , fPos(pos)
|
| + , fCount(count)
|
| + , fFirstPos(reverse ? SK_Scalar1 : 0)
|
| + , fBegin(reverse ? count - 1 : 0)
|
| + , fAdvance(reverse ? -1 : 1) {
|
| + SkASSERT(colors);
|
| + SkASSERT(count > 1);
|
| + }
|
| +
|
| + template<typename F>
|
| + void build(F func) const {
|
| + if (!fPos) {
|
| + this->buildImplicitPos(func);
|
| + return;
|
| + }
|
| +
|
| + const int end = fBegin + fAdvance * (fCount - 1);
|
| + const SkScalar lastPos = 1 - fFirstPos;
|
| + int prev = fBegin;
|
| + SkScalar prevPos = fFirstPos;
|
| +
|
| + do {
|
| + const int curr = prev + fAdvance;
|
| + SkASSERT(curr >= 0 && curr < fCount);
|
| +
|
| + // TODO: this sanitization should be done in SkGradientShaderBase
|
| + const SkScalar currPos = (fAdvance > 0)
|
| + ? SkTPin(fPos[curr], prevPos, lastPos)
|
| + : SkTPin(fPos[curr], lastPos, prevPos);
|
| +
|
| + if (currPos != prevPos) {
|
| + SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
|
| + func(fColors[prev], fColors[curr], prevPos, currPos);
|
| + }
|
| +
|
| + prev = curr;
|
| + prevPos = currPos;
|
| + } while (prev != end);
|
| + }
|
| +
|
| +private:
|
| + template<typename F>
|
| + void buildImplicitPos(F func) const {
|
| + // When clients don't provide explicit color stop positions (fPos == nullptr),
|
| + // the color stops are distributed evenly across the unit interval
|
| + // (implicit positioning).
|
| + const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
|
| + const int end = fBegin + fAdvance * (fCount - 2);
|
| + int prev = fBegin;
|
| + SkScalar prevPos = fFirstPos;
|
| +
|
| + while (prev != end) {
|
| + const int curr = prev + fAdvance;
|
| + SkASSERT(curr >= 0 && curr < fCount);
|
| +
|
| + const SkScalar currPos = prevPos + dt;
|
| + func(fColors[prev], fColors[curr], prevPos, currPos);
|
| + prev = curr;
|
| + prevPos = currPos;
|
| + }
|
| +
|
| + // emit the last interval with a pinned end position, to avoid precision issues
|
| + func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
|
| + }
|
| +
|
| + const SkColor* fColors;
|
| + const SkScalar* fPos;
|
| + const int fCount;
|
| + const SkScalar fFirstPos;
|
| + const int fBegin;
|
| + const int fAdvance;
|
| +};
|
| +
|
| } // anonymous namespace
|
|
|
| SkGradientShaderBase::GradientShaderBase4fContext::
|
| @@ -134,69 +211,38 @@ GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderB
|
| SkASSERT(shader.fColorCount > 1);
|
| SkASSERT(shader.fOrigColors);
|
|
|
| - int direction = 1;
|
| - int first_index = 0;
|
| - int last_index = shader.fColorCount - 1;
|
| - SkScalar first_pos = 0;
|
| - SkScalar last_pos = 1;
|
| const bool dx_is_pos = fDstToPos.getScaleX() >= 0;
|
| - if (!dx_is_pos) {
|
| - direction = -direction;
|
| - SkTSwap(first_index, last_index);
|
| - SkTSwap(first_pos, last_pos);
|
| - }
|
| + const int first_index = dx_is_pos ? 0 : shader.fColorCount - 1;
|
| + const int last_index = shader.fColorCount - 1 - first_index;
|
| + const SkScalar first_pos = dx_is_pos ? 0 : SK_Scalar1;
|
| + const SkScalar last_pos = 1 - first_pos;
|
|
|
| if (shader.fTileMode == SkShader::kClamp_TileMode) {
|
| - // synthetic edge interval: -/+inf .. P0)
|
| + // synthetic edge interval: -/+inf .. P0
|
| const SkPMColor clamp_color = pack_color(shader.fOrigColors[first_index],
|
| fColorsArePremul);
|
| const SkScalar clamp_pos = dx_is_pos ? SK_ScalarMin : SK_ScalarMax;
|
| fIntervals.emplace_back(clamp_color, clamp_pos,
|
| clamp_color, first_pos,
|
| componentScale);
|
| + } else if (shader.fTileMode == SkShader::kMirror_TileMode && !dx_is_pos) {
|
| + // synthetic mirror intervals injected before main intervals: (2 .. 1]
|
| + addMirrorIntervals(shader, componentScale, dx_is_pos);
|
| }
|
|
|
| - int prev = first_index;
|
| - int curr = prev + direction;
|
| - SkScalar prev_pos = first_pos;
|
| - if (shader.fOrigPos) {
|
| - // explicit positions
|
| - do {
|
| - // TODO: this sanitization should be done in SkGradientShaderBase
|
| - const SkScalar curr_pos = (dx_is_pos)
|
| - ? SkTPin(shader.fOrigPos[curr], prev_pos, last_pos)
|
| - : SkTPin(shader.fOrigPos[curr], last_pos, prev_pos);
|
| - if (curr_pos != prev_pos) {
|
| - fIntervals.emplace_back(
|
| - pack_color(shader.fOrigColors[prev], fColorsArePremul),
|
| - prev_pos,
|
| - pack_color(shader.fOrigColors[curr], fColorsArePremul),
|
| - curr_pos,
|
| - componentScale);
|
| - }
|
| - prev = curr;
|
| - prev_pos = curr_pos;
|
| - curr += direction;
|
| - } while (prev != last_index);
|
| - } else {
|
| - // implicit positions
|
| - const SkScalar dt = direction * SK_Scalar1 / (shader.fColorCount - 1);
|
| - do {
|
| - const SkScalar curr_pos = prev_pos + dt;
|
| - fIntervals.emplace_back(
|
| - pack_color(shader.fOrigColors[prev], fColorsArePremul),
|
| - prev_pos,
|
| - pack_color(shader.fOrigColors[curr], fColorsArePremul),
|
| - curr_pos,
|
| - componentScale);
|
| + const IntervalBuilder builder(shader.fOrigColors,
|
| + shader.fOrigPos,
|
| + shader.fColorCount,
|
| + !dx_is_pos);
|
| + builder.build([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
|
| + SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0);
|
|
|
| - prev = curr;
|
| - prev_pos = curr_pos;
|
| - curr += direction;
|
| - } while (prev != last_index);
|
| - // pin the last pos to maintain accurate [0,1] pos coverage.
|
| - fIntervals.back().fP1 = last_pos;
|
| - }
|
| + fIntervals.emplace_back(pack_color(c0, fColorsArePremul),
|
| + p0,
|
| + pack_color(c1, fColorsArePremul),
|
| + p1,
|
| + componentScale);
|
| + });
|
|
|
| if (shader.fTileMode == SkShader::kClamp_TileMode) {
|
| // synthetic edge interval: Pn .. +/-inf
|
| @@ -206,36 +252,34 @@ GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderB
|
| fIntervals.emplace_back(clamp_color, last_pos,
|
| clamp_color, clamp_pos,
|
| componentScale);
|
| - } else if (shader.fTileMode == SkShader::kMirror_TileMode) {
|
| - const int count = fIntervals.count();
|
| - // synthetic flipped intervals in [1 .. 2)
|
| - for (int i = count - 1; i >= 0; --i) {
|
| - const Interval& interval = fIntervals[i];
|
| - const SkScalar p0 = interval.fP0;
|
| - const SkScalar p1 = interval.fP1;
|
| - Sk4f dc = Sk4f::Load(interval.fDc.fVec);
|
| - Sk4f c = Sk4f::Load(interval.fC0.fVec) + dc * Sk4f(p1 - p0);
|
| - fIntervals.emplace_back(c, dc * Sk4f(-1), 2 - p1, 2 - p0);
|
| - }
|
| -
|
| - if (!dx_is_pos) {
|
| - // When dx is negative, our initial invervals are in (1..0] order.
|
| - // The loop above appends their flipped counterparts, pivoted in 2: (1..0](2..1]
|
| - // To achieve the expected monotonic interval order, we need to
|
| - // swap the two halves: (2..1](1..0]
|
| - // TODO: we can probably avoid this late swap with some additional logic during
|
| - // the initial interval buildup.
|
| - SkASSERT(fIntervals.count() == count * 2)
|
| - for (int i = 0; i < count; ++i) {
|
| - SkTSwap(fIntervals[i], fIntervals[count + i]);
|
| - }
|
| - }
|
| + } else if (shader.fTileMode == SkShader::kMirror_TileMode && dx_is_pos) {
|
| + // synthetic mirror intervals injected after main intervals: [1 .. 2)
|
| + addMirrorIntervals(shader, componentScale, dx_is_pos);
|
| }
|
|
|
| SkASSERT(fIntervals.count() > 0);
|
| fCachedInterval = fIntervals.begin();
|
| }
|
|
|
| +void SkGradientShaderBase::
|
| +GradientShaderBase4fContext::addMirrorIntervals(const SkGradientShaderBase& shader,
|
| + const Sk4f& componentScale, bool dx_is_pos) {
|
| + // Iterates in reverse order (vs main interval builder) and adds intervals reflected in 2.
|
| + const IntervalBuilder builder(shader.fOrigColors,
|
| + shader.fOrigPos,
|
| + shader.fColorCount,
|
| + dx_is_pos);
|
| + builder.build([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
|
| + SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == 2 - p0);
|
| +
|
| + fIntervals.emplace_back(pack_color(c0, fColorsArePremul),
|
| + 2 - p0,
|
| + pack_color(c1, fColorsArePremul),
|
| + 2 - p1,
|
| + componentScale);
|
| + });
|
| +}
|
| +
|
| const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
|
| SkGradientShaderBase::
|
| GradientShaderBase4fContext::findInterval(SkScalar fx) const {
|
|
|