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