1/* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8@header { 9 #include "include/core/SkScalar.h" 10 #include "src/core/SkBlurMask.h" 11 #include "src/gpu/GrProxyProvider.h" 12 #include "src/gpu/GrShaderCaps.h" 13} 14 15in float4 rect; 16 17layout(key) bool highp = abs(rect.x) > 16000 || abs(rect.y) > 16000 || 18 abs(rect.z) > 16000 || abs(rect.w) > 16000; 19 20layout(when= highp) uniform float4 rectF; 21layout(when=!highp) uniform half4 rectH; 22 23in uniform half sigma; 24 25@make { 26 static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider, 27 const GrShaderCaps& caps, 28 const SkRect& rect, float sigma) { 29 float doubleProfileSize = (12 * sigma); 30 if (!caps.floatIs32Bits()) { 31 // We promote the math that gets us into the Gaussian space to full float when the rect 32 // coords are large. If we don't have full float then fail. We could probably clip the 33 // rect to an outset device bounds instead. 34 if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f || 35 SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) { 36 return nullptr; 37 } 38 } 39 // Sigma is always a half. 40 SkASSERT(sigma > 0); 41 if (sigma > 16000.f) { 42 return nullptr; 43 } 44 45 if (doubleProfileSize >= (float) rect.width() || 46 doubleProfileSize >= (float) rect.height()) { 47 // if the blur sigma is too large so the gaussian overlaps the whole 48 // rect in either direction, fall back to CPU path for now. 49 return nullptr; 50 } 51 52 return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(rect, sigma)); 53 } 54} 55 56void main() { 57 half invr = 1.0 / (2.0 * sigma); 58 59 // Get the smaller of the signed distance from the frag coord to the left and right edges. 60 half x; 61 @if (highp) { 62 float lDiff = rectF.x - sk_FragCoord.x; 63 float rDiff = sk_FragCoord.x - rectF.z; 64 x = half(max(lDiff, rDiff) * invr); 65 } else { 66 half lDiff = half(rectH.x - sk_FragCoord.x); 67 half rDiff = half(sk_FragCoord.x - rectH.z); 68 x = max(lDiff, rDiff) * invr; 69 } 70 // This is lifted from the implementation of SkBlurMask::ComputeBlurProfile. It approximates 71 // a Gaussian as three box filters, and then computes the integral of this approximation from 72 // -inf to x. 73 // TODO: Make this a function when supported in .fp files as we duplicate it for y below. 74 half xCoverage; 75 if (x > 1.5) { 76 xCoverage = 0.0; 77 } else if (x < -1.5) { 78 xCoverage = 1.0; 79 } else { 80 half x2 = x * x; 81 half x3 = x2 * x; 82 83 if (x > 0.5) { 84 xCoverage = 0.5625 - (x3 / 6.0 - 3.0 * x2 * 0.25 + 1.125 * x); 85 } else if (x > -0.5) { 86 xCoverage = 0.5 - (0.75 * x - x3 / 3.0); 87 } else { 88 xCoverage = 0.4375 + (-x3 / 6.0 - 3.0 * x2 * 0.25 - 1.125 * x); 89 } 90 } 91 92 // Repeat of above for y. 93 half y; 94 @if (highp) { 95 float tDiff = rectF.y - sk_FragCoord.y; 96 float bDiff = sk_FragCoord.y - rectF.w; 97 y = half(max(tDiff, bDiff) * invr); 98 } else { 99 half tDiff = half(rectH.y - sk_FragCoord.y); 100 half bDiff = half(sk_FragCoord.y - rectH.w); 101 y = max(tDiff, bDiff) * invr; 102 } 103 half yCoverage; 104 if (y > 1.5) { 105 yCoverage = 0.0; 106 } else if (y < -1.5) { 107 yCoverage = 1.0; 108 } else { 109 half y2 = y * y; 110 half y3 = y2 * y; 111 112 if (y > 0.5) { 113 yCoverage = 0.5625 - (y3 / 6.0 - 3.0 * y2 * 0.25 + 1.125 * y); 114 } else if (y > -0.5) { 115 yCoverage = 0.5 - (0.75 * y - y3 / 3.0); 116 } else { 117 yCoverage = 0.4375 + (-y3 / 6.0 - 3.0 * y2 * 0.25 - 1.125 * y); 118 } 119 } 120 121 sk_OutColor = sk_InColor * xCoverage * yCoverage; 122} 123 124@setData(pdman) { 125 float r[] {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom}; 126 pdman.set4fv(highp ? rectF : rectH, 1, r); 127} 128 129@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag } 130 131@test(data) { 132 float sigma = data->fRandom->nextRangeF(3,8); 133 float width = data->fRandom->nextRangeF(200,300); 134 float height = data->fRandom->nextRangeF(200,300); 135 return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(), 136 SkRect::MakeWH(width, height), sigma); 137} 138