1 /*
2  * Copyright (c) 2021-2022 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 "core/components/common/properties/clip_path.h"
17 
18 namespace OHOS::Ace {
19 
SetLength(const std::vector<Dimension> & lengths)20 bool Inset::SetLength(const std::vector<Dimension>& lengths)
21 {
22     std::string::size_type size = lengths.size();
23     bool ret = true;
24     switch (size) {
25         case 1:
26             SetLength(lengths.at(0), lengths.at(0), lengths.at(0), lengths.at(0));
27             break;
28         case 2:
29             SetLength(lengths.at(0), lengths.at(1), lengths.at(0), lengths.at(1));
30             break;
31         case 3:
32             SetLength(lengths.at(0), lengths.at(1), lengths.at(2), lengths.at(1));
33             break;
34         case 4:
35             SetLength(lengths.at(0), lengths.at(1), lengths.at(2), lengths.at(3));
36             break;
37         default:
38             LOGE("invalid length value");
39             ret = false;
40             break;
41     }
42     return ret;
43 }
44 
SetLength(const Dimension & top,const Dimension & right,const Dimension & bottom,const Dimension & left)45 void Inset::SetLength(const Dimension& top, const Dimension& right, const Dimension& bottom, const Dimension& left)
46 {
47     SetTop(top);
48     SetRight(right);
49     SetBottom(bottom);
50     SetLeft(left);
51 }
52 
SetRadius(const std::vector<Dimension> & rounds,bool isX)53 void Inset::SetRadius(const std::vector<Dimension>& rounds, bool isX)
54 {
55     std::string::size_type size = rounds.size();
56     switch (size) {
57         case 1:
58             SetRadius(rounds.at(0), rounds.at(0), rounds.at(0), rounds.at(0), isX);
59             break;
60         case 2:
61             SetRadius(rounds.at(0), rounds.at(1), rounds.at(0), rounds.at(1), isX);
62             break;
63         case 3:
64             SetRadius(rounds.at(0), rounds.at(1), rounds.at(2), rounds.at(1), isX);
65             break;
66         case 4:
67             SetRadius(rounds.at(0), rounds.at(1), rounds.at(2), rounds.at(3), isX);
68             break;
69         default:
70             LOGE("invalid radius value");
71             break;
72     }
73 }
74 
SetRadius(const Dimension & top,const Dimension & right,const Dimension & bottom,const Dimension & left,bool isX)75 void Inset::SetRadius(const Dimension& top, const Dimension& right, const Dimension& bottom, const Dimension& left,
76     bool isX)
77 {
78     SetTopLeftRadius(top, isX);
79     SetTopRightRadius(right, isX);
80     SetBottomRightRadius(bottom, isX);
81     SetBottomLeftRadius(left, isX);
82 }
83 
GetGeometryBoxType(const std::string & value)84 GeometryBoxType ClipPath::GetGeometryBoxType(const std::string& value)
85 {
86     GeometryBoxType geometryBoxType = GeometryBoxType::NONE;
87     std::string::size_type boxPosition = 0;
88     std::string::size_type tmp = value.find("margin-box");
89     if (tmp != std::string::npos && tmp >= boxPosition) {
90         geometryBoxType = GeometryBoxType::MARGIN_BOX;
91         boxPosition = tmp;
92     }
93     tmp = value.find("border-box");
94     if (tmp != std::string::npos && tmp >= boxPosition) {
95         geometryBoxType = GeometryBoxType::BORDER_BOX;
96         boxPosition = tmp;
97     }
98     tmp = value.find("padding-box");
99     if (tmp != std::string::npos && tmp >= boxPosition) {
100         geometryBoxType = GeometryBoxType::PADDING_BOX;
101         boxPosition = tmp;
102     }
103     tmp = value.find("content-box");
104     if (tmp != std::string::npos && tmp >= boxPosition) {
105         geometryBoxType = GeometryBoxType::CONTENT_BOX;
106     }
107     return geometryBoxType;
108 }
109 
GetBasicShapeInfo(const std::string & value,BasicShapeType & basicShapeType,std::string & data)110 void ClipPath::GetBasicShapeInfo(const std::string& value, BasicShapeType& basicShapeType, std::string& data)
111 {
112     std::string::size_type first = 0;
113     std::string::size_type tmp = value.find("inset(");
114     if (tmp != std::string::npos && tmp >= first) {
115         first = tmp + std::strlen("inset(");
116         data = value.substr(first, (value.find_first_of(')', first) - first));
117         basicShapeType = BasicShapeType::INSET;
118     }
119     tmp = value.find("circle(");
120     if (tmp != std::string::npos && tmp >= first) {
121         first = tmp + std::strlen("circle(");
122         data = value.substr(first, (value.find_first_of(')', first) - first));
123         basicShapeType = BasicShapeType::CIRCLE;
124     }
125     tmp = value.find("ellipse(");
126     if (tmp != std::string::npos && tmp >= first) {
127         first = tmp + std::strlen("ellipse(");
128         data = value.substr(first, (value.find_first_of(')', first) - first));
129         basicShapeType = BasicShapeType::ELLIPSE;
130     }
131     tmp = value.find("polygon(");
132     if (tmp != std::string::npos && tmp >= first) {
133         first = tmp + std::strlen("polygon(");
134         data = value.substr(first, (value.find_first_of(')', first) - first));
135         basicShapeType = BasicShapeType::POLYGON;
136     }
137     tmp = value.find("path('");
138     if (tmp != std::string::npos && tmp >= first) {
139         first = tmp + std::strlen("path('");
140         data = value.substr(first, (value.find("')", first) - first));
141         basicShapeType = BasicShapeType::PATH;
142     }
143     tmp = value.find("path(\"");
144     if (tmp != std::string::npos && tmp >= first) {
145         first = tmp + std::strlen("path(\"");
146         data = value.substr(first, (value.find("\")", first) - first));
147         basicShapeType = BasicShapeType::PATH;
148     }
149 }
150 
CreateCircle(const std::string & data)151 RefPtr<Circle> ClipPath::CreateCircle(const std::string& data)
152 {
153     std::string::size_type atIndex = data.find("at");
154     if (atIndex == std::string::npos) {
155         Dimension radius = StringUtils::StringToDimension(StringUtils::TrimStr(data));
156         if (!radius.IsValid()) {
157             return nullptr;
158         }
159         auto circle = AceType::MakeRefPtr<Circle>();
160         circle->SetRadius(radius);
161         return circle;
162     }
163     Dimension radius = StringUtils::StringToDimension(StringUtils::TrimStr(data.substr(0, atIndex)));
164     if (!radius.IsValid()) {
165         return nullptr;
166     }
167     auto circle = AceType::MakeRefPtr<Circle>();
168     circle->SetRadius(radius);
169     std::vector<Dimension> axis;
170     StringUtils::SplitStr(StringUtils::TrimStr(data.substr(atIndex + 2)), " ", axis);
171     if (axis.size() == 1) {
172         circle->SetAxisX(axis.at(0));
173     }
174     if (axis.size() >= 2) {
175         circle->SetAxisX(axis.at(0));
176         circle->SetAxisY(axis.at(1));
177     }
178     return circle;
179 }
180 
CreateEllipse(const std::string & data)181 RefPtr<Ellipse> ClipPath::CreateEllipse(const std::string& data)
182 {
183     std::string::size_type atIndex = data.find("at");
184     if (atIndex == std::string::npos) {
185         return CreateEllipseSize(data);
186     }
187     auto ellipse = CreateEllipseSize(StringUtils::TrimStr(data.substr(0, atIndex)));
188     if (!ellipse) {
189         return nullptr;
190     }
191     std::vector<Dimension> axis;
192     StringUtils::SplitStr(StringUtils::TrimStr(data.substr(atIndex + 2)), " ", axis);
193     if (axis.size() == 1) {
194         ellipse->SetAxisX(axis.at(0));
195     }
196     if (axis.size() >= 2) {
197         ellipse->SetAxisX(axis.at(0));
198         ellipse->SetAxisY(axis.at(1));
199     }
200     return ellipse;
201 }
202 
CreateEllipseSize(const std::string & data)203 RefPtr<Ellipse> ClipPath::CreateEllipseSize(const std::string& data)
204 {
205     std::vector<Dimension> lengths;
206     StringUtils::SplitStr(data, " ", lengths);
207     if (lengths.size() != 2) {
208         return nullptr;
209     }
210     if (!lengths.at(0).IsValid() || !lengths.at(1).IsValid()) {
211         return nullptr;
212     }
213     auto ellipse = AceType::MakeRefPtr<Ellipse>();
214     ellipse->SetRadiusX(lengths.at(0));
215     ellipse->SetRadiusY(lengths.at(1));
216     return ellipse;
217 }
218 
CreateInset(const std::string & data)219 RefPtr<Inset> ClipPath::CreateInset(const std::string& data)
220 {
221     std::string::size_type roundIndex = data.find("round");
222     if (roundIndex == std::string::npos) {
223         return CreateInsetSize(data);
224     }
225     auto inset = CreateInsetSize(StringUtils::TrimStr(data.substr(0, roundIndex)));
226     if (!inset) {
227         return nullptr;
228     }
229     std::string roundData = StringUtils::TrimStr(data.substr(roundIndex + 5));
230     std::string::size_type roundDataIndex = roundData.find('/');
231     if (roundDataIndex == std::string::npos) {
232         std::vector<Dimension> rounds;
233         StringUtils::SplitStr(roundData, " ", rounds);
234         if (rounds.empty()) {
235             return inset;
236         }
237         inset->SetRadius(rounds);
238     } else {
239         std::vector<Dimension> rounds1;
240         StringUtils::SplitStr(StringUtils::TrimStr(roundData.substr(0, roundDataIndex)), " ", rounds1);
241         if (!rounds1.empty()) {
242             inset->SetRadius(rounds1, true);
243         }
244         std::vector<Dimension> rounds2;
245         StringUtils::SplitStr(StringUtils::TrimStr(roundData.substr(roundDataIndex + 1)), " ", rounds2);
246         if (!rounds2.empty()) {
247             inset->SetRadius(rounds2, false);
248         }
249     }
250     return inset;
251 }
252 
CreateInsetSize(const std::string & data)253 RefPtr<Inset> ClipPath::CreateInsetSize(const std::string& data)
254 {
255     std::vector<Dimension> lengths;
256     StringUtils::SplitStr(data, " ", lengths);
257     if (lengths.empty()) {
258         return nullptr;
259     }
260     auto inset = AceType::MakeRefPtr<Inset>();
261     if (inset->SetLength(lengths)) {
262         return inset;
263     }
264     return nullptr;
265 }
266 
CreatePolygon(const std::string & data)267 RefPtr<Polygon> ClipPath::CreatePolygon(const std::string& data)
268 {
269     std::vector<std::string> points;
270     StringUtils::StringSplitter(StringUtils::TrimStr(data), ',', points);
271     if (points.empty()) {
272         return nullptr;
273     }
274     auto polygon = AceType::MakeRefPtr<Polygon>();
275     for (const auto& item : points) {
276         std::vector<Dimension> point;
277         StringUtils::SplitStr(StringUtils::TrimStr(item), " ", point);
278         if (point.size() != 2) {
279             return nullptr;
280         }
281         polygon->PushPoint(point[0], point[1]);
282     }
283     if (polygon->IsValid()) {
284         return polygon;
285     }
286     return nullptr;
287 }
288 
CreatePath(const std::string & data)289 RefPtr<Path> ClipPath::CreatePath(const std::string& data)
290 {
291     if (data.empty()) {
292         return nullptr;
293     }
294     auto path = AceType::MakeRefPtr<Path>();
295     path->SetValue(StringUtils::TrimStr(data));
296     return path;
297 }
298 
CreateShape(const std::string & value)299 RefPtr<ClipPath> ClipPath::CreateShape(const std::string& value)
300 {
301     BasicShapeType basicShapeType = BasicShapeType::NONE;
302     std::string data;
303     GetBasicShapeInfo(value, basicShapeType, data);
304     if (basicShapeType == BasicShapeType::NONE || data.empty()) {
305         return nullptr;
306     }
307     RefPtr<BasicShape> basicShape = nullptr;
308     switch (basicShapeType) {
309         case BasicShapeType::CIRCLE: {
310             basicShape = CreateCircle(data);
311             break;
312         }
313         case BasicShapeType::ELLIPSE: {
314             basicShape = CreateEllipse(data);
315             break;
316         }
317         case BasicShapeType::INSET: {
318             basicShape = CreateInset(data);
319             break;
320         }
321         case BasicShapeType::PATH: {
322             basicShape = CreatePath(data);
323             break;
324         }
325         case BasicShapeType::POLYGON: {
326             basicShape = CreatePolygon(data);
327             break;
328         }
329         default: {
330             LOGE("basic shape type is none or invalid");
331             break;
332         }
333     }
334     if (basicShape) {
335         return AceType::MakeRefPtr<ClipPath>(basicShape);
336     }
337     return nullptr;
338 }
339 
340 } // namespace OHOS::Ace
341