blob: 5498e73989ff7017d67989e56e053bf621328e7e [file] [log] [blame]
/*
* 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<GrFragmentProcessor> 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<GrFragmentProcessor>(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);
}