1 /*
2 * Copyright (c) 2024 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 "ge_kawase_blur_shader_filter.h"
17
18 #include "ge_log.h"
19 #include "ge_system_properties.h"
20 #include "src/core/SkOpts.h"
21
22 #include "effect/color_matrix.h"
23
24 namespace OHOS {
25 namespace Rosen {
26 // Advanced Filter
27 #define PROPERTY_HIGPU_VERSION "const.gpu.vendor"
28 #define PROPERTY_DEBUG_SUPPORT_AF "persist.sys.graphic.supports_af"
29 #define PROPERTY_BLUR_EXTRA_FILTER "persist.sys.graphic.blurExtraFilter"
30 #define PROPERTY_KAWASE_ORIGINAL_IMAGE "persist.sys.graphic.kawaseOriginalEnable"
31
32 namespace {
33 constexpr uint32_t BLUR_SAMPLE_COUNT = 5;
34 constexpr float BASE_BLUR_SCALE = 0.5f; // base downSample radio
35 constexpr int32_t MAX_PASSES_LARGE_RADIUS = 7; // Maximum number of render passes
36 constexpr float DILATED_CONVOLUTION_LARGE_RADIUS = 4.6f;
37 // To avoid downscaling artifacts, interpolate the blurred fbo with the full composited image, up to this radius
38 constexpr float MAX_CROSS_FADE_RADIUS = 10.0f;
39 static std::shared_ptr<Drawing::RuntimeEffect> g_blurEffect;
40 static std::shared_ptr<Drawing::RuntimeEffect> g_mixEffect;
41 static std::shared_ptr<Drawing::RuntimeEffect> g_blurEffectAf;
42 static std::shared_ptr<Drawing::RuntimeEffect> g_simpleFilter;
43
44 } // namespace
45
46 // Advanced Filter: we can get normalized uv offset from width and height
47 struct OffsetInfo {
48 float offsetX;
49 float offsetY;
50 int width;
51 int height;
52 };
53
54 // Advanced Filter
IsAdvancedFilterUsable()55 static bool IsAdvancedFilterUsable()
56 {
57 std::string gpuVersion = GESystemProperties::GetEventProperty(PROPERTY_HIGPU_VERSION);
58 // The AF Feature is only enabled on higpu v200 platform
59 if (gpuVersion.compare("higpu.v200") != 0) {
60 return false;
61 }
62 // If persist.sys.graphic.supports_af=0
63 // we will not use it
64 return GESystemProperties::GetBoolSystemProperty(PROPERTY_DEBUG_SUPPORT_AF, false);
65 }
66
GetBlurExtraFilterEnabled()67 static bool GetBlurExtraFilterEnabled()
68 {
69 static bool blurExtraFilterEnabled =
70 (std::atoi(GESystemProperties::GetEventProperty(PROPERTY_BLUR_EXTRA_FILTER).c_str()) != 0);
71 return blurExtraFilterEnabled;
72 }
73
GetKawaseOriginalEnabled()74 static bool GetKawaseOriginalEnabled()
75 {
76 #ifdef GE_OHOS
77 static bool kawaseOriginalEnabled =
78 (std::atoi(GESystemProperties::GetEventProperty(PROPERTY_KAWASE_ORIGINAL_IMAGE).c_str()) != 0);
79 return kawaseOriginalEnabled;
80 #else
81 return false;
82 #endif
83 }
84
getNormalizedOffset(SkV2 * offsets,const uint32_t offsetCount,const OffsetInfo & offsetInfo)85 static void getNormalizedOffset(SkV2* offsets, const uint32_t offsetCount, const OffsetInfo& offsetInfo)
86 {
87 if (offsets == nullptr || offsetCount != BLUR_SAMPLE_COUNT) {
88 LOGE("%s: Invalid offsets.", __func__);
89 return;
90 }
91 if (std::fabs(offsetInfo.width) < 1e-6 || std::fabs(offsetInfo.height) < 1e-6) {
92 LOGE("%s: Invalid width or height.", __func__);
93 return;
94 }
95 const SkV2 normalizedOffsets[BLUR_SAMPLE_COUNT] = { SkV2 { 0.0f, 0.0f },
96 SkV2 { offsetInfo.offsetX / offsetInfo.width, offsetInfo.offsetY / offsetInfo.height },
97 SkV2 { -offsetInfo.offsetX / offsetInfo.width, offsetInfo.offsetY / offsetInfo.height },
98 SkV2 { offsetInfo.offsetX / offsetInfo.width, -offsetInfo.offsetY / offsetInfo.height },
99 SkV2 { -offsetInfo.offsetX / offsetInfo.width, -offsetInfo.offsetY / offsetInfo.height } };
100 for (uint32_t i = 0; i < BLUR_SAMPLE_COUNT; ++i) {
101 offsets[i] = normalizedOffsets[i];
102 }
103 }
104
105 static const bool IS_ADVANCED_FILTER_USABLE_CHECK_ONCE = IsAdvancedFilterUsable();
106
GEKawaseBlurShaderFilter(const Drawing::GEKawaseBlurShaderFilterParams & params)107 GEKawaseBlurShaderFilter::GEKawaseBlurShaderFilter(const Drawing::GEKawaseBlurShaderFilterParams& params)
108 : radius_(params.radius)
109 {
110 if (!InitBlurEffect()) {
111 LOGE("GEKawaseBlurShaderFilter::GEKawaseBlurShaderFilter failed when initializing BlurEffect.");
112 return;
113 }
114 // Advanced Filter
115 if (IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && !InitBlurEffectForAdvancedFilter()) {
116 LOGE("GEKawaseBlurShaderFilter::GEKawaseBlurShaderFilter failed when initializing BlurEffectAF.");
117 return;
118 }
119
120 if (!InitMixEffect()) {
121 LOGE("GEKawaseBlurShaderFilter::GEKawaseBlurShaderFilter failed when initializing MixEffect.");
122 return;
123 }
124
125 if (radius_ < 1) {
126 LOGI("GEKawaseBlurShaderFilter radius(%{public}d) should be [1, 8k], ignore blur.", radius_);
127 radius_ = 0;
128 }
129
130 if (radius_ > 8000) { // 8000 experienced value
131 LOGI("GEKawaseBlurShaderFilter radius(%{public}d) should be [1, 8k], change to 8k.", radius_);
132 radius_ = 8000; // 8000 experienced value
133 }
134
135 if (GetBlurExtraFilterEnabled()) {
136 if (!InitSimpleFilter()) {
137 LOGE("GEKawaseBlurShaderFilter::GEKawaseBlurShaderFilter failed to construct SimpleFilter");
138 return;
139 }
140 }
141 }
142
GetRadius() const143 int GEKawaseBlurShaderFilter::GetRadius() const
144 {
145 return radius_;
146 }
147
ApplySimpleFilter(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const std::shared_ptr<Drawing::ShaderEffect> & prevShader,const Drawing::ImageInfo & scaledInfo,const Drawing::SamplingOptions & linear) const148 std::shared_ptr<Drawing::ShaderEffect> GEKawaseBlurShaderFilter::ApplySimpleFilter(Drawing::Canvas& canvas,
149 const std::shared_ptr<Drawing::Image>& input, const std::shared_ptr<Drawing::ShaderEffect>& prevShader,
150 const Drawing::ImageInfo& scaledInfo, const Drawing::SamplingOptions& linear) const
151 {
152 Drawing::RuntimeShaderBuilder simpleBlurBuilder(g_simpleFilter);
153 simpleBlurBuilder.SetChild("imageInput", prevShader);
154 std::shared_ptr<Drawing::Image> tmpSimpleBlur(simpleBlurBuilder.MakeImage(
155 canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
156 return Drawing::ShaderEffect::CreateImageShader(*tmpSimpleBlur, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
157 linear, Drawing::Matrix());
158 }
159
ProcessImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> image,const Drawing::Rect & src,const Drawing::Rect & dst)160 std::shared_ptr<Drawing::Image> GEKawaseBlurShaderFilter::ProcessImage(Drawing::Canvas& canvas,
161 const std::shared_ptr<Drawing::Image> image, const Drawing::Rect& src, const Drawing::Rect& dst)
162 {
163 if (!IsInputValid(canvas, image, src, dst)) {
164 return image;
165 }
166
167 auto input = image;
168 if (!input) {
169 return image;
170 }
171 CheckInputImage(canvas, image, input, src);
172 ComputeRadiusAndScale(radius_);
173
174 float tmpRadius = static_cast<float>(blurRadius_ / DILATED_CONVOLUTION_LARGE_RADIUS);
175 int numberOfPasses =
176 std::min(MAX_PASSES_LARGE_RADIUS, std::max(static_cast<int>(ceil(tmpRadius)), 1)); // 1 : min pass num
177 if (numberOfPasses < 1) { // 1 : min pass num
178 numberOfPasses = 1; // 1 : min pass num
179 }
180 float radiusByPasses = tmpRadius / numberOfPasses;
181
182 auto width = std::max(static_cast<int>(std::ceil(dst.GetWidth())), input->GetWidth());
183 auto height = std::max(static_cast<int>(std::ceil(dst.GetHeight())), input->GetHeight());
184 auto originImageInfo = input->GetImageInfo();
185 auto scaledInfo = Drawing::ImageInfo(std::ceil(width * blurScale_), std::ceil(height * blurScale_),
186 originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
187 Drawing::Matrix blurMatrix = BuildMatrix(src, scaledInfo, input);
188 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
189
190 // Advanced Filter: check is AF usable only the first time
191 bool isUsingAF = IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && g_blurEffectAf != nullptr;
192 auto tmpShader = Drawing::ShaderEffect::CreateImageShader(
193 *input, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrix);
194 Drawing::RuntimeShaderBuilder blurBuilder(isUsingAF ? g_blurEffectAf : g_blurEffect);
195 if (GetBlurExtraFilterEnabled() && g_simpleFilter) {
196 tmpShader = ApplySimpleFilter(canvas, input, tmpShader, scaledInfo, linear);
197 }
198 blurBuilder.SetChild("imageInput", tmpShader);
199
200 auto offsetXY = radiusByPasses * blurScale_;
201 SetBlurBuilderParam(blurBuilder, offsetXY, scaledInfo, width, height);
202
203 auto tmpBlur(blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
204
205 if (!tmpBlur) {
206 return image;
207 }
208
209 // And now we'll build our chain of scaled blur stages
210 for (auto i = 1; i < numberOfPasses; i++) {
211 auto blurShader = Drawing::ShaderEffect::CreateImageShader(
212 *tmpBlur, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, Drawing::Matrix());
213 const float stepScale = static_cast<float>(i) * blurScale_;
214 blurBuilder.SetChild("imageInput", blurShader);
215
216 // Advanced Filter
217 auto offsetXYFilter = radiusByPasses * stepScale;
218 SetBlurBuilderParam(blurBuilder, offsetXYFilter, scaledInfo, width, height);
219 tmpBlur = blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
220 }
221
222 auto output = ScaleAndAddRandomColor(canvas, input, tmpBlur, src, dst, width, height);
223 return output;
224 }
225
IsInputValid(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst)226 bool GEKawaseBlurShaderFilter::IsInputValid(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
227 const Drawing::Rect& src, const Drawing::Rect& dst)
228 {
229 if (!g_blurEffect || !g_mixEffect || !image) {
230 LOGE("GEKawaseBlurShaderFilter::shader error");
231 return false;
232 }
233 if (radius_ <= 0) {
234 LOGD("GEKawaseBlurShaderFilter::input invalid radius : %{public}d", radius_);
235 OutputOriginalImage(canvas, image, src, dst);
236 return false;
237 }
238 if (GetKawaseOriginalEnabled()) {
239 OutputOriginalImage(canvas, image, src, dst);
240 return false;
241 }
242 return true;
243 }
244
SetBlurBuilderParam(Drawing::RuntimeShaderBuilder & blurBuilder,const float offsetXY,const Drawing::ImageInfo & scaledInfo,const int width,const int height)245 void GEKawaseBlurShaderFilter::SetBlurBuilderParam(Drawing::RuntimeShaderBuilder& blurBuilder, const float offsetXY,
246 const Drawing::ImageInfo& scaledInfo, const int width, const int height)
247 {
248 // Advanced Filter: check is AF usable only the first time
249 bool isUsingAF = IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && g_blurEffectAf != nullptr;
250 if (isUsingAF) {
251 SkV2 offsets[BLUR_SAMPLE_COUNT];
252 OffsetInfo offsetInfo = { offsetXY, offsetXY, scaledInfo.GetWidth(), scaledInfo.GetHeight() };
253 getNormalizedOffset(offsets, BLUR_SAMPLE_COUNT, offsetInfo);
254 blurBuilder.SetUniform(
255 "in_blurOffset", offsetInfo.offsetX, offsetInfo.offsetY, offsetInfo.width, offsetInfo.height);
256 } else {
257 blurBuilder.SetUniform("in_blurOffset", offsetXY, offsetXY);
258 blurBuilder.SetUniform("in_maxSizeXY", width * blurScale_, height * blurScale_);
259 }
260 }
261
BuildMatrix(const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const std::shared_ptr<Drawing::Image> & input)262 const OHOS::Rosen::Drawing::Matrix GEKawaseBlurShaderFilter::BuildMatrix(
263 const Drawing::Rect& src, const Drawing::ImageInfo& scaledInfo, const std::shared_ptr<Drawing::Image>& input)
264 {
265 Drawing::Matrix blurMatrix;
266 blurMatrix.Translate(-src.GetLeft(), -src.GetTop());
267 int scaleWidth = scaledInfo.GetWidth();
268 int width = input->GetWidth();
269 float scaleW = static_cast<float>(scaleWidth) / (width > 0 ? width : 1);
270
271 int scaleHeight = scaledInfo.GetHeight();
272 int height = input->GetHeight();
273 float scaleH = static_cast<float>(scaleHeight) / (height > 0 ? height : 1);
274 blurMatrix.PostScale(scaleW, scaleH);
275 return blurMatrix;
276 }
277
InitBlurEffect()278 bool GEKawaseBlurShaderFilter::InitBlurEffect()
279 {
280 if (g_blurEffect != nullptr) {
281 return true;
282 }
283
284 static std::string blurString(R"(
285 uniform shader imageInput;
286 uniform float2 in_blurOffset;
287 uniform float2 in_maxSizeXY;
288
289 half4 main(float2 xy) {
290 half4 c = imageInput.eval(xy);
291 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
292 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
293 c += imageInput.eval(float2(clamp(in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
294 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
295 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
296 clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
297 c += imageInput.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
298 clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
299 return half4(c.rgba * 0.2);
300 }
301 )");
302 g_blurEffect = Drawing::RuntimeEffect::CreateForShader(blurString);
303 if (g_blurEffect == nullptr) {
304 LOGE("GEKawaseBlurShaderFilter::RuntimeShader blurEffect create failed");
305 return false;
306 }
307
308 return true;
309 }
310
InitMixEffect()311 bool GEKawaseBlurShaderFilter::InitMixEffect()
312 {
313 if (g_mixEffect != nullptr) {
314 return true;
315 }
316
317 static std::string mixString(R"(
318 uniform shader blurredInput;
319 uniform shader originalInput;
320 uniform float mixFactor;
321 uniform float inColorFactor;
322
323 highp float random(float2 xy) {
324 float t = dot(xy, float2(78.233, 12.9898));
325 return fract(sin(t) * 43758.5453);
326 }
327 half4 main(float2 xy) {
328 highp float noiseGranularity = inColorFactor / 255.0;
329 half4 finalColor = mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor);
330 float noise = mix(-noiseGranularity, noiseGranularity, random(xy));
331 finalColor.rgb += noise;
332 return finalColor;
333 }
334 )");
335 g_mixEffect = Drawing::RuntimeEffect::CreateForShader(mixString);
336 if (g_mixEffect == nullptr) {
337 LOGE("GEKawaseBlurShaderFilter::RuntimeShader mixEffect create failed");
338 return false;
339 }
340
341 return true;
342 }
343
InitSimpleFilter()344 bool GEKawaseBlurShaderFilter::InitSimpleFilter()
345 {
346 if (g_simpleFilter != nullptr) {
347 return true;
348 }
349
350 static std::string simpleShader(R"(
351 uniform shader imageInput;
352 half4 main(float2 xy) {
353 return imageInput.eval(xy);
354 }
355 )");
356 g_simpleFilter = Drawing::RuntimeEffect::CreateForShader(simpleShader);
357 if (g_simpleFilter == nullptr) {
358 LOGE("GEKawaseBlurShaderFilter::RuntimeShader failed to create simple filter");
359 return false;
360 }
361
362 return true;
363 }
364
365 // Advanced Filter
InitBlurEffectForAdvancedFilter()366 bool GEKawaseBlurShaderFilter::InitBlurEffectForAdvancedFilter()
367 {
368 if (g_blurEffectAf != nullptr) {
369 return true;
370 }
371
372 Drawing::RuntimeEffectOptions ops;
373 ops.useAF = true;
374 static std::string blurStringAF(R"(
375 uniform shader imageInput;
376 uniform float2 in_blurOffset[5];
377
378 half4 main(float2 xy) {
379 half4 c = half4(0, 0, 0, 0);
380 for (int i = 0; i < 5; ++i) {
381 c += imageInput.eval(float2(xy.x + in_blurOffset[i].x, xy.y + in_blurOffset[i].y));
382 }
383 return half4(c.rgba * 0.2);
384 }
385 )");
386 g_blurEffectAf = Drawing::RuntimeEffect::CreateForShader(blurStringAF, ops);
387 if (g_blurEffectAf == nullptr) {
388 LOGE("%s: RuntimeShader blurEffectAF create failed", __func__);
389 return false;
390 }
391
392 return true;
393 }
394
GetShaderTransform(const Drawing::Canvas * canvas,const Drawing::Rect & blurRect,float scaleW,float scaleH)395 Drawing::Matrix GEKawaseBlurShaderFilter::GetShaderTransform(
396 const Drawing::Canvas* canvas, const Drawing::Rect& blurRect, float scaleW, float scaleH)
397 {
398 Drawing::Matrix matrix;
399 matrix.SetScale(scaleW, scaleH);
400 Drawing::Matrix translateMatrix;
401 translateMatrix.Translate(blurRect.GetLeft(), blurRect.GetTop());
402 matrix.PostConcat(translateMatrix);
403 return matrix;
404 }
405
CheckInputImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,std::shared_ptr<Drawing::Image> & checkedImage,const Drawing::Rect & src) const406 void GEKawaseBlurShaderFilter::CheckInputImage(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
407 std::shared_ptr<Drawing::Image>& checkedImage, const Drawing::Rect& src) const
408 {
409 auto srcRect = Drawing::RectI(src.GetLeft(), src.GetTop(), src.GetRight(), src.GetBottom());
410 if (image->GetImageInfo().GetBound() != srcRect) {
411 auto resizedImage = std::make_shared<Drawing::Image>();
412 auto gpuCtx = canvas.GetGPUContext();
413 if (gpuCtx == nullptr || !(image->IsValid(gpuCtx.get()))) {
414 LOGE("GEKawaseBlurShaderFilter::CheckInputImage invalid image");
415 return;
416 }
417 if (resizedImage->BuildSubset(image, srcRect, *gpuCtx)) {
418 checkedImage = resizedImage;
419 LOGD("GEKawaseBlurShaderFilter::resize image success");
420 } else {
421 LOGD("GEKawaseBlurShaderFilter::resize image failed, use original image");
422 }
423 }
424 }
425
OutputOriginalImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const426 void GEKawaseBlurShaderFilter::OutputOriginalImage(Drawing::Canvas& canvas,
427 const std::shared_ptr<Drawing::Image>& image, const Drawing::Rect& src, const Drawing::Rect& dst) const
428 {
429 auto width = image->GetWidth();
430 auto height = image->GetHeight();
431 if (width == 0 || height == 0) {
432 return;
433 }
434
435 Drawing::Brush brush;
436 Drawing::Matrix inputMatrix;
437 float scaleW = dst.GetWidth() / width;
438 float scaleH = dst.GetHeight() / height;
439 inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
440 inputMatrix.PostScale(scaleW, scaleH);
441 Drawing::Matrix matrix;
442 matrix.Translate(dst.GetLeft(), dst.GetTop());
443 inputMatrix.PostConcat(matrix);
444 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
445 const auto inputShader = Drawing::ShaderEffect::CreateImageShader(
446 *image, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, inputMatrix);
447 brush.SetShaderEffect(inputShader);
448 canvas.AttachBrush(brush);
449 canvas.DrawRect(dst);
450 canvas.DetachBrush();
451 }
452
ScaleAndAddRandomColor(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const std::shared_ptr<Drawing::Image> & blurImage,const Drawing::Rect & src,const Drawing::Rect & dst,int & width,int & height) const453 std::shared_ptr<Drawing::Image> GEKawaseBlurShaderFilter::ScaleAndAddRandomColor(Drawing::Canvas& canvas,
454 const std::shared_ptr<Drawing::Image>& image, const std::shared_ptr<Drawing::Image>& blurImage,
455 const Drawing::Rect& src, const Drawing::Rect& dst, int& width, int& height) const
456 {
457 if (abs(blurScale_) < 1e-6 || blurImage->GetWidth() < 1e-6 || blurImage->GetHeight() < 1e-6 ||
458 image->GetWidth() < 1e-6 || image->GetHeight() < 1e-6) {
459 LOGE("GEKawaseBlurShaderFilter::blurScale is zero.");
460 return blurImage;
461 }
462
463 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
464
465 Drawing::RuntimeShaderBuilder mixBuilder(g_mixEffect);
466 const auto scaleMatrix = GetShaderTransform(
467 &canvas, dst, dst.GetWidth() / blurImage->GetWidth(), dst.GetHeight() / blurImage->GetHeight());
468 auto tmpShader = Drawing::ShaderEffect::CreateImageShader(
469 *blurImage, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, scaleMatrix);
470 mixBuilder.SetChild("blurredInput", tmpShader);
471 Drawing::Matrix inputMatrix;
472 inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
473 inputMatrix.PostScale(dst.GetWidth() / image->GetWidth(), dst.GetHeight() / image->GetHeight());
474 Drawing::Matrix matrix;
475 matrix.Translate(dst.GetLeft(), dst.GetTop());
476 inputMatrix.PostConcat(matrix);
477 auto mixShader = Drawing::ShaderEffect::CreateImageShader(
478 *image, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, inputMatrix);
479 mixBuilder.SetChild("originalInput", mixShader);
480 float mixFactor = (abs(MAX_CROSS_FADE_RADIUS) <= 1e-6) ? 1.f : (blurRadius_ / MAX_CROSS_FADE_RADIUS);
481 mixBuilder.SetUniform("mixFactor", std::min(1.0f, mixFactor));
482
483 static auto factor = 1.75; // 1.75 from experience
484 mixBuilder.SetUniform("inColorFactor", factor);
485 LOGD("GEKawaseBlurShaderFilter::kawase random color factor : %{public}f", factor);
486 auto scaledInfo = Drawing::ImageInfo(width, height, blurImage->GetImageInfo().GetColorType(),
487 blurImage->GetImageInfo().GetAlphaType(), blurImage->GetImageInfo().GetColorSpace());
488
489 auto output = mixBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
490 return output;
491 }
492
ComputeRadiusAndScale(int radius)493 void GEKawaseBlurShaderFilter::ComputeRadiusAndScale(int radius)
494 {
495 static constexpr int noiseFactor = 3; // 3 : smooth the radius change
496 blurRadius_ = radius * 4 / noiseFactor * noiseFactor; // 4 : scale between gauss radius and kawase
497 AdjustRadiusAndScale();
498 }
499
AdjustRadiusAndScale()500 void GEKawaseBlurShaderFilter::AdjustRadiusAndScale()
501 {
502 static constexpr int radiusStep1 = 50; // 50 : radius step1
503 static constexpr int radiusStep2 = 150; // 150 : radius step2
504 static constexpr int radiusStep3 = 400; // 400 : radius step3
505 static constexpr float scaleFactor1 = 0.25f; // 0.25 : downSample scale for step1
506 static constexpr float scaleFactor2 = 0.125f; // 0.125 : downSample scale for step2
507 static constexpr float scaleFactor3 = 0.0625f; // 0.0625 : downSample scale for step3
508 auto radius = static_cast<int>(blurRadius_);
509 if (radius > radiusStep3) {
510 blurScale_ = scaleFactor3;
511 } else if (radius > radiusStep2) {
512 blurScale_ = scaleFactor2;
513 } else if (radius > radiusStep1) {
514 blurScale_ = scaleFactor1;
515 } else {
516 blurScale_ = BASE_BLUR_SCALE;
517 }
518 }
519
GetDescription() const520 std::string GEKawaseBlurShaderFilter::GetDescription() const
521 {
522 return "blur radius is " + std::to_string(blurRadius_);
523 }
524
525 } // namespace Rosen
526 } // namespace OHOS
527