• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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