diff --git a/gm/gradients.cpp b/gm/gradients.cpp index 45b6564a64..8ae1dd7901 100644 --- a/gm/gradients.cpp +++ b/gm/gradients.cpp @@ -919,3 +919,20 @@ DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) { DEF_SIMPLE_GM(gradient_many_stops_4f, canvas, 500, 500) { draw_many_stops(canvas, SkLinearGradient::kForce4fContext_PrivateFlag); } + +static void draw_subpixel_gradient(SkCanvas* canvas, uint32_t flags) { + const SkPoint pts[] = { {50, 50}, {50.1f, 50.1f}}; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; + SkPaint p; + p.setShader(SkGradientShader::MakeLinear( + pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kRepeat_TileMode, flags, nullptr)); + canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p); +} + +DEF_SIMPLE_GM(gradient_subpixel, canvas, 500, 500) { + draw_subpixel_gradient(canvas, 0); +} + +DEF_SIMPLE_GM(gradient_subpixel_4f, canvas, 500, 500) { + draw_subpixel_gradient(canvas, SkLinearGradient::kForce4fContext_PrivateFlag); +} diff --git a/src/effects/gradients/Sk4fLinearGradient.cpp b/src/effects/gradients/Sk4fLinearGradient.cpp index ebfd13812e..1100493212 100644 --- a/src/effects/gradients/Sk4fLinearGradient.cpp +++ b/src/effects/gradients/Sk4fLinearGradient.cpp @@ -105,8 +105,8 @@ SkScalar pinFx(SkScalar fx) { bool in_range(SkScalar x, SkScalar k1, SkScalar k2) { SkASSERT(k1 != k2); return (k1 < k2) - ? (x >= k1 && x < k2) - : (x > k2 && x <= k1); + ? (x >= k1 && x < k2) + : (x >= k2 && x < k1); } } // anonymous namespace @@ -294,12 +294,28 @@ public: SkASSERT(fAdvX >= 0); SkASSERT(firstInterval <= lastInterval); SkASSERT(in_range(fx, i->fP0, i->fP1)); + + if (tileMode != kClamp_TileMode && !is_vertical) { + const auto spanX = (lastInterval->fP1 - firstInterval->fP0) / dx; + SkASSERT(spanX >= 0); + + // If we're in a repeating tile mode and the whole gradient is compressed into a + // fraction of a pixel, we just use the average color in zero-ramp mode. + // This also avoids cases where we make no progress due to interval advances being + // close to zero. + static constexpr SkScalar kMinSpanX = .25f; + if (spanX < kMinSpanX) { + this->init_average_props(); + return; + } + } + this->compute_interval_props(fx - i->fP0); } SkScalar currentAdvance() const { SkASSERT(fAdvX >= 0); - SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx); + SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx || !isfinite(fAdvX)); return fAdvX; } @@ -334,6 +350,29 @@ private: } } + void init_average_props() { + fAdvX = SK_ScalarInfinity; + fZeroRamp = true; + fDcDx = 0; + fCc = Sk4f(0); + + // TODO: precompute the average at interval setup time? + for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) { + // Each interval contributes its average color to the total/weighted average: + // + // C = (c0 + c1) / 2 = (c0 + c0 + dc * (p1 - p0)) / 2 + // + // Avg += C * (p1 - p0) + // + const auto dp = i->fP1 - i->fP0; + auto c = DstTraits::load(i->fC0); + if (!i->fZeroRamp) { + c = c + DstTraits::load(i->fDc) * dp * 0.5f; + } + fCc = fCc + c * dp; + } + } + const Interval* next_interval(const Interval* i) const { SkASSERT(i >= fFirstInterval); SkASSERT(i <= fLastInterval);