1 /*
2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "render/rs_kawase_blur.h"
17 #include "platform/common/rs_log.h"
18 #include "platform/common/rs_system_properties.h"
19 #include "common/rs_optional_trace.h"
20
21 namespace OHOS {
22 namespace Rosen {
23 #ifndef USE_ROSEN_DRAWING
KawaseBlurFilter()24 KawaseBlurFilter::KawaseBlurFilter()
25 {
26 SkString blurString(R"(
27 uniform shader imageInput;
28 uniform float2 in_blurOffset;
29 uniform float2 in_maxSizeXY;
30
31 half4 main(float2 xy) {
32 half4 c = imageInput.eval(xy);
33 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
34 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
35 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
36 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
37 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
38 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
39 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
40 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
41 return half4(c.rgb * 0.2, 1.0);
42 }
43 )");
44
45 SkString mixString(R"(
46 uniform shader blurredInput;
47 uniform shader originalInput;
48 uniform float mixFactor;
49 half4 main(float2 xy) {
50 return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor));
51 }
52 )");
53
54 auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
55 if (!blurEffect) {
56 ROSEN_LOGE("KawaseBlurFilter::RuntimeShader blurEffect error: %s\n", error.c_str());
57 return;
58 }
59 blurEffect_ = std::move(blurEffect);
60
61 auto [mixEffect, error2] = SkRuntimeEffect::MakeForShader(mixString);
62 if (!mixEffect) {
63 ROSEN_LOGE("KawaseBlurFilter::RuntimeShader mixEffect error: %s\n", error2.c_str());
64 return;
65 }
66 mixEffect_ = std::move(mixEffect);
67 }
68
69 KawaseBlurFilter::~KawaseBlurFilter() = default;
70
GetShaderTransform(const SkCanvas * canvas,const SkRect & blurRect,float scale)71 SkMatrix KawaseBlurFilter::GetShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale)
72 {
73 auto matrix = SkMatrix::Scale(scale, scale);
74 matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop));
75 return matrix;
76 }
77
ApplyKawaseBlur(SkCanvas & canvas,const sk_sp<SkImage> & image,const KawaseParameter & param)78 bool KawaseBlurFilter::ApplyKawaseBlur(SkCanvas& canvas, const sk_sp<SkImage>& image, const KawaseParameter& param)
79 {
80 auto src = param.src;
81 auto dst = param.dst;
82 int inputRadius = param.radius;
83 if (inputRadius <= 0) {
84 SkPaint paint;
85 if (param.colorFilter) {
86 paint.setColorFilter(param.colorFilter);
87 }
88 SkMatrix inputMatrix = SkMatrix::Translate(-src.fLeft, -src.fTop);
89 inputMatrix.postConcat(SkMatrix::Translate(dst.fLeft, dst.fTop));
90 SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
91 const auto inputShader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, &inputMatrix);
92 paint.setShader(inputShader);
93 canvas.drawRect(dst, paint);
94 return true;
95 }
96 if (!blurEffect_ || !mixEffect_) {
97 return false;
98 }
99 ComputeRadiusAndScale(param.radius);
100 RS_OPTIONAL_TRACE_BEGIN("ApplyKawaseBlur " + GetDescription());
101 int maxPasses = supportLargeRadius ? kMaxPassesLargeRadius : kMaxPasses;
102 float dilatedConvolutionFactor = supportLargeRadius ? kDilatedConvolutionLargeRadius : kDilatedConvolution;
103 float tmpRadius = static_cast<float>(blurRadius_) / dilatedConvolutionFactor;
104 int numberOfPasses = std::min(maxPasses, std::max(static_cast<int>(ceil(tmpRadius)), 1)); // 1 : min pass num
105 float radiusByPasses = tmpRadius / numberOfPasses;
106 SkImageInfo scaledInfo = image->imageInfo().makeWH(std::ceil(dst.width() * blurScale_),
107 std::ceil(dst.height() * blurScale_));
108 SkMatrix blurMatrix = SkMatrix::Translate(-src.fLeft, -src.fTop);
109 blurMatrix.postScale(blurScale_, blurScale_);
110 SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
111 SkRuntimeShaderBuilder blurBuilder(blurEffect_);
112 blurBuilder.child("imageInput") = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
113 blurBuilder.uniform("in_blurOffset") = SkV2{radiusByPasses * blurScale_, radiusByPasses * blurScale_};
114 blurBuilder.uniform("in_maxSizeXY") = SkV2{dst.width() * blurScale_, dst.height() * blurScale_};
115 sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(canvas.recordingContext(), nullptr, scaledInfo, false));
116 // And now we'll build our chain of scaled blur stages
117 for (auto i = 1; i < numberOfPasses; i++) {
118 const float stepScale = static_cast<float>(i) * blurScale_;
119 blurBuilder.child("imageInput") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
120 blurBuilder.uniform("in_blurOffset") = SkV2{radiusByPasses * stepScale, radiusByPasses * stepScale};
121 blurBuilder.uniform("in_maxSizeXY") = SkV2{dst.width() * blurScale_, dst.height() * blurScale_};
122 tmpBlur = blurBuilder.makeImage(canvas.recordingContext(), nullptr, scaledInfo, false);
123 }
124 RS_OPTIONAL_TRACE_END();
125 return ApplyBlur(canvas, image, tmpBlur, param);
126 }
127
ApplyBlur(SkCanvas & canvas,const sk_sp<SkImage> & image,const sk_sp<SkImage> & blurImage,const KawaseParameter & param) const128 bool KawaseBlurFilter::ApplyBlur(SkCanvas& canvas, const sk_sp<SkImage>& image, const sk_sp<SkImage>& blurImage,
129 const KawaseParameter& param) const
130 {
131 auto src = param.src;
132 auto dst = param.dst;
133 float invBlurScale = 1.0f / blurScale_;
134 SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
135 SkImageInfo scaledInfo = image->imageInfo().makeWH(std::ceil(dst.width() * blurScale_),
136 std::ceil(dst.height() * blurScale_));
137 const auto blurMatrix = GetShaderTransform(&canvas, dst, invBlurScale);
138 const auto blurShader = blurImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, &blurMatrix);
139 SkPaint paint;
140 paint.setAlphaf(param.alpha);
141 if (param.colorFilter) {
142 paint.setColorFilter(param.colorFilter);
143 }
144 if (blurRadius_ < kMaxCrossFadeRadius) {
145 SkMatrix inputMatrix = SkMatrix::Translate(-src.fLeft, -src.fTop);
146 inputMatrix.postConcat(SkMatrix::Translate(dst.fLeft, dst.fTop));
147 SkRuntimeShaderBuilder mixBuilder(mixEffect_);
148 mixBuilder.child("blurredInput") = blurShader;
149 mixBuilder.child("originalInput") = image->makeShader(
150 SkTileMode::kClamp, SkTileMode::kClamp, linear, inputMatrix);
151 mixBuilder.uniform("mixFactor") = std::min(1.0f, blurRadius_ * 1.f / kMaxCrossFadeRadius);
152 paint.setShader(mixBuilder.makeShader(nullptr, true));
153 } else {
154 paint.setShader(blurShader);
155 }
156 canvas.drawRect(dst, paint);
157 return true;
158 }
159
ComputeRadiusAndScale(int radius)160 void KawaseBlurFilter::ComputeRadiusAndScale(int radius)
161 {
162 static constexpr int noiseFactor = 3; // 3 : smooth the radius change
163 blurRadius_ = radius * 4 / noiseFactor * noiseFactor; // 4 : scale between gauss radius and kawase
164 AdjustRadiusAndScale();
165 }
166
AdjustRadiusAndScale()167 void KawaseBlurFilter::AdjustRadiusAndScale()
168 {
169 static constexpr int radiusStep1 = 50; // 50 : radius step1
170 static constexpr int radiusStep2 = 150; // 150 : radius step2
171 static constexpr int radiusStep3 = 480; // 480 : radius step3
172 static constexpr float scaleFactor1 = 0.25f; // 0.25 : downSample scale for step1
173 static constexpr float scaleFactor2 = 0.125f; // 0.125 : downSample scale for step2
174 static constexpr float scaleFactor3 = 0.03f; // 0.03 : downSample scale for step3
175 float scale = baseBlurScale;
176 if (blurRadius_ > radiusStep3) {
177 scale = scaleFactor3;
178 } else if (blurRadius_ > radiusStep2) {
179 scale = scaleFactor2;
180 } else if (blurRadius_ > radiusStep1) {
181 scale = scaleFactor1;
182 }
183 blurScale_ = scale;
184 }
185
GetDescription() const186 std::string KawaseBlurFilter::GetDescription() const
187 {
188 return "blur radius is " + std::to_string(blurRadius_);
189 }
190 #endif
191 } // namespace Rosen
192 } // namespace OHOS