• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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