Home | History | Annotate | Download | only in effects
      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