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