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 #include "render/rs_foreground_effect_filter.h"
16 
17 #include "common/rs_common_def.h"
18 #include "common/rs_optional_trace.h"
19 #include "pipeline/rs_paint_filter_canvas.h"
20 #include "platform/common/rs_log.h"
21 #include "src/core/SkOpts.h"
22 
23 namespace OHOS {
24 namespace Rosen {
RSForegroundEffectFilter(float blurRadius)25 RSForegroundEffectFilter::RSForegroundEffectFilter(float blurRadius)
26     : RSDrawingFilterOriginal(nullptr)
27 {
28     type_ = FilterType::FOREGROUND_EFFECT;
29     MakeForegroundEffect();
30     ComputeParamter(blurRadius);
31 
32     hash_ = SkOpts::hash(&type_, sizeof(type_), 0);
33     hash_ = SkOpts::hash(&blurRadius_, sizeof(blurRadius_), hash_);
34 }
35 
36 RSForegroundEffectFilter::~RSForegroundEffectFilter() = default;
37 
GetDescription()38 std::string RSForegroundEffectFilter::GetDescription()
39 {
40     return "ForegroundEffect radius: " + std::to_string(blurRadius_) +
41         ", scale: " + std::to_string(blurScale_) + ", passNum: " + std::to_string(numberOfPasses_) +
42         ", dirtyExtension: " + std::to_string(GetDirtyExtension());
43 }
44 
IsValid() const45 bool RSForegroundEffectFilter::IsValid() const
46 {
47     constexpr float epsilon = 0.999f; // if blur radius less than 1, do not need to draw
48     return blurRadius_ > epsilon;
49 }
50 
MakeForegroundEffect()51 std::shared_ptr<Drawing::RuntimeShaderBuilder> RSForegroundEffectFilter::MakeForegroundEffect()
52 {
53     static std::shared_ptr<Drawing::RuntimeEffect> blurEffect_ = nullptr;
54     std::string blurString(
55         R"(
56         uniform shader imageInput;
57         uniform float2 in_blurOffset;
58 
59         half4 main(float2 xy) {
60             half4 c = imageInput.eval(xy);
61             c += imageInput.eval(float2(in_blurOffset.x + xy.x,
62                                         in_blurOffset.y + xy.y));
63             c += imageInput.eval(float2(in_blurOffset.x + xy.x,
64                                         -in_blurOffset.y + xy.y));
65             c += imageInput.eval(float2(-in_blurOffset.x + xy.x,
66                                         in_blurOffset.y + xy.y));
67             c += imageInput.eval(float2(-in_blurOffset.x + xy.x,
68                                         -in_blurOffset.y + xy.y));
69             return half4(c.rgba * 0.2);
70         }
71     )");
72 
73     if (blurEffect_ == nullptr) {
74         blurEffect_ = Drawing::RuntimeEffect::CreateForShader(blurString);
75         if (blurEffect_ == nullptr) {
76             ROSEN_LOGE("RSForegroundEffect::RuntimeShader blurEffect create failed");
77             return nullptr;
78         }
79     }
80     std::shared_ptr<Drawing::RuntimeShaderBuilder> blurBuilder =
81         std::make_shared<Drawing::RuntimeShaderBuilder>(blurEffect_);
82     return blurBuilder;
83 }
84 
ComputeParamter(int radius)85 void RSForegroundEffectFilter::ComputeParamter(int radius)
86 {
87     static constexpr int noiseFactor = 3; // 3 : smooth the radius change
88     blurRadius_ = radius * 4 / noiseFactor * noiseFactor; // 4 : scale between gauss radius and kawase
89     blurRadius_ = std::clamp(blurRadius_, 0.f, BLUR_RADIUS_LIMIT);
90     AdjustRadiusAndScale();
91     ComputePassesAndUnit();
92 }
93 
AdjustRadiusAndScale()94 void RSForegroundEffectFilter::AdjustRadiusAndScale()
95 {
96     static constexpr int radiusStep1 = 50; // 50 : radius step1
97     static constexpr int radiusStep2 = 150; // 150 : radius step2
98     static constexpr int radiusStep3 = 400; // 400 : radius step3
99     static constexpr float scaleFactor1 = 0.25f; // 0.25 : downSample scale for step1
100     static constexpr float scaleFactor2 = 0.125f; // 0.125 : downSample scale for step2
101     static constexpr float scaleFactor3 = 0.0625f; // 0.0625 : downSample scale for step3
102 
103     auto radius = static_cast<int>(blurRadius_);
104     if (radius > radiusStep3) {
105         blurScale_ = scaleFactor3;
106     } else if (radius > radiusStep2) {
107         blurScale_ = scaleFactor2;
108     } else if (radius > radiusStep1) {
109         blurScale_ = scaleFactor1;
110     } else {
111         blurScale_ = BASE_BLUR_SCALE;
112     }
113 }
114 
ComputePassesAndUnit()115 void RSForegroundEffectFilter::ComputePassesAndUnit()
116 {
117     int maxPasses = MAX_PASSES_LARGE_RADIUS;
118     float tmpRadius = static_cast<float>(blurRadius_) / DILATED_CONVOLUTION_LARGE_RADIUS;
119     numberOfPasses_ = std::min(maxPasses, std::max(static_cast<int>(ceil(tmpRadius)), 1)); // 1 : min pass num
120     if (numberOfPasses_ == 0) {
121         numberOfPasses_ = 1;
122     }
123     radiusByPasses_ = tmpRadius / numberOfPasses_;
124     int unitPadding = 1; // add padding to avoid expanded image is not large enough
125     unit_ = std::ceil(radiusByPasses_ * blurScale_) + unitPadding;
126 }
127 
GetDirtyExtension() const128 float RSForegroundEffectFilter::GetDirtyExtension() const
129 {
130     if (ROSEN_EQ(blurScale_, 0.0f)) {
131         ROSEN_LOGD("RSForegroundEffectFilter::GetDirtyExtension blurRadius is 0.0");
132         return 0.0f;
133     }
134     return std::ceil(EXPAND_UNIT_NUM * unit_ * numberOfPasses_ * 1 / blurScale_);
135 }
136 
ApplyForegroundEffect(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const ForegroundEffectParam & param) const137 void RSForegroundEffectFilter::ApplyForegroundEffect(Drawing::Canvas& canvas,
138     const std::shared_ptr<Drawing::Image>& image, const ForegroundEffectParam& param) const
139 {
140     auto blurBuilder = MakeForegroundEffect();
141     if (!blurBuilder || !image || image->GetWidth() == 0 || image->GetHeight() == 0) {
142         ROSEN_LOGE("RSForegroundEffectFilter::shader error");
143         return;
144     }
145 
146     auto src = param.src;
147     auto dst = param.dst;
148 
149     RS_OPTIONAL_TRACE_NAME("ApplyForegroundEffect");
150     ROSEN_LOGD("ForegroundEffect::kawase radius : %{public}f, scale : %{public}f, pass num : %{public}d",
151         blurRadius_, blurScale_, numberOfPasses_);
152 
153     auto width = std::max(static_cast<int>(std::ceil(dst.GetWidth())), image->GetWidth());
154     auto height = std::max(static_cast<int>(std::ceil(dst.GetHeight())), image->GetHeight());
155     auto imgInfo = image->GetImageInfo();
156 
157     Drawing::Matrix blurMatrix;
158     float scaleW = static_cast<float>(std::ceil(width * blurScale_)) / image->GetWidth();
159     float scaleH = static_cast<float>(std::ceil(height * blurScale_)) / image->GetHeight();
160     blurMatrix.PostScale(scaleW, scaleH);
161     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
162 
163     blurBuilder->SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::DECAL,
164         Drawing::TileMode::DECAL, linear, blurMatrix));
165     blurBuilder->SetUniform("in_blurOffset", radiusByPasses_ * blurScale_, radiusByPasses_ * blurScale_);
166 
167     float extension = static_cast<float>(std::ceil(GetDirtyExtension() * blurScale_));
168     float halfExtension = 0.5 * extension;
169     auto scaledInfoGeo = Drawing::ImageInfo(std::ceil(width * blurScale_) + extension,
170         std::ceil(height * blurScale_) + extension, imgInfo.GetColorType(),
171         imgInfo.GetAlphaType(), imgInfo.GetColorSpace());
172     Drawing::Matrix blurMatrixGeo;
173     blurMatrixGeo.Translate(halfExtension, halfExtension);
174 
175     std::shared_ptr<Drawing::Image> tmpBlur(blurBuilder->MakeImage(
176         canvas.GetGPUContext().get(), &blurMatrixGeo, scaledInfoGeo, false));
177     // And now we'll build our chain of scaled blur stages
178     for (auto i = 1; i < numberOfPasses_; i++) {
179         const float stepScale = static_cast<float>(i) * blurScale_;
180         blurBuilder->SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur,
181             Drawing::TileMode::DECAL, Drawing::TileMode::DECAL, linear, Drawing::Matrix()));
182         blurBuilder->SetUniform("in_blurOffset", radiusByPasses_ * stepScale, radiusByPasses_ * stepScale);
183 
184         tmpBlur = blurBuilder->MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfoGeo, false);
185     }
186 
187     Drawing::Matrix blurMatrixInv;
188     blurMatrixInv.Translate(-halfExtension, -halfExtension);
189     if (ROSEN_EQ(scaleW, 0.0f) || ROSEN_EQ(scaleH, 0.0f)) {
190         ROSEN_LOGD("RSForegroundEffectFilter scaleW or scaleH is 0");
191         return;
192     }
193     blurMatrixInv.PostScale(1 / scaleW, 1 / scaleH);
194     const auto blurShader = Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::DECAL,
195         Drawing::TileMode::DECAL, linear, blurMatrixInv);
196     Drawing::Brush brush;
197 
198     brush.SetShaderEffect(blurShader);
199     canvas.DrawBackground(brush);
200 }
201 
DrawImageRect(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const202 void RSForegroundEffectFilter::DrawImageRect(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
203     const Drawing::Rect& src, const Drawing::Rect& dst) const
204 {
205     ForegroundEffectParam param = ForegroundEffectParam(src, dst);
206     ApplyForegroundEffect(canvas, image, param);
207 }
208 } // namespace Rosen
209 } // namespace OHOS