• 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 "GrProxyProvider.h"
10    #include "GrShaderCaps.h"
11    #include "SkBlurMask.h"
12    #include "SkScalar.h"
13}
14
15in uniform float4 rect;
16in float sigma;
17in uniform sampler2D blurProfile;
18
19@constructorParams {
20    GrSamplerState samplerParams
21}
22
23@samplerParams(blurProfile) {
24    samplerParams
25}
26
27// in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger than
28// that, the shader math will end up with infinities and result in the blur effect not working
29// correctly. To avoid this, we switch into highp when the coordinates are too big. As 2^14 is the
30// minimum range but the actual range can be bigger, we might end up switching to highp sooner than
31// strictly necessary, but most devices that have a bigger range for mediump also have mediump being
32// exactly the same as highp (e.g. all non-OpenGL ES devices), and thus incur no additional penalty
33// for the switch.
34layout(key) bool highPrecision = abs(rect.x) > 16000 || abs(rect.y) > 16000 ||
35                                 abs(rect.z) > 16000 || abs(rect.w) > 16000 ||
36                                 abs(rect.z - rect.x) > 16000 || abs(rect.w - rect.y) > 16000;
37
38layout(when=!highPrecision) uniform half4 proxyRectHalf;
39layout(when=highPrecision) uniform float4 proxyRectFloat;
40uniform half profileSize;
41
42
43@class {
44    static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrProxyProvider* proxyProvider,
45                                                          float sigma) {
46        unsigned int profileSize = SkScalarCeilToInt(6 * sigma);
47
48        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
49        GrUniqueKey key;
50        GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
51        builder[0] = profileSize;
52        builder.finish();
53
54        sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey(
55                                                                    key, kTopLeft_GrSurfaceOrigin));
56        if (!blurProfile) {
57            SkImageInfo ii = SkImageInfo::MakeA8(profileSize, 1);
58
59            SkBitmap bitmap;
60            if (!bitmap.tryAllocPixels(ii)) {
61                return nullptr;
62            }
63
64            SkBlurMask::ComputeBlurProfile(bitmap.getAddr8(0, 0), profileSize, sigma);
65            bitmap.setImmutable();
66
67            sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
68            if (!image) {
69                return nullptr;
70            }
71
72            blurProfile = proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags,
73                                                            1, SkBudgeted::kYes,
74                                                            SkBackingFit::kExact);
75            if (!blurProfile) {
76                return nullptr;
77            }
78
79            SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
80            proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
81        }
82
83        return blurProfile;
84    }
85}
86
87@make {
88     static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
89                                                      const GrShaderCaps& caps,
90                                                      const SkRect& rect, float sigma) {
91         if (!caps.floatIs32Bits()) {
92             // We promote the rect uniform from half to float when it has large values for
93             // precision. If we don't have full float then fail.
94             if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
95                 SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f ||
96                 SkScalarAbs(rect.width()) > 16000.f || SkScalarAbs(rect.height()) > 16000.f) {
97                 return nullptr;
98             }
99         }
100         int doubleProfileSize = SkScalarCeilToInt(12*sigma);
101
102         if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
103             // if the blur sigma is too large so the gaussian overlaps the whole
104             // rect in either direction, fall back to CPU path for now.
105             return nullptr;
106         }
107
108         sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma));
109         if (!blurProfile) {
110            return nullptr;
111         }
112
113         return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(
114            rect, sigma, std::move(blurProfile),
115            GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp)));
116     }
117}
118
119void main() {
120    @if (highPrecision) {
121        float2 translatedPos = sk_FragCoord.xy - rect.xy;
122        float width = rect.z - rect.x;
123        float height = rect.w - rect.y;
124        float2 smallDims = float2(width - profileSize, height - profileSize);
125        float center = 2 * floor(profileSize / 2 + 0.25) - 1;
126        float2 wh = smallDims - float2(center, center);
127        half hcoord = half(((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize);
128        half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a;
129        half vcoord = half(((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize);
130        half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a;
131        sk_OutColor = sk_InColor * hlookup * vlookup;
132    } else {
133        half2 translatedPos = half2(sk_FragCoord.xy - rect.xy);
134        half width = half(rect.z - rect.x);
135        half height = half(rect.w - rect.y);
136        half2 smallDims = half2(width - profileSize, height - profileSize);
137        half center = 2 * floor(profileSize / 2 + 0.25) - 1;
138        half2 wh = smallDims - half2(center, center);
139        half hcoord = ((half(abs(translatedPos.x - 0.5 * width)) - 0.5 * wh.x)) / profileSize;
140        half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a;
141        half vcoord = ((half(abs(translatedPos.y - 0.5 * height)) - 0.5 * wh.y)) / profileSize;
142        half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a;
143        sk_OutColor = sk_InColor * hlookup * vlookup;
144    }
145}
146
147@setData(pdman) {
148    pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma));
149}
150
151@optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
152
153@test(data) {
154    float sigma = data->fRandom->nextRangeF(3,8);
155    float width = data->fRandom->nextRangeF(200,300);
156    float height = data->fRandom->nextRangeF(200,300);
157    return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(),
158                                  SkRect::MakeWH(width, height), sigma);
159}
160