| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| @header { |
| #include "GrProxyProvider.h" |
| #include "../effects/SkBlurMask.h" |
| } |
| |
| in uniform float4 rect; |
| in float sigma; |
| in uniform sampler2D blurProfile; |
| |
| @constructorParams { |
| GrSamplerState samplerParams |
| } |
| |
| @samplerParams(blurProfile) { |
| samplerParams |
| } |
| |
| // in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger than |
| // that, the shader math will end up with infinities and result in the blur effect not working |
| // correctly. To avoid this, we switch into highp when the coordinates are too big. As 2^14 is the |
| // minimum range but the actual range can be bigger, we might end up switching to highp sooner than |
| // strictly necessary, but most devices that have a bigger range for mediump also have mediump being |
| // exactly the same as highp (e.g. all non-OpenGL ES devices), and thus incur no additional penalty |
| // for the switch. |
| layout(key) bool highPrecision = abs(rect.x) > 16000 || abs(rect.y) > 16000 || |
| abs(rect.z) > 16000 || abs(rect.w) > 16000 || |
| abs(rect.z - rect.x) > 16000 || abs(rect.w - rect.y) > 16000; |
| |
| layout(when=!highPrecision) uniform half4 proxyRectHalf; |
| layout(when=highPrecision) uniform float4 proxyRectFloat; |
| uniform half profileSize; |
| |
| |
| @class { |
| static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrProxyProvider* proxyProvider, |
| float sigma) { |
| unsigned int profileSize = SkScalarCeilToInt(6 * sigma); |
| |
| static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
| GrUniqueKey key; |
| GrUniqueKey::Builder builder(&key, kDomain, 1); |
| builder[0] = profileSize; |
| builder.finish(); |
| |
| sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey( |
| key, kTopLeft_GrSurfaceOrigin)); |
| if (!blurProfile) { |
| SkImageInfo ii = SkImageInfo::MakeA8(profileSize, 1); |
| |
| SkBitmap bitmap; |
| if (!bitmap.tryAllocPixels(ii)) { |
| return nullptr; |
| } |
| |
| SkBlurMask::ComputeBlurProfile(bitmap.getAddr8(0, 0), profileSize, sigma); |
| bitmap.setImmutable(); |
| |
| sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); |
| if (!image) { |
| return nullptr; |
| } |
| |
| blurProfile = proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, |
| 1, SkBudgeted::kYes, |
| SkBackingFit::kExact); |
| if (!blurProfile) { |
| return nullptr; |
| } |
| |
| SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin); |
| proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get()); |
| } |
| |
| return blurProfile; |
| } |
| } |
| |
| @make { |
| static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider, |
| const SkRect& rect, float sigma) { |
| int doubleProfileSize = SkScalarCeilToInt(12*sigma); |
| |
| if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) { |
| // if the blur sigma is too large so the gaussian overlaps the whole |
| // rect in either direction, fall back to CPU path for now. |
| return nullptr; |
| } |
| |
| sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma)); |
| if (!blurProfile) { |
| return nullptr; |
| } |
| |
| return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect( |
| rect, sigma, std::move(blurProfile), |
| GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp))); |
| } |
| } |
| |
| void main() { |
| @if (highPrecision) { |
| float2 translatedPos = sk_FragCoord.xy - rect.xy; |
| float width = rect.z - rect.x; |
| float height = rect.w - rect.y; |
| float2 smallDims = float2(width - profileSize, height - profileSize); |
| float center = 2 * floor(profileSize / 2 + 0.25) - 1; |
| float2 wh = smallDims - float2(center, center); |
| half hcoord = ((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize; |
| half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a; |
| half vcoord = ((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize; |
| half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a; |
| sk_OutColor = sk_InColor * hlookup * vlookup; |
| } else { |
| half2 translatedPos = sk_FragCoord.xy - rect.xy; |
| half width = rect.z - rect.x; |
| half height = rect.w - rect.y; |
| half2 smallDims = half2(width - profileSize, height - profileSize); |
| half center = 2 * floor(profileSize / 2 + 0.25) - 1; |
| half2 wh = smallDims - float2(center, center); |
| half hcoord = ((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize; |
| half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a; |
| half vcoord = ((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize; |
| half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a; |
| sk_OutColor = sk_InColor * hlookup * vlookup; |
| } |
| } |
| |
| @setData(pdman) { |
| pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma)); |
| } |
| |
| @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag } |
| |
| @test(data) { |
| float sigma = data->fRandom->nextRangeF(3,8); |
| float width = data->fRandom->nextRangeF(200,300); |
| float height = data->fRandom->nextRangeF(200,300); |
| return GrRectBlurEffect::Make(data->proxyProvider(), SkRect::MakeWH(width, height), sigma); |
| } |