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 "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 173void 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