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 "render/rs_motion_blur_filter.h"
17 
18 #include "common/rs_optional_trace.h"
19 #include "draw/surface.h"
20 #include "platform/common/rs_log.h"
21 #include "platform/common/rs_system_properties.h"
22 #include "src/core/SkOpts.h"
23 
24 namespace OHOS {
25 namespace Rosen {
26 namespace {
27 constexpr static float FLOAT_SCALE_THRESHOLD = 1.1f; // 1.1f scale threshold
28 constexpr static float FLOAT_IMAGE_SCALE = 0.5f;    // 0.5f image scale
29 } // namespace
30 
31 std::shared_ptr<Drawing::RuntimeEffect> RSMotionBlurFilter::motionBlurShaderEffect_ = nullptr;
32 
RSMotionBlurFilter(const std::shared_ptr<MotionBlurParam> & para)33 RSMotionBlurFilter::RSMotionBlurFilter(const std::shared_ptr<MotionBlurParam>& para)
34     : RSDrawingFilterOriginal(nullptr), motionBlurPara_(para)
35 {
36     type_ = FilterType::MOTION_BLUR;
37     hash_ = SkOpts::hash(&type_, sizeof(type_), 0);
38     hash_ = SkOpts::hash(&motionBlurPara_, sizeof(motionBlurPara_), hash_);
39 }
40 
41 RSMotionBlurFilter::~RSMotionBlurFilter() = default;
42 
DrawImageRect(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const43 void RSMotionBlurFilter::DrawImageRect(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
44     const Drawing::Rect& src, const Drawing::Rect& dst) const
45 {
46     RS_OPTIONAL_TRACE_NAME("RSMotionBlurFilter::MotionBlur");
47 
48     if (!image || image->GetWidth() == 0 || image->GetHeight() == 0) {
49         lastRect_ = Drawing::Rect(0.f, 0.f, 0.f, 0.f);
50         ROSEN_LOGE("RSMotionBlurFilter::image error");
51         return;
52     }
53 
54     if (!RSSystemProperties::GetMotionBlurEnabled() || motionBlurPara_ == nullptr || motionBlurPara_->radius <= 0) {
55         lastRect_ = Drawing::Rect(0.f, 0.f, 0.f, 0.f);
56         OutputOriginalImage(canvas, image, src, dst);
57         return;
58     }
59 
60     if (disableMotionBlur_) {
61         OutputOriginalImage(canvas, image, src, dst);
62         return;
63     }
64 
65     Drawing::Matrix mat = canvas.GetTotalMatrix();
66     Drawing::Rect rect = Drawing::Rect(0.f, 0.f, image->GetWidth(), image->GetHeight());
67     mat.MapRect(rect, rect);
68     curRect_ = Drawing::Rect(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
69     if (!RectValid(lastRect_, curRect_)) {
70         lastRect_ = Drawing::Rect(curRect_.GetLeft(), curRect_.GetTop(), curRect_.GetRight(), curRect_.GetBottom());
71         OutputOriginalImage(canvas, image, src, dst);
72         return;
73     }
74 
75     DrawMotionBlur(canvas, image, src, dst);
76 }
77 
DrawMotionBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const78 void RSMotionBlurFilter::DrawMotionBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
79     const Drawing::Rect& src, const Drawing::Rect& dst) const
80 {
81     if (motionBlurPara_ == nullptr) {
82         lastRect_ = Drawing::Rect(0.f, 0.f, 0.f, 0.f);
83         OutputOriginalImage(canvas, image, src, dst);
84         return;
85     }
86 
87     RS_OPTIONAL_TRACE_NAME("DrawMotionBlur");
88     Vector2f rectOffset;
89     Vector2f scaleSize;
90     Vector2f scaleAnchorCoord;
91     CaculateRect(rectOffset, scaleSize, scaleAnchorCoord);
92 
93     Drawing::Matrix inputMatrix;
94     inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
95     inputMatrix.PostScale(FLOAT_IMAGE_SCALE, FLOAT_IMAGE_SCALE);
96     Drawing::Matrix matrix;
97     matrix.Translate(dst.GetLeft(), dst.GetTop());
98     inputMatrix.PostConcat(matrix);
99     auto imageShader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::DECAL,
100         Drawing::TileMode::DECAL, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), inputMatrix);
101     auto builder = MakeMotionBlurShader(imageShader, scaleAnchorCoord, scaleSize, rectOffset, motionBlurPara_->radius);
102 
103     auto originImageInfo = image->GetImageInfo();
104     auto scaledInfo = Drawing::ImageInfo(std::ceil(image->GetWidth() * FLOAT_IMAGE_SCALE),
105         std::ceil(image->GetHeight() * FLOAT_IMAGE_SCALE), originImageInfo.GetColorType(),
106         originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
107     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
108     std::shared_ptr<Drawing::Image> tmpBlur(builder->MakeImage(
109         canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
110 
111     float invBlurScale = 1.0f / FLOAT_IMAGE_SCALE;
112     Drawing::Matrix invBlurMatrix;
113     invBlurMatrix.PostScale(invBlurScale, invBlurScale);
114     auto shader = Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::CLAMP,
115         Drawing::TileMode::CLAMP, linear, invBlurMatrix);
116 
117     Drawing::Brush brush;
118     brush.SetShaderEffect(shader);
119     canvas.AttachBrush(brush);
120     canvas.DrawRect(dst);
121     canvas.DetachBrush();
122 }
123 
MakeMotionBlurShader(std::shared_ptr<Drawing::ShaderEffect> srcImageShader,Vector2f & scaleAnchor,Vector2f & scaleSize,Vector2f & rectOffset,float radius) const124 std::shared_ptr<Drawing::RuntimeShaderBuilder> RSMotionBlurFilter::MakeMotionBlurShader(
125     std::shared_ptr<Drawing::ShaderEffect> srcImageShader, Vector2f& scaleAnchor, Vector2f& scaleSize,
126     Vector2f& rectOffset, float radius) const
127 {
128     static const char* MotionBlurProg = R"(
129         uniform shader srcImageShader;
130         uniform float2 scaleAnchor;
131         uniform float2 scaleSize;
132         uniform float2 rectOffset;
133         uniform float radius;
134 
135         half4 main(float2 coord)
136         {
137             const float num = 6.0; // 6.0 sample times
138             float2 scaleSizeStep = (scaleSize - 1.0) / num * radius;
139             float2 rectOffsetStep = rectOffset / num * radius;
140             float2 samplingOffset = (coord - scaleAnchor) * scaleSizeStep + rectOffsetStep;
141 
142             float2 coord1 = coord + samplingOffset;
143             float2 coord2 = coord + samplingOffset * 2; // 2 times
144             float2 coord3 = coord + samplingOffset * 3; // 3 times
145             float2 coord4 = coord + samplingOffset * 4; // 4 times
146             float2 coord5 = coord + samplingOffset * 5; // 5 times
147             float2 coord6 = coord + samplingOffset * 6; // 6 times
148             half4 color = srcImageShader.eval(coord) * 0.11; // 0.11 alpha
149             color += srcImageShader.eval(coord1) * 0.12; // 0.12 alpha
150             color += srcImageShader.eval(coord2) * 0.13; // 0.13 alpha
151             color += srcImageShader.eval(coord3) * 0.14; // 0.14 alpha
152             color += srcImageShader.eval(coord4) * 0.15; // 0.15 alpha
153             color += srcImageShader.eval(coord5) * 0.16; // 0.16 alpha
154             color += srcImageShader.eval(coord6) * 0.19; // 0.19 alpha
155 
156             return color;
157         }
158     )";
159 
160     if (motionBlurShaderEffect_ == nullptr) {
161         motionBlurShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(MotionBlurProg);
162         if (motionBlurShaderEffect_ == nullptr) {
163             return nullptr;
164         }
165     }
166 
167     auto builder = std::make_shared<Drawing::RuntimeShaderBuilder>(motionBlurShaderEffect_);
168     builder->SetChild("srcImageShader", srcImageShader);
169     builder->SetUniform("scaleAnchor", scaleAnchor[0], scaleAnchor[1]);
170     builder->SetUniform("scaleSize", scaleSize[0], scaleSize[1]);
171     builder->SetUniform("rectOffset", rectOffset[0], rectOffset[1]);
172     builder->SetUniform("radius", radius);
173     return builder;
174 }
175 
CaculateRect(Vector2f & rectOffset,Vector2f & scaleSize,Vector2f & scaleAnchorCoord) const176 void RSMotionBlurFilter::CaculateRect(Vector2f& rectOffset, Vector2f& scaleSize, Vector2f& scaleAnchorCoord) const
177 {
178     rectOffset[0] = curRect_.GetLeft() - lastRect_.GetLeft() +
179         (curRect_.GetWidth() - lastRect_.GetWidth()) * motionBlurPara_->scaleAnchor[0];
180     rectOffset[1] = curRect_.GetTop() - lastRect_.GetTop() +
181         (curRect_.GetHeight() - lastRect_.GetHeight()) * motionBlurPara_->scaleAnchor[1];
182     scaleSize[0] = curRect_.GetWidth() / lastRect_.GetWidth();
183     scaleSize[1] = curRect_.GetHeight() / lastRect_.GetHeight();
184 
185     if (curRect_.GetWidth() < lastRect_.GetWidth() && curRect_.GetHeight() < lastRect_.GetHeight()) {
186         rectOffset[0] = -rectOffset[0];
187         rectOffset[1] = -rectOffset[1];
188         scaleSize[0] = 1.0 / scaleSize[0];
189         scaleSize[1] = 1.0 / scaleSize[1];
190     }
191 
192     if (scaleSize[0] > FLOAT_SCALE_THRESHOLD || scaleSize[1] > FLOAT_SCALE_THRESHOLD) {
193         rectOffset[0] = rectOffset[0] * FLOAT_SCALE_THRESHOLD / scaleSize[0];
194         rectOffset[1] = rectOffset[1] * FLOAT_SCALE_THRESHOLD / scaleSize[1];
195         scaleSize[0] = FLOAT_SCALE_THRESHOLD;
196         scaleSize[1] = FLOAT_SCALE_THRESHOLD;
197     }
198     rectOffset[0] = rectOffset[0] * FLOAT_IMAGE_SCALE;
199     rectOffset[1] = rectOffset[1] * FLOAT_IMAGE_SCALE;
200 
201     scaleAnchorCoord = {motionBlurPara_->scaleAnchor[0] * curRect_.GetWidth() * FLOAT_IMAGE_SCALE,
202         motionBlurPara_->scaleAnchor[1] * curRect_.GetHeight() * FLOAT_IMAGE_SCALE};
203 
204     lastRect_ = Drawing::Rect(curRect_.GetLeft(), curRect_.GetTop(), curRect_.GetRight(), curRect_.GetBottom());
205 }
206 
RectValid(const Drawing::Rect & rect1,const Drawing::Rect & rect2) const207 bool RSMotionBlurFilter::RectValid(const Drawing::Rect& rect1, const Drawing::Rect& rect2) const
208 {
209     if (rect1.GetWidth() < 1 || rect1.GetHeight() < 1 || rect2.GetWidth() < 1 || rect2.GetHeight() < 1) {
210         return false;
211     }
212 
213     if (rect1 == rect2) {
214         return false;
215     }
216 
217     return true;
218 }
219 
OutputOriginalImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const220 void RSMotionBlurFilter::OutputOriginalImage(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
221     const Drawing::Rect& src, const Drawing::Rect& dst) const
222 {
223     if (!image || image->GetWidth() == 0 || image->GetHeight() == 0) {
224         ROSEN_LOGE("RSMotionBlurFilter::OutputOriginalImage image error");
225         return;
226     }
227     Drawing::Matrix inputMatrix;
228     inputMatrix.Translate(-src.GetLeft(), -src.GetTop());
229     inputMatrix.PostScale(dst.GetWidth() / image->GetWidth(), dst.GetHeight() / image->GetHeight());
230 
231     Drawing::Matrix matrix;
232     matrix.Translate(dst.GetLeft(), dst.GetTop());
233     inputMatrix.PostConcat(matrix);
234     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
235     const auto inputShader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
236         Drawing::TileMode::CLAMP, linear, inputMatrix);
237 
238     Drawing::Brush brush;
239     brush.SetShaderEffect(inputShader);
240     canvas.AttachBrush(brush);
241     canvas.DrawRect(dst);
242     canvas.DetachBrush();
243 }
244 
GetDescription()245 std::string RSMotionBlurFilter::GetDescription()
246 {
247     return "RSMotionBlurFilter";
248 }
249 
Dump(std::string & out) const250 void MotionBlurParam::Dump(std::string& out) const
251 {
252     out += "[radius:" + std::to_string(radius) + " scaleAnchor[x:";
253     out += std::to_string(scaleAnchor.x_) + " y:" + std::to_string(scaleAnchor.y_) + "]]";
254 }
255 } // namespace Rosen
256 } // namespace OHOS
257