• 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 "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