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 in float sigma;
      9 layout(ctype=SkRect) in float4 rect;
     10 in uniform half cornerRadius;
     11 in uniform sampler2D ninePatchSampler;
     12 layout(ctype=SkRect) uniform float4 proxyRect;
     13 uniform half blurRadius;
     14 
     15 @header {
     16     #include "GrCaps.h"
     17     #include "GrClip.h"
     18     #include "GrContext.h"
     19     #include "GrPaint.h"
     20     #include "GrProxyProvider.h"
     21     #include "GrRecordingContext.h"
     22     #include "GrRecordingContextPriv.h"
     23     #include "GrRenderTargetContext.h"
     24     #include "GrStyle.h"
     25     #include "SkBlurMaskFilter.h"
     26     #include "SkBlurPriv.h"
     27     #include "SkGpuBlurUtils.h"
     28     #include "SkRRectPriv.h"
     29 }
     30 
     31 @class {
     32     static sk_sp<GrTextureProxy> find_or_create_rrect_blur_mask(GrRecordingContext* context,
     33                                                                 const SkRRect& rrectToDraw,
     34                                                                 const SkISize& size,
     35                                                                 float xformedSigma) {
     36         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
     37         GrUniqueKey key;
     38         GrUniqueKey::Builder builder(&key, kDomain, 9, "RoundRect Blur Mask");
     39         builder[0] = SkScalarCeilToInt(xformedSigma-1/6.0f);
     40 
     41         int index = 1;
     42         for (auto c : { SkRRect::kUpperLeft_Corner,  SkRRect::kUpperRight_Corner,
     43                         SkRRect::kLowerRight_Corner, SkRRect::kLowerLeft_Corner }) {
     44             SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) &&
     45                      SkScalarIsInt(rrectToDraw.radii(c).fY));
     46             builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
     47             builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
     48         }
     49         builder.finish();
     50 
     51         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     52 
     53         sk_sp<GrTextureProxy> mask(proxyProvider->findOrCreateProxyByUniqueKey(
     54                                                                  key, kBottomLeft_GrSurfaceOrigin));
     55         if (!mask) {
     56             GrBackendFormat format =
     57                 context->priv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType);
     58             // TODO: this could be approx but the texture coords will need to be updated
     59             sk_sp<GrRenderTargetContext> rtc(
     60                     context->priv().makeDeferredRenderTargetContextWithFallback(
     61                                                 format, SkBackingFit::kExact, size.fWidth,
     62                                                 size.fHeight, kAlpha_8_GrPixelConfig, nullptr));
     63             if (!rtc) {
     64                 return nullptr;
     65             }
     66 
     67             GrPaint paint;
     68 
     69             rtc->clear(nullptr, SK_PMColor4fTRANSPARENT,
     70                        GrRenderTargetContext::CanClearFullscreen::kYes);
     71             rtc->drawRRect(GrNoClip(), std::move(paint), GrAA::kYes, SkMatrix::I(), rrectToDraw,
     72                            GrStyle::SimpleFill());
     73 
     74             sk_sp<GrTextureProxy> srcProxy(rtc->asTextureProxyRef());
     75             if (!srcProxy) {
     76                 return nullptr;
     77             }
     78             sk_sp<GrRenderTargetContext> rtc2(
     79                       SkGpuBlurUtils::GaussianBlur(context,
     80                                                    std::move(srcProxy),
     81                                                    nullptr,
     82                                                    SkIRect::MakeWH(size.fWidth, size.fHeight),
     83                                                    SkIRect::EmptyIRect(),
     84                                                    xformedSigma,
     85                                                    xformedSigma,
     86                                                    GrTextureDomain::kIgnore_Mode,
     87                                                    kPremul_SkAlphaType,
     88                                                    SkBackingFit::kExact));
     89             if (!rtc2) {
     90                 return nullptr;
     91             }
     92 
     93             mask = rtc2->asTextureProxyRef();
     94             if (!mask) {
     95                 return nullptr;
     96             }
     97             SkASSERT(mask->origin() == kBottomLeft_GrSurfaceOrigin);
     98             proxyProvider->assignUniqueKeyToProxy(key, mask.get());
     99         }
    100 
    101         return mask;
    102     }
    103 }
    104 
    105 @optimizationFlags {
    106     kCompatibleWithCoverageAsAlpha_OptimizationFlag
    107 }
    108 
    109 @make {
    110     static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext* context,
    111                                                      float sigma,
    112                                                      float xformedSigma,
    113                                                      const SkRRect& srcRRect,
    114                                                      const SkRRect& devRRect);
    115 }
    116 
    117 @cpp {
    118     std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::Make(GrRecordingContext* context,
    119                                                                  float sigma,
    120                                                                  float xformedSigma,
    121                                                                  const SkRRect& srcRRect,
    122                                                                  const SkRRect& devRRect) {
    123         SkASSERT(!SkRRectPriv::IsCircle(devRRect) && !devRRect.isRect()); // Should've been caught up-stream
    124 
    125         // TODO: loosen this up
    126         if (!SkRRectPriv::IsSimpleCircular(devRRect)) {
    127             return nullptr;
    128         }
    129 
    130         // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
    131         // sufficiently small relative to both the size of the corner radius and the
    132         // width (and height) of the rrect.
    133         SkRRect rrectToDraw;
    134         SkISize size;
    135         SkScalar ignored[kSkBlurRRectMaxDivisions];
    136         int ignoredSize;
    137         uint32_t ignored32;
    138 
    139         bool ninePatchable = SkComputeBlurredRRectParams(srcRRect, devRRect,
    140                                                          SkRect::MakeEmpty(),
    141                                                          sigma, xformedSigma,
    142                                                          &rrectToDraw, &size,
    143                                                          ignored, ignored,
    144                                                          ignored, ignored,
    145                                                          &ignoredSize, &ignoredSize,
    146                                                          &ignored32);
    147         if (!ninePatchable) {
    148             return nullptr;
    149         }
    150 
    151         sk_sp<GrTextureProxy> mask(find_or_create_rrect_blur_mask(context, rrectToDraw,
    152                                                                   size, xformedSigma));
    153         if (!mask) {
    154             return nullptr;
    155         }
    156 
    157         return std::unique_ptr<GrFragmentProcessor>(
    158                 new GrRRectBlurEffect(xformedSigma, devRRect.getBounds(),
    159                                       SkRRectPriv::GetSimpleRadii(devRRect).fX, std::move(mask)));
    160     }
    161 }
    162 
    163 @test(d) {
    164     SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
    165     SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
    166     SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
    167     SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
    168     SkRRect rrect;
    169     rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
    170     return GrRRectBlurEffect::Make(d->context(), sigma, sigma, rrect, rrect);
    171 }
    172 
    173 void main() {
    174     // warp the fragment position to the appropriate part of the 9patch blur texture
    175 
    176     half2 rectCenter = half2((proxyRect.xy + proxyRect.zw) / 2.0);
    177     half2 translatedFragPos = half2(sk_FragCoord.xy - proxyRect.xy);
    178     half threshold = cornerRadius + 2.0 * blurRadius;
    179     half2 middle = half2(proxyRect.zw - proxyRect.xy - 2.0 * threshold);
    180 
    181     if (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x + threshold)) {
    182             translatedFragPos.x = threshold;
    183     } else if (translatedFragPos.x >= (middle.x + threshold)) {
    184         translatedFragPos.x -= middle.x - 1.0;
    185     }
    186 
    187     if (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {
    188         translatedFragPos.y = threshold;
    189     } else if (translatedFragPos.y >= (middle.y + threshold)) {
    190         translatedFragPos.y -= middle.y - 1.0;
    191     }
    192 
    193     half2 proxyDims = half2(2.0 * threshold + 1.0);
    194     half2 texCoord = translatedFragPos / proxyDims;
    195 
    196     sk_OutColor = sk_InColor * texture(ninePatchSampler, texCoord);
    197 }
    198 
    199 @setData(pdman) {
    200     float blurRadiusValue = 3.f * SkScalarCeilToScalar(sigma - 1 / 6.0f);
    201     pdman.set1f(blurRadius, blurRadiusValue);
    202 
    203     SkRect outset = rect;
    204     outset.outset(blurRadiusValue, blurRadiusValue);
    205     pdman.set4f(proxyRect, outset.fLeft, outset.fTop, outset.fRight, outset.fBottom);
    206 }
    207