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