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