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 #include "include/gpu/GrDirectContext.h"
21 #include "effect/runtime_shader_builder.h"
22
23 namespace OHOS {
24 namespace Rosen {
25 // Advanced Filter
26 #define PROPERTY_HIGPU_VERSION "const.gpu.vendor"
27 #define PROPERTY_DEBUG_SUPPORT_AF "persist.sys.graphic.supports_af"
28 static constexpr uint32_t BLUR_SAMPLE_COUNT = 5;
29
30 // Advanced Filter: we can get normalized uv offset from width and height
31 struct OffsetInfo {
32 float offsetX;
33 float offsetY;
34 int width;
35 int height;
36 };
37
38 // Advanced Filter
IsAdvancedFilterUsable()39 static bool IsAdvancedFilterUsable()
40 {
41 return false;
42 }
43
44 static const bool IS_ADVANCED_FILTER_USABLE_CHECK_ONCE = IsAdvancedFilterUsable();
45
KawaseBlurFilter()46 KawaseBlurFilter::KawaseBlurFilter()
47 {
48 std::string blurString(
49 R"(
50 uniform shader imageInput;
51 uniform float2 in_blurOffset;
52 uniform float2 in_maxSizeXY;
53
54 half4 main(float2 xy) {
55 half4 c = imageInput.eval(xy);
56 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
57 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
58 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
59 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
60 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
61 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
62 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
63 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
64 return half4(c.rgb * 0.2, 1.0);
65 }
66 )");
67
68 std::string mixString(
69 R"(
70 uniform shader blurredInput;
71 uniform shader originalInput;
72 uniform float mixFactor;
73 uniform float inColorFactor;
74
75 highp float random(float2 xy) {
76 float t = dot(xy, float2(78.233, 12.9898));
77 return fract(sin(t) * 43758.5453);
78 }
79 half4 main(float2 xy) {
80 highp float noiseGranularity = inColorFactor / 255.0;
81 half4 finalColor = mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor);
82 float noise = mix(-noiseGranularity, noiseGranularity, random(xy));
83 finalColor.rgb += noise;
84 return finalColor;
85 }
86 )");
87
88 auto blurEffect = Drawing::RuntimeEffect::CreateForShader(blurString);
89 if (!blurEffect) {
90 ROSEN_LOGE("KawaseBlurFilter::RuntimeShader blurEffect create failed");
91 return;
92 }
93 blurEffect_ = std::move(blurEffect);
94
95 // Advanced Filter
96 if (IS_ADVANCED_FILTER_USABLE_CHECK_ONCE) {
97 setupBlurEffectAdvancedFilter();
98 }
99
100 auto mixEffect = Drawing::RuntimeEffect::CreateForShader(mixString);
101 if (!mixEffect) {
102 ROSEN_LOGE("KawaseBlurFilter::RuntimeShader mixEffect create failed");
103 return;
104 }
105 mixEffect_ = std::move(mixEffect);
106
107 SetupSimpleFilter();
108 }
109
110 KawaseBlurFilter::~KawaseBlurFilter() = default;
111
112 // Advanced Filter
setupBlurEffectAdvancedFilter()113 void KawaseBlurFilter::setupBlurEffectAdvancedFilter()
114 {
115 std::string blurStringAF(
116 R"(
117 uniform shader imageInput;
118 uniform float2 in_blurOffset[5];
119
120 half4 main(float2 xy) {
121 half4 c = half4(0, 0, 0, 0);
122 for (int i = 0; i < 5; ++i) {
123 c += imageInput.eval(float2(xy.x + in_blurOffset[i].x, xy.y + in_blurOffset[i].y));
124 }
125 return half4(c.rgb * 0.2, 1.0);
126 }
127 )");
128
129 Drawing::RuntimeEffectOptions ops;
130 ops.useAF = true;
131 auto blurEffectAF = Drawing::RuntimeEffect::CreateForShader(blurStringAF, ops);
132 if (!blurEffectAF) {
133 ROSEN_LOGE("%s: RuntimeShader blurEffectAF create failed", __func__);
134 return;
135 }
136 blurEffectAF_ = std::move(blurEffectAF);
137 }
138
SetupSimpleFilter()139 void KawaseBlurFilter::SetupSimpleFilter()
140 {
141 std::string simpleShader(
142 R"(
143 uniform shader imageInput;
144 half4 main(float2 xy) {
145 return imageInput.eval(xy);
146 }
147 )");
148
149 auto simpleFilter = Drawing::RuntimeEffect::CreateForShader(simpleShader);
150 if (!simpleFilter) {
151 ROSEN_LOGE("KawaseBlurFilter::RuntimeShader Failed to create simple filter");
152 return;
153 }
154 simpleFilter_ = std::move(simpleFilter);
155 }
156
getNormalizedOffset(SkV2 * offsets,const uint32_t offsetCount,const OffsetInfo & offsetInfo)157 static void getNormalizedOffset(SkV2* offsets, const uint32_t offsetCount, const OffsetInfo& offsetInfo)
158 {
159 if (offsets == nullptr || offsetCount != BLUR_SAMPLE_COUNT) {
160 ROSEN_LOGE("%s: Invalid offsets.", __func__);
161 return;
162 }
163 if (std::fabs(offsetInfo.width) < 1e-6 || std::fabs(offsetInfo.height) < 1e-6) {
164 ROSEN_LOGE("%s: Invalid width or height.", __func__);
165 return;
166 }
167 SkV2 normalizedOffsets[BLUR_SAMPLE_COUNT] = {
168 SkV2{0.0f, 0.0f},
169 SkV2{offsetInfo.offsetX / offsetInfo.width, offsetInfo.offsetY / offsetInfo.height},
170 SkV2{-offsetInfo.offsetX / offsetInfo.width, offsetInfo.offsetY / offsetInfo.height},
171 SkV2{offsetInfo.offsetX / offsetInfo.width, -offsetInfo.offsetY / offsetInfo.height},
172 SkV2{-offsetInfo.offsetX / offsetInfo.width, -offsetInfo.offsetY / offsetInfo.height}
173 };
174 for (uint32_t i = 0; i < BLUR_SAMPLE_COUNT; ++i) {
175 offsets[i] = normalizedOffsets[i];
176 }
177 }
178
GetShaderTransform(const Drawing::Canvas * canvas,const Drawing::Rect & blurRect,float scaleW,float scaleH)179 Drawing::Matrix KawaseBlurFilter::GetShaderTransform(const Drawing::Canvas* canvas, const Drawing::Rect& blurRect,
180 float scaleW, float scaleH)
181 {
182 Drawing::Matrix matrix;
183 matrix.SetScale(scaleW, scaleH);
184 Drawing::Matrix translateMatrix;
185 translateMatrix.Translate(blurRect.GetLeft(), blurRect.GetTop());
186 matrix.PostConcat(translateMatrix);
187 return matrix;
188 }
189
CheckInputImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const KawaseParameter & param,std::shared_ptr<Drawing::Image> & checkedImage)190 void KawaseBlurFilter::CheckInputImage(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
191 const KawaseParameter& param, std::shared_ptr<Drawing::Image>& checkedImage)
192 {
193 auto src = param.src;
194 auto srcRect = Drawing::RectI(src.GetLeft(), src.GetTop(), src.GetRight(), src.GetBottom());
195 if (image->GetImageInfo().GetBound() != srcRect) {
196 auto resizedImage = std::make_shared<Drawing::Image>();
197 if ((canvas.GetGPUContext() == nullptr) || (resizedImage == nullptr)) {
198 ROSEN_LOGE("KawaseBlurFilter::canvas context or resizedImage is null.");
199 return;
200 }
201
202 if (resizedImage->BuildSubset(image, srcRect, *canvas.GetGPUContext())) {
203 checkedImage = resizedImage;
204 ROSEN_LOGD("KawaseBlurFilter::resize image success");
205 } else {
206 ROSEN_LOGE("KawaseBlurFilter::resize image failed, use original image");
207 }
208 }
209 }
210
OutputOriginalImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const KawaseParameter & param)211 void KawaseBlurFilter::OutputOriginalImage(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
212 const KawaseParameter& param)
213 {
214 auto src = param.src;
215 auto dst = param.dst;
216 Drawing::Brush brush;
217 if (param.colorFilter) {
218 Drawing::Filter filter;
219 filter.SetColorFilter(param.colorFilter);
220 brush.SetFilter(filter);
221 }
222 Drawing::Matrix inputMatrix;
223 float scaleW = dst.GetWidth() / image->GetWidth();
224 float scaleH = dst.GetHeight() / image->GetHeight();
225 inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
226 inputMatrix.PostScale(scaleW, scaleH);
227 Drawing::Matrix matrix;
228 matrix.Translate(dst.GetLeft(), dst.GetTop());
229 inputMatrix.PostConcat(matrix);
230 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
231 const auto inputShader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
232 Drawing::TileMode::CLAMP, linear, inputMatrix);
233 brush.SetShaderEffect(inputShader);
234 canvas.AttachBrush(brush);
235 canvas.DrawRect(dst);
236 canvas.DetachBrush();
237 }
238
ApplySimpleFilter(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Matrix & blurMatrix,const Drawing::ImageInfo & scaledInfo,const Drawing::SamplingOptions & linear) const239 std::shared_ptr<Drawing::ShaderEffect> KawaseBlurFilter::ApplySimpleFilter(Drawing::Canvas& canvas,
240 const std::shared_ptr<Drawing::Image>& input, const Drawing::Matrix& blurMatrix,
241 const Drawing::ImageInfo& scaledInfo, const Drawing::SamplingOptions& linear) const
242 {
243 Drawing::RuntimeShaderBuilder simpleBlurBuilder(simpleFilter_);
244 simpleBlurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input, Drawing::TileMode::CLAMP,
245 Drawing::TileMode::CLAMP, linear, blurMatrix));
246 std::shared_ptr<Drawing::Image> tmpSimpleBlur(simpleBlurBuilder.MakeImage(
247 canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
248 return Drawing::ShaderEffect::CreateImageShader(*tmpSimpleBlur, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
249 linear, Drawing::Matrix());
250 }
251
ApplyKawaseBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const KawaseParameter & param)252 bool KawaseBlurFilter::ApplyKawaseBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
253 const KawaseParameter& param)
254 {
255 if (!blurEffect_ || !mixEffect_ || !image) {
256 ROSEN_LOGE("KawaseBlurFilter::shader error, use Gauss instead");
257 return false;
258 }
259 static auto useKawaseOriginal = RSSystemProperties::GetKawaseOriginalEnabled();
260 if (param.radius <= 0 || useKawaseOriginal) {
261 ROSEN_LOGD("KawaseBlurFilter::input invalid radius : %{public}d", param.radius);
262 OutputOriginalImage(canvas, image, param);
263 return true;
264 }
265 auto input = image;
266 CheckInputImage(canvas, image, param, input);
267 ComputeRadiusAndScale(param.radius);
268 RS_OPTIONAL_TRACE_BEGIN("ApplyKawaseBlur " + GetDescription());
269 int maxPasses = supportLargeRadius ? kMaxPassesLargeRadius : kMaxPasses;
270 float dilatedConvolutionFactor = supportLargeRadius ? kDilatedConvolutionLargeRadius : kDilatedConvolution;
271 if (abs(dilatedConvolutionFactor) <= 1e-6) {
272 dilatedConvolutionFactor = 4.6f; // 4.6 : radio between gauss and kawase
273 }
274 float tmpRadius = static_cast<float>(blurRadius_) / dilatedConvolutionFactor;
275 int numberOfPasses = std::min(maxPasses, std::max(static_cast<int>(ceil(tmpRadius)), 1)); // 1 : min pass num
276 float radiusByPasses = tmpRadius / numberOfPasses;
277 ROSEN_LOGD("KawaseBlurFilter::kawase radius : %{public}f, scale : %{public}f, pass num : %{public}d",
278 blurRadius_, blurScale_, numberOfPasses);
279 int width = std::max(static_cast<int>(std::ceil(param.dst.GetWidth())), input->GetWidth());
280 int height = std::max(static_cast<int>(std::ceil(param.dst.GetHeight())), input->GetHeight());
281 auto blurParams = BlurParams{numberOfPasses, width, height, radiusByPasses};
282 auto blurImage = ExecutePingPongBlur(canvas, input, param, blurParams);
283 RS_OPTIONAL_TRACE_END();
284 if (!blurImage) {
285 return false;
286 }
287 return ApplyBlur(canvas, input, blurImage, param);
288 }
289
ExecutePingPongBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const KawaseParameter & inParam,const BlurParams & blur) const290 std::shared_ptr<Drawing::Image> KawaseBlurFilter::ExecutePingPongBlur(Drawing::Canvas& canvas,
291 const std::shared_ptr<Drawing::Image>& input, const KawaseParameter& inParam, const BlurParams& blur) const
292 {
293 auto originImageInfo = input->GetImageInfo();
294 auto scaledInfo = Drawing::ImageInfo(std::ceil(blur.width * blurScale_), std::ceil(blur.height * blurScale_),
295 originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
296 Drawing::Matrix blurMatrix;
297 blurMatrix.Translate(-inParam.src.GetLeft(), -inParam.src.GetTop());
298 float scaleW = static_cast<float>(scaledInfo.GetWidth()) / input->GetWidth();
299 float scaleH = static_cast<float>(scaledInfo.GetHeight()) / input->GetHeight();
300 blurMatrix.PostScale(scaleW, scaleH);
301 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
302
303 // Advanced Filter: check is AF usable only the first time
304 bool isUsingAF = IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && blurEffectAF_ != nullptr;
305 Drawing::RuntimeShaderBuilder blurBuilder(isUsingAF ? blurEffectAF_ : blurEffect_);
306 if (RSSystemProperties::GetBlurExtraFilterEnabled() && simpleFilter_) {
307 blurBuilder.SetChild("imageInput", ApplySimpleFilter(canvas, input, blurMatrix, scaledInfo, linear));
308 } else {
309 blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input, Drawing::TileMode::CLAMP,
310 Drawing::TileMode::CLAMP, linear, blurMatrix));
311 }
312
313 if (isUsingAF) {
314 SkV2 firstPassOffsets[BLUR_SAMPLE_COUNT];
315 OffsetInfo firstPassOffsetInfo = {blur.radiusByPass * blurScale_, blur.radiusByPass * blurScale_,
316 scaledInfo.GetWidth(), scaledInfo.GetHeight()};
317 getNormalizedOffset(firstPassOffsets, BLUR_SAMPLE_COUNT, firstPassOffsetInfo);
318 blurBuilder.SetUniform("in_blurOffset", firstPassOffsetInfo.offsetX, firstPassOffsetInfo.offsetY,
319 firstPassOffsetInfo.width, firstPassOffsetInfo.height);
320 } else {
321 blurBuilder.SetUniform("in_blurOffset", blur.radiusByPass * blurScale_, blur.radiusByPass * blurScale_);
322 blurBuilder.SetUniform("in_maxSizeXY", blur.width * blurScale_, blur.height * blurScale_);
323 }
324
325 std::shared_ptr<Drawing::Image> tmpBlur(blurBuilder.MakeImage(
326 canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
327 // And now we'll build our chain of scaled blur stages
328 for (auto i = 1; i < blur.numberOfPasses; i++) {
329 const float stepScale = static_cast<float>(i) * blurScale_;
330 blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::CLAMP,
331 Drawing::TileMode::CLAMP, linear, Drawing::Matrix()));
332
333 // Advanced Filter
334 if (isUsingAF) {
335 SkV2 offsets[BLUR_SAMPLE_COUNT];
336 OffsetInfo offsetInfo = {blur.radiusByPass * stepScale, blur.radiusByPass * stepScale,
337 scaledInfo.GetWidth(), scaledInfo.GetHeight()};
338 getNormalizedOffset(offsets, BLUR_SAMPLE_COUNT, offsetInfo);
339 blurBuilder.SetUniform("in_blurOffset", offsetInfo.offsetX, offsetInfo.offsetY, offsetInfo.width,
340 offsetInfo.height);
341 } else {
342 blurBuilder.SetUniform("in_blurOffset", blur.radiusByPass * stepScale, blur.radiusByPass * stepScale);
343 blurBuilder.SetUniform("in_maxSizeXY", blur.width * blurScale_, blur.height * blurScale_);
344 }
345 tmpBlur = blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
346 }
347 return tmpBlur;
348 }
349
ApplyBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const std::shared_ptr<Drawing::Image> & blurImage,const KawaseParameter & param) const350 bool KawaseBlurFilter::ApplyBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
351 const std::shared_ptr<Drawing::Image>& blurImage, const KawaseParameter& param) const
352 {
353 auto src = param.src;
354 auto dst = param.dst;
355 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
356 const auto blurMatrix = GetShaderTransform(&canvas, dst, dst.GetWidth() / blurImage->GetWidth(),
357 dst.GetHeight() / blurImage->GetHeight());
358 const auto blurShader = Drawing::ShaderEffect::CreateImageShader(*blurImage, Drawing::TileMode::CLAMP,
359 Drawing::TileMode::CLAMP, linear, blurMatrix);
360 Drawing::Brush brush;
361 brush.SetAlphaF(param.alpha);
362 if (param.colorFilter) {
363 Drawing::Filter filter;
364 filter.SetColorFilter(param.colorFilter);
365 brush.SetFilter(filter);
366 }
367 static auto addRandomColor = RSSystemProperties::GetRandomColorEnabled();
368 if (addRandomColor) {
369 Drawing::Matrix inputMatrix;
370 inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
371 inputMatrix.PostScale(dst.GetWidth() / image->GetWidth(), dst.GetHeight() / image->GetHeight());
372 Drawing::Matrix matrix;
373 matrix.Translate(dst.GetLeft(), dst.GetTop());
374 inputMatrix.PostConcat(matrix);
375 Drawing::RuntimeShaderBuilder mixBuilder(mixEffect_);
376 mixBuilder.SetChild("blurredInput", blurShader);
377 mixBuilder.SetChild("originalInput", Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
378 Drawing::TileMode::CLAMP, linear, inputMatrix));
379 float mixFactor = (abs(kMaxCrossFadeRadius) <= 1e-6) ? 1.f : (blurRadius_ / kMaxCrossFadeRadius);
380 mixBuilder.SetUniform("mixFactor", std::min(1.0f, mixFactor));
381 static auto factor = RSSystemProperties::GetKawaseRandomColorFactor();
382 mixBuilder.SetUniform("inColorFactor", factor);
383 ROSEN_LOGD("KawaseBlurFilter::kawase random color factor : %{public}f", factor);
384 brush.SetShaderEffect(mixBuilder.MakeShader(nullptr, image->IsOpaque()));
385 } else {
386 brush.SetShaderEffect(blurShader);
387 }
388 canvas.AttachBrush(brush);
389 canvas.DrawRect(dst);
390 canvas.DetachBrush();
391 return true;
392 }
393
ComputeRadiusAndScale(int radius)394 void KawaseBlurFilter::ComputeRadiusAndScale(int radius)
395 {
396 blurRadius_ = radius * 4; // 4 : scale between gauss radius and kawase
397 AdjustRadiusAndScale();
398 }
399
AdjustRadiusAndScale()400 void KawaseBlurFilter::AdjustRadiusAndScale()
401 {
402 static constexpr int radiusStep1 = 50; // 50 : radius step1
403 static constexpr int radiusStep2 = 150; // 150 : radius step2
404 static constexpr int radiusStep3 = 400; // 400 : radius step3
405 static constexpr float scaleFactor1 = 0.25f; // 0.25 : downSample scale for step1
406 static constexpr float scaleFactor2 = 0.125f; // 0.125 : downSample scale for step2
407 static constexpr float scaleFactor3 = 0.0625f; // 0.0625 : downSample scale for step3
408 auto radius = static_cast<int>(blurRadius_);
409 if (radius > radiusStep3) {
410 blurScale_ = scaleFactor3;
411 } else if (radius > radiusStep2) {
412 blurScale_ = scaleFactor2;
413 } else if (radius > radiusStep1) {
414 blurScale_ = scaleFactor1;
415 } else {
416 blurScale_ = baseBlurScale;
417 }
418 }
419
GetDescription() const420 std::string KawaseBlurFilter::GetDescription() const
421 {
422 return "blur radius is " + std::to_string(blurRadius_);
423 }
424 } // namespace Rosen
425 } // namespace OHOS