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