1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 
19 #include "KawaseBlurFilter.h"
20 #include <SkAlphaType.h>
21 #include <SkBlendMode.h>
22 #include <SkCanvas.h>
23 #include <SkImageInfo.h>
24 #include <SkPaint.h>
25 #include <SkRRect.h>
26 #include <SkRuntimeEffect.h>
27 #include <SkShader.h>
28 #include <SkSize.h>
29 #include <SkString.h>
30 #include <SkSurface.h>
31 #include <SkTileMode.h>
32 #include <include/gpu/GpuTypes.h>
33 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
34 #include <log/log.h>
35 #include <utils/Trace.h>
36 
37 namespace android {
38 namespace renderengine {
39 namespace skia {
40 
KawaseBlurFilter()41 KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
42     SkString blurString(
43         "uniform shader child;"
44         "uniform float in_blurOffset;"
45 
46         "half4 main(float2 xy) {"
47             "half4 c = child.eval(xy);"
48             "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));"
49             "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));"
50             "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));"
51             "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));"
52             "return half4(c.rgb * 0.2, 1.0);"
53         "}");
54 
55     auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
56     if (!blurEffect) {
57         LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
58     }
59     mBlurEffect = std::move(blurEffect);
60 }
61 
62 // Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an
63 // SkImage.
makeImage(SkSurface * surface,SkRuntimeShaderBuilder * builder)64 static sk_sp<SkImage> makeImage(SkSurface* surface, SkRuntimeShaderBuilder* builder) {
65     sk_sp<SkShader> shader = builder->makeShader(nullptr);
66     if (!shader) {
67         return nullptr;
68     }
69     SkPaint paint;
70     paint.setShader(std::move(shader));
71     paint.setBlendMode(SkBlendMode::kSrc);
72     surface->getCanvas()->drawPaint(paint);
73     return surface->makeImageSnapshot();
74 }
75 
generate(SkiaGpuContext * context,const uint32_t blurRadius,const sk_sp<SkImage> input,const SkRect & blurRect) const76 sk_sp<SkImage> KawaseBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
77                                           const sk_sp<SkImage> input,
78                                           const SkRect& blurRect) const {
79     LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
80     LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__);
81 
82     if (blurRadius == 0) {
83         return input;
84     }
85 
86     // Kawase is an approximation of Gaussian, but it behaves differently from it.
87     // A radius transformation is required for approximating them, and also to introduce
88     // non-integer steps, necessary to smoothly interpolate large radii.
89     float tmpRadius = (float)blurRadius / 2.0f;
90     uint32_t numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
91     float radiusByPasses = tmpRadius / (float)numberOfPasses;
92 
93     // create blur surface with the bit depth and colorspace of the original surface
94     SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
95                                                        std::ceil(blurRect.height() * kInputScale));
96 
97     // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
98     // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
99     // but instead we must do the inverse.
100     SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
101     blurMatrix.postScale(kInputScale, kInputScale);
102 
103     // start by downscaling and doing the first blur pass
104     SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
105     SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
106     blurBuilder.child("child") =
107             input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
108     blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
109 
110     sk_sp<SkSurface> surface = context->createRenderTarget(scaledInfo);
111     LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__);
112     sk_sp<SkImage> tmpBlur = makeImage(surface.get(), &blurBuilder);
113 
114     // And now we'll build our chain of scaled blur stages. If there is more than one pass,
115     // create a second surface and ping pong between them.
116     sk_sp<SkSurface> surfaceTwo;
117     if (numberOfPasses <= 1) {
118         LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null", __func__);
119     } else {
120         surfaceTwo = surface->makeSurface(scaledInfo);
121         LOG_ALWAYS_FATAL_IF(!surfaceTwo, "%s: Failed to create second blur surface!", __func__);
122 
123         for (auto i = 1; i < numberOfPasses; i++) {
124             LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
125             blurBuilder.child("child") =
126                     tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
127             blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
128             tmpBlur = makeImage(surfaceTwo.get(), &blurBuilder);
129             using std::swap;
130             swap(surface, surfaceTwo);
131         }
132     }
133 
134     return tmpBlur;
135 }
136 
137 } // namespace skia
138 } // namespace renderengine
139 } // namespace android
140