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_spherize_effect_filter.h"
16 
17 #include "common/rs_common_def.h"
18 #include "common/rs_optional_trace.h"
19 #include "platform/common/rs_log.h"
20 #include "src/core/SkOpts.h"
21 
22 namespace OHOS {
23 namespace Rosen {
RSSpherizeEffectFilter(float spherizeDegree)24 RSSpherizeEffectFilter::RSSpherizeEffectFilter(float spherizeDegree)
25     : RSDrawingFilterOriginal(nullptr), spherizeDegree_(spherizeDegree)
26 {
27     type_ = FilterType::SPHERIZE_EFFECT;
28 
29     hash_ = SkOpts::hash(&type_, sizeof(type_), 0);
30     hash_ = SkOpts::hash(&spherizeDegree_, sizeof(spherizeDegree_), hash_);
31 }
32 
33 RSSpherizeEffectFilter::~RSSpherizeEffectFilter() = default;
34 
GetDescription()35 std::string RSSpherizeEffectFilter::GetDescription()
36 {
37     return "RSSpherizeEffectFilter " + std::to_string(spherizeDegree_);
38 }
39 
IsValid() const40 bool RSSpherizeEffectFilter::IsValid() const
41 {
42     constexpr float epsilon = 0.001f;
43     return spherizeDegree_ > epsilon;
44 }
45 
GetSpherizeDegree() const46 float RSSpherizeEffectFilter::GetSpherizeDegree() const
47 {
48     return spherizeDegree_;
49 }
50 
GetBrush(const std::shared_ptr<Drawing::Image> & image) const51 Drawing::Brush RSSpherizeEffectFilter::GetBrush(const std::shared_ptr<Drawing::Image>& image) const
52 {
53     Drawing::Brush brush;
54     brush.SetBlendMode(Drawing::BlendMode::SRC_OVER);
55     Drawing::SamplingOptions samplingOptions;
56     Drawing::Matrix scaleMat;
57     brush.SetShaderEffect(Drawing::ShaderEffect::CreateImageShader(
58         *image, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, samplingOptions, scaleMat));
59     return brush;
60 }
61 
DrawImageRect(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const62 void RSSpherizeEffectFilter::DrawImageRect(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
63     const Drawing::Rect& src, const Drawing::Rect& dst) const
64 {
65     if (!spherizeDegree_ || !image || image->GetWidth() == 0 || image->GetHeight() == 0) {
66         ROSEN_LOGE("RSSpherizeEffectFilter::shader error");
67         return;
68     }
69     RS_OPTIONAL_TRACE_NAME_FMT("DrawSpherize:%f", spherizeDegree_);
70     int width = image->GetWidth();
71     int height = image->GetHeight();
72     bool isWidthGreater = width > height;
73 
74     auto brush = GetBrush(image);
75     canvas.AttachBrush(brush);
76 
77     // 4 coordinates of image texture
78     const Drawing::Point texCoords[4] = { { 0.0f, 0.0f }, { width, 0.0f }, { width, height }, { 0.0f, height } };
79     float offsetSquare = 0.f;
80     if (isWidthGreater) {
81         offsetSquare = (width - height) * spherizeDegree_ / 2.0; // 2.0 express half of the change distance
82         width = width - (width - height) * spherizeDegree_;
83     } else {
84         offsetSquare = (height - width) * spherizeDegree_ / 2.0; // 2.0 express half of the change distance
85         height = height - (height - width) * spherizeDegree_;
86     }
87 
88     float segmentWidthOne = width / 3.0; // Anchor point of 1 is located at one-third of the image width.
89     float segmentWidthTwo = width / 3.0 * 2.0; // Anchor point 2 is located at two-thirds of the image width.
90     float segmentHeightOne = height / 3.0; // Anchor point 1 is located at one-third of the height of the image.
91     float segmentHeightTwo = height / 3.0 * 2.0; // Anchor point 2 is located at a height of two-thirds of the image.
92     // The width moving distance of the four corner anchor points of the image.
93     float offsetSphereWidth = width / 6.0 * spherizeDegree_;
94     // The high moving distance of the four corner anchor points of the image.
95     float offsetSphereHeight = height / 6.0 * spherizeDegree_;
96 
97     const int PointNum = 12; // 12 anchor points
98     Drawing::Point ctrlPoints[PointNum] = {
99         // top edge control points
100         {0.0f, 0.0f}, {segmentWidthOne, 0.0f}, {segmentWidthTwo, 0.0f}, {width, 0.0f},
101         // right edge control points
102         {width, segmentHeightOne}, {width, segmentHeightTwo},
103         // bottom edge control points
104         {width, height}, {segmentWidthTwo, height}, {segmentWidthOne, height}, {0.0f, height},
105         // left edge control points
106         {0.0f, segmentHeightTwo}, {0.0f, segmentHeightOne}
107     };
108     ctrlPoints[0].Offset(offsetSphereWidth, offsetSphereHeight); // Point 0 express top left control point
109     ctrlPoints[3].Offset(-offsetSphereWidth, offsetSphereHeight); // Point 3 express top right control point
110     ctrlPoints[6].Offset(-offsetSphereWidth, -offsetSphereHeight); // Point 6 express bottom right control point
111     ctrlPoints[9].Offset(offsetSphereWidth, -offsetSphereHeight); // Point 9 express bottom left control point
112     if (isWidthGreater) {
113         for (int i = 0; i < PointNum; ++i) {
114             ctrlPoints[i].Offset(offsetSquare, 0);
115         }
116     } else {
117         for (int i = 0; i < PointNum; ++i) {
118             ctrlPoints[i].Offset(0, offsetSquare);
119         }
120     }
121     Drawing::Path path;
122     // The zeroth point express the starting point of drawing.
123     path.MoveTo(ctrlPoints[0].GetX(), ctrlPoints[0].GetY());
124     // The 1st, 2nd, and 3rd control points are connected to represent the upper edge.
125     path.CubicTo(ctrlPoints[1], ctrlPoints[2], ctrlPoints[3]);
126     // The 4th, 5th and 6th control points are connected to represent the right edge.
127     path.CubicTo(ctrlPoints[4], ctrlPoints[5], ctrlPoints[6]);
128     // The 7th, 8th, and 9th control points are connected to represent the bottom edge.
129     path.CubicTo(ctrlPoints[7], ctrlPoints[8], ctrlPoints[9]);
130     // The 0th, 10th, and 11th control points are connected to represent the left edge.
131     path.CubicTo(ctrlPoints[10], ctrlPoints[11], ctrlPoints[0]);
132     canvas.ClipPath(path, Drawing::ClipOp::INTERSECT, true);
133     canvas.DrawPatch(ctrlPoints, nullptr, texCoords, Drawing::BlendMode::SRC_OVER);
134     canvas.DetachBrush();
135 }
136 } // namespace Rosen
137 } // namespace OHOS