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