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