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