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