Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27cb4c81 authored by Shan Huang's avatar Shan Huang Committed by Android (Google) Code Review
Browse files

Merge "Tweak Kawase2 to use fewer passes and fewer samples." into main

parents c0919ab1 fc4f2817
Loading
Loading
Loading
Loading
+30 −23
Original line number Diff line number Diff line
@@ -96,13 +96,17 @@ void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface,
void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
                                    const float inverseScale, const float radius,
                                    const float alpha) const {
    SkPaint paint;
    if (radius == 0) {
        paint.setShader(std::move(input));
        paint.setAlphaf(alpha);
    } else {
        SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
        blurBuilder.child("child") = std::move(input);
    blurBuilder.uniform("in_inverseScale") = inverseScale;
        blurBuilder.uniform("in_blurOffset") = radius;
        blurBuilder.uniform("in_crossFade") = alpha;
    SkPaint paint;
        paint.setShader(blurBuilder.makeShader(nullptr));
    }
    paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
    drawSurface->getCanvas()->drawPaint(paint);
}
@@ -116,32 +120,35 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin

    // Use a variable number of blur passes depending on the radius. The non-integer part of this
    // calculation is used to mix the final pass into the second-last with an alpha blend.
    constexpr int kMaxSurfaces = 4;
    const float filterDepth =
            std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
    constexpr int kMaxSurfaces = 3;
    const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f);
    const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));

    // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
    // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale.
    sk_sp<SkSurface> surfaces[kMaxSurfaces] =
            {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
             filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
             filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
             filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};

    // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
    static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
             filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
             filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};

    // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250.
    static const float kWeights[5] = {
            1.0f, // 1st downsampling pass
            1.0f, // 2nd downsampling pass
            1.0f, // 3rd downsampling pass
            0.0f, // 1st upscaling pass. Set to zero to upscale without blurring for performance.
            1.0f, // 2nd upscaling pass
    };

    // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
    // simpler blurs. A transformation is required to approximate the same effect as Gaussian.
    float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
    float sumSquaredR = powf(kWeights[0], 2.0f);
    for (int i = 0; i < filterPasses; i++) {
        const float alpha = std::min(1.0f, filterDepth - i);
        sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
        sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
        sumSquaredR += powf(powf(2.0f, i) * alpha * kWeights[1 + i] / kInputScale, 2.0f);
        sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[4 - i] / kInputScale, 2.0f);
    }
    // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
    const float step = M_SQRT1_2 *
            sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
    // Solve for R = sqrt(sum(r_i^2)).
    const float step = radius * sqrt(1.0f / sumSquaredR);

    // Start by downscaling and doing the first blur pass.
    {
@@ -162,7 +169,7 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin
    }
    // Finally blur+upscale back to our original size.
    for (int i = filterPasses - 1; i >= 0; i--) {
        blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
        blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step,
                 std::min(1.0f, filterDepth - i));
    }
    return surfaces[0]->makeImageSnapshot();