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