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_distortion_shader_filter.h"
16 
17 #include "common/rs_optional_trace.h"
18 #include "common/rs_common_def.h"
19 #include "platform/common/rs_log.h"
20 
21 namespace OHOS {
22 namespace Rosen {
23 // 0.24 means a mapping between the external input and the actual calculated value.
24 static const float DISTORTION_SCALE = 0.24f;
25 
RSDistortionFilter(float distortionK)26 RSDistortionFilter::RSDistortionFilter(float distortionK)
27     : RSDrawingFilterOriginal(nullptr), distortionK_(distortionK)
28 {
29     type_ = FilterType::DISTORT;
30 
31     hash_ = SkOpts::hash(&type_, sizeof(type_), 0);
32     hash_ = SkOpts::hash(&distortionK_, sizeof(distortionK_), hash_);
33     hash_ = SkOpts::hash(&scaleCoord_, sizeof(scaleCoord_), hash_);
34     hash_ = SkOpts::hash(&offsetX_, sizeof(offsetX_), hash_);
35     hash_ = SkOpts::hash(&offsetY_, sizeof(offsetY_), hash_);
36 }
37 
38 RSDistortionFilter::~RSDistortionFilter() = default;
39 
GetDescription()40 std::string RSDistortionFilter::GetDescription()
41 {
42     return "DistortionEffect k: " + std::to_string(distortionK_) +
43         ", scaleCoord: " + std::to_string(scaleCoord_) + ", offsetX: " + std::to_string(offsetX_) +
44         ", offsetY: " + std::to_string(offsetY_);
45 }
46 
IsValid() const47 bool RSDistortionFilter::IsValid() const
48 {
49     if (distortionK_ < -1 || distortionK_ > 1) {
50         return false;
51     }
52     if (ROSEN_EQ(scaleCoord_, 0.0f)) {
53         return false;
54     }
55     // -0.5f means the calculation coefficient of scale
56     if (ROSEN_LNE(distortionK_ * DISTORTION_SCALE, -0.5f / (scaleCoord_ * scaleCoord_))) {
57         return false;
58     }
59     return true;
60 }
61 
MakeDistortionShader()62 std::shared_ptr<Drawing::RuntimeShaderBuilder> RSDistortionFilter::MakeDistortionShader()
63 {
64     static std::shared_ptr<Drawing::RuntimeEffect> distortEffect = nullptr;
65     std::string distortString(
66         R"(
67         uniform shader imageInput;
68         uniform float2 iResolution;
69         uniform float distortionK;
70         uniform float scale; // coord: 0_1 -> -scale_scale
71         uniform float2 offset;
72 
73         half4 main(float2 xy) {
74             float2 uv = xy / iResolution.xy;
75             uv = 2.0 * scale * uv - float2(scale, scale) - offset;
76             float rr = uv.x * uv.x +  uv.y * uv.y;
77             float2 finalUV = uv / (1.0 + distortionK * rr);
78             finalUV = finalUV + float2(scale, scale) + offset;
79             finalUV = finalUV / (2 * scale);
80             half4 c = imageInput.eval(finalUV * iResolution.xy);
81             return half4(c.rgba);
82         }
83     )");
84 
85     if (distortEffect == nullptr) {
86         distortEffect = Drawing::RuntimeEffect::CreateForShader(distortString);
87         if (distortEffect == nullptr) {
88             ROSEN_LOGE("RSDistortionFilter::RuntimeShader distortEffect create failed");
89             return nullptr;
90         }
91     }
92     std::shared_ptr<Drawing::RuntimeShaderBuilder> distortBuilder =
93         std::make_shared<Drawing::RuntimeShaderBuilder>(distortEffect);
94     return distortBuilder;
95 }
96 
DrawDistortion(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const97 void RSDistortionFilter::DrawDistortion(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
98     const Drawing::Rect& src, const Drawing::Rect& dst) const
99 {
100     auto distortBuilder = MakeDistortionShader();
101     if (!distortBuilder || !IsValid()) {
102         ROSEN_LOGE("RSDistortionFilter::shader error");
103         return;
104     }
105     RS_OPTIONAL_TRACE_NAME("DrawDistortion");
106 
107     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
108     distortBuilder->SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::DECAL,
109         Drawing::TileMode::DECAL, linear, Drawing::Matrix()));
110     distortBuilder->SetUniform("iResolution", image->GetWidth(), image->GetHeight());
111     distortBuilder->SetUniform("distortionK", distortionK_ * DISTORTION_SCALE);
112     distortBuilder->SetUniform("scale", scaleCoord_);
113     distortBuilder->SetUniform("offset", offsetX_, offsetY_);
114 
115     std::shared_ptr<Drawing::ShaderEffect> distortShader = distortBuilder->MakeShader(nullptr, false);
116     Drawing::Brush brush;
117     brush.SetShaderEffect(distortShader);
118     if (distortionK_ < 0) {
119         canvas.AttachBrush(brush);
120         canvas.DrawRect(dst);
121         canvas.DetachBrush();
122         return;
123     }
124     canvas.DrawBackground(brush);
125 }
DrawImageRect(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const126 void RSDistortionFilter::DrawImageRect(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
127     const Drawing::Rect& src, const Drawing::Rect& dst) const
128 {
129     if (!image || image->GetWidth() <= 0 || image->GetHeight() <= 0) {
130         ROSEN_LOGE("RSDistortionFilter::image error");
131         return;
132     }
133     DrawDistortion(canvas, image, src, dst);
134 }
135 } // namespace Rosen
136 } // namespace OHOS