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