/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @header { #include "include/core/SkScalar.h" #include "src/core/SkBlurMask.h" #include "src/gpu/GrProxyProvider.h" #include "src/gpu/GrShaderCaps.h" } in float4 rect; layout(key) bool highp = abs(rect.x) > 16000 || abs(rect.y) > 16000 || abs(rect.z) > 16000 || abs(rect.w) > 16000; layout(when= highp) uniform float4 rectF; layout(when=!highp) uniform half4 rectH; in uniform half sigma; @make { static std::unique_ptr Make(GrProxyProvider* proxyProvider, const GrShaderCaps& caps, const SkRect& rect, float sigma) { float doubleProfileSize = (12 * sigma); if (!caps.floatIs32Bits()) { // We promote the math that gets us into the Gaussian space to full float when the rect // coords are large. If we don't have full float then fail. We could probably clip the // rect to an outset device bounds instead. if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f || SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) { return nullptr; } } // Sigma is always a half. SkASSERT(sigma > 0); if (sigma > 16000.f) { return nullptr; } if (doubleProfileSize >= (float) rect.width() || doubleProfileSize >= (float) rect.height()) { // if the blur sigma is too large so the gaussian overlaps the whole // rect in either direction, fall back to CPU path for now. return nullptr; } return std::unique_ptr(new GrRectBlurEffect(rect, sigma)); } } void main() { half invr = 1.0 / (2.0 * sigma); // Get the smaller of the signed distance from the frag coord to the left and right edges. half x; @if (highp) { float lDiff = rectF.x - sk_FragCoord.x; float rDiff = sk_FragCoord.x - rectF.z; x = half(max(lDiff, rDiff) * invr); } else { half lDiff = half(rectH.x - sk_FragCoord.x); half rDiff = half(sk_FragCoord.x - rectH.z); x = max(lDiff, rDiff) * invr; } // This is lifted from the implementation of SkBlurMask::ComputeBlurProfile. It approximates // a Gaussian as three box filters, and then computes the integral of this approximation from // -inf to x. // TODO: Make this a function when supported in .fp files as we duplicate it for y below. half xCoverage; if (x > 1.5) { xCoverage = 0.0; } else if (x < -1.5) { xCoverage = 1.0; } else { half x2 = x * x; half x3 = x2 * x; if (x > 0.5) { xCoverage = 0.5625 - (x3 / 6.0 - 3.0 * x2 * 0.25 + 1.125 * x); } else if (x > -0.5) { xCoverage = 0.5 - (0.75 * x - x3 / 3.0); } else { xCoverage = 0.4375 + (-x3 / 6.0 - 3.0 * x2 * 0.25 - 1.125 * x); } } // Repeat of above for y. half y; @if (highp) { float tDiff = rectF.y - sk_FragCoord.y; float bDiff = sk_FragCoord.y - rectF.w; y = half(max(tDiff, bDiff) * invr); } else { half tDiff = half(rectH.y - sk_FragCoord.y); half bDiff = half(sk_FragCoord.y - rectH.w); y = max(tDiff, bDiff) * invr; } half yCoverage; if (y > 1.5) { yCoverage = 0.0; } else if (y < -1.5) { yCoverage = 1.0; } else { half y2 = y * y; half y3 = y2 * y; if (y > 0.5) { yCoverage = 0.5625 - (y3 / 6.0 - 3.0 * y2 * 0.25 + 1.125 * y); } else if (y > -0.5) { yCoverage = 0.5 - (0.75 * y - y3 / 3.0); } else { yCoverage = 0.4375 + (-y3 / 6.0 - 3.0 * y2 * 0.25 - 1.125 * y); } } sk_OutColor = sk_InColor * xCoverage * yCoverage; } @setData(pdman) { float r[] {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom}; pdman.set4fv(highp ? rectF : rectH, 1, r); } @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag } @test(data) { float sigma = data->fRandom->nextRangeF(3,8); float width = data->fRandom->nextRangeF(200,300); float height = data->fRandom->nextRangeF(200,300); return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(), SkRect::MakeWH(width, height), sigma); }