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