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 13 in uniform float4 rect; 14 in float sigma; 15 in 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. 32 layout(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 36 layout(when=!highPrecision) uniform half4 proxyRectHalf; 37 layout(when=highPrecision) uniform float4 proxyRectFloat; 38 uniform 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); 49 builder[0] = profileSize; 50 builder.finish(); 51 52 sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey( 53 key, kTopLeft_GrSurfaceOrigin)); 54 if (!blurProfile) { 55 GrSurfaceDesc texDesc; 56 texDesc.fOrigin = kTopLeft_GrSurfaceOrigin; 57 texDesc.fWidth = profileSize; 58 texDesc.fHeight = 1; 59 texDesc.fConfig = kAlpha_8_GrPixelConfig; 60 61 std::unique_ptr<uint8_t[]> profile(SkBlurMask::ComputeBlurProfile(sigma)); 62 63 blurProfile = proxyProvider->createTextureProxy(texDesc, SkBudgeted::kYes, 64 profile.get(), 0); 65 if (!blurProfile) { 66 return nullptr; 67 } 68 69 SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin); 70 proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get()); 71 } 72 73 return blurProfile; 74 } 75 } 76 77 @make { 78 static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider, 79 const SkRect& rect, float sigma) { 80 int doubleProfileSize = SkScalarCeilToInt(12*sigma); 81 82 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) { 83 // if the blur sigma is too large so the gaussian overlaps the whole 84 // rect in either direction, fall back to CPU path for now. 85 return nullptr; 86 } 87 88 sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma)); 89 if (!blurProfile) { 90 return nullptr; 91 } 92 93 return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect( 94 rect, sigma, std::move(blurProfile), 95 GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp))); 96 } 97 } 98 99 void main() { 100 @if (highPrecision) { 101 float2 translatedPos = sk_FragCoord.xy - rect.xy; 102 float width = rect.z - rect.x; 103 float height = rect.w - rect.y; 104 float2 smallDims = float2(width - profileSize, height - profileSize); 105 float center = 2 * floor(profileSize / 2 + 0.25) - 1; 106 float2 wh = smallDims - float2(center, center); 107 half hcoord = ((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize; 108 half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a; 109 half vcoord = ((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize; 110 half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a; 111 sk_OutColor = sk_InColor * hlookup * vlookup; 112 } else { 113 half2 translatedPos = sk_FragCoord.xy - rect.xy; 114 half width = rect.z - rect.x; 115 half height = rect.w - rect.y; 116 half2 smallDims = half2(width - profileSize, height - profileSize); 117 half center = 2 * floor(profileSize / 2 + 0.25) - 1; 118 half2 wh = smallDims - float2(center, center); 119 half hcoord = ((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize; 120 half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a; 121 half vcoord = ((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize; 122 half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a; 123 sk_OutColor = sk_InColor * hlookup * vlookup; 124 } 125 } 126 127 @setData(pdman) { 128 pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma)); 129 } 130 131 @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag } 132 133 @test(data) { 134 float sigma = data->fRandom->nextRangeF(3,8); 135 float width = data->fRandom->nextRangeF(200,300); 136 float height = data->fRandom->nextRangeF(200,300); 137 return GrRectBlurEffect::Make(data->proxyProvider(), SkRect::MakeWH(width, height), sigma); 138 } 139