1 /*
2 * Copyright (c) 2021-2025 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 "draw/path.h"
17
18 #include "impl_factory.h"
19
20 #include "utils/log.h"
21 #include "utils/utils_path.h"
22
23 namespace OHOS {
24 namespace Rosen {
25 namespace Drawing {
26 static constexpr int FRACTION_DIM = 3;
27 static constexpr int MAX_APPROXIMATE_VALUES = 1000000; // max number of values to approximate a curve
28
29 // LCOV_EXCL_START
Path()30 Path::Path() noexcept : impl_(ImplFactory::CreatePathImpl()) {}
31
Path(const Path & other)32 Path::Path(const Path& other) noexcept
33 {
34 impl_.reset(other.impl_->Clone());
35 }
36
operator =(const Path & other)37 Path& Path::operator=(const Path &other) noexcept
38 {
39 impl_.reset(other.impl_->Clone());
40 return *this;
41 }
42
~Path()43 Path::~Path() {}
44 // LCOV_EXCL_STOP
45
BuildFromSVGString(const std::string & str)46 bool Path::BuildFromSVGString(const std::string& str)
47 {
48 return impl_->InitWithSVGString(str);
49 }
50
51 // LCOV_EXCL_START
ConvertToSVGString() const52 std::string Path::ConvertToSVGString() const
53 {
54 return impl_->ConvertToSVGString();
55 }
56 // LCOV_EXCL_STOP
57
MoveTo(scalar x,scalar y)58 void Path::MoveTo(scalar x, scalar y)
59 {
60 impl_->MoveTo(x, y);
61 }
62
LineTo(scalar x,scalar y)63 void Path::LineTo(scalar x, scalar y)
64 {
65 impl_->LineTo(x, y);
66 }
67
ArcTo(scalar pt1X,scalar pt1Y,scalar pt2X,scalar pt2Y,scalar startAngle,scalar sweepAngle)68 void Path::ArcTo(scalar pt1X, scalar pt1Y, scalar pt2X, scalar pt2Y, scalar startAngle, scalar sweepAngle)
69 {
70 impl_->ArcTo(pt1X, pt1Y, pt2X, pt2Y, startAngle, sweepAngle);
71 }
72
ArcTo(const Point & pt1,const Point & pt2,scalar startAngle,scalar sweepAngle)73 void Path::ArcTo(const Point& pt1, const Point& pt2, scalar startAngle, scalar sweepAngle)
74 {
75 impl_->ArcTo(pt1.GetX(), pt1.GetY(), pt2.GetX(), pt2.GetY(), startAngle, sweepAngle);
76 }
77
ArcTo(scalar rx,scalar ry,scalar angle,PathDirection direction,scalar endX,scalar endY)78 void Path::ArcTo(scalar rx, scalar ry, scalar angle, PathDirection direction, scalar endX, scalar endY)
79 {
80 impl_->ArcTo(rx, ry, angle, direction, endX, endY);
81 }
82
ArcTo(scalar x1,scalar y1,scalar x2,scalar y2,scalar radius)83 void Path::ArcTo(scalar x1, scalar y1, scalar x2, scalar y2, scalar radius)
84 {
85 impl_->ArcTo(x1, y1, x2, y2, radius);
86 }
87
CubicTo(scalar ctrlPt1X,scalar ctrlPt1Y,scalar ctrlPt2X,scalar ctrlPt2Y,scalar endPtX,scalar endPtY)88 void Path::CubicTo(scalar ctrlPt1X, scalar ctrlPt1Y, scalar ctrlPt2X, scalar ctrlPt2Y, scalar endPtX, scalar endPtY)
89 {
90 impl_->CubicTo(ctrlPt1X, ctrlPt1Y, ctrlPt2X, ctrlPt2Y, endPtX, endPtY);
91 }
92
CubicTo(const Point & ctrlPt1,const Point & ctrlPt2,const Point & endPt)93 void Path::CubicTo(const Point& ctrlPt1, const Point& ctrlPt2, const Point& endPt)
94 {
95 impl_->CubicTo(ctrlPt1.GetX(), ctrlPt1.GetY(), ctrlPt2.GetX(), ctrlPt2.GetY(), endPt.GetX(), endPt.GetY());
96 }
97
QuadTo(scalar ctrlPtX,scalar ctrlPtY,scalar endPtX,scalar endPtY)98 void Path::QuadTo(scalar ctrlPtX, scalar ctrlPtY, scalar endPtX, scalar endPtY)
99 {
100 impl_->QuadTo(ctrlPtX, ctrlPtY, endPtX, endPtY);
101 }
102
QuadTo(const Point & ctrlPt,const Point endPt)103 void Path::QuadTo(const Point& ctrlPt, const Point endPt)
104 {
105 impl_->QuadTo(ctrlPt.GetX(), ctrlPt.GetY(), endPt.GetX(), endPt.GetY());
106 }
107
ConicTo(scalar ctrlX,scalar ctrlY,scalar endX,scalar endY,scalar weight)108 void Path::ConicTo(scalar ctrlX, scalar ctrlY, scalar endX, scalar endY, scalar weight)
109 {
110 impl_->ConicTo(ctrlX, ctrlY, endX, endY, weight);
111 }
112
RMoveTo(scalar dx,scalar dy)113 void Path::RMoveTo(scalar dx, scalar dy)
114 {
115 impl_->RMoveTo(dx, dy);
116 }
117
RLineTo(scalar dx,scalar dy)118 void Path::RLineTo(scalar dx, scalar dy)
119 {
120 impl_->RLineTo(dx, dy);
121 }
122
RArcTo(scalar rx,scalar ry,scalar angle,PathDirection direction,scalar dx,scalar dy)123 void Path::RArcTo(scalar rx, scalar ry, scalar angle, PathDirection direction, scalar dx, scalar dy)
124 {
125 impl_->RArcTo(rx, ry, angle, direction, dx, dy);
126 }
127
RCubicTo(scalar dx1,scalar dy1,scalar dx2,scalar dy2,scalar dx3,scalar dy3)128 void Path::RCubicTo(scalar dx1, scalar dy1, scalar dx2, scalar dy2, scalar dx3, scalar dy3)
129 {
130 impl_->RCubicTo(dx1, dy1, dx2, dy2, dx3, dy3);
131 }
132
RConicTo(scalar ctrlPtX,scalar ctrlPtY,scalar endPtX,scalar endPtY,scalar weight)133 void Path::RConicTo(scalar ctrlPtX, scalar ctrlPtY, scalar endPtX, scalar endPtY, scalar weight)
134 {
135 impl_->RConicTo(ctrlPtX, ctrlPtY, endPtX, endPtY, weight);
136 }
137
RQuadTo(scalar dx1,scalar dy1,scalar dx2,scalar dy2)138 void Path::RQuadTo(scalar dx1, scalar dy1, scalar dx2, scalar dy2)
139 {
140 impl_->RQuadTo(dx1, dy1, dx2, dy2);
141 }
142
AddRect(const Rect & rect,PathDirection dir)143 void Path::AddRect(const Rect& rect, PathDirection dir)
144 {
145 impl_->AddRect(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom(), dir);
146 }
147
AddRect(const Rect & rect,unsigned start,PathDirection dir)148 void Path::AddRect(const Rect& rect, unsigned start, PathDirection dir)
149 {
150 impl_->AddRect(rect, start, dir);
151 }
152
AddRect(scalar left,scalar top,scalar right,scalar bottom,PathDirection dir)153 void Path::AddRect(scalar left, scalar top, scalar right, scalar bottom, PathDirection dir)
154 {
155 impl_->AddRect(left, top, right, bottom, dir);
156 }
157
AddOval(const Rect & oval,PathDirection dir)158 void Path::AddOval(const Rect& oval, PathDirection dir)
159 {
160 impl_->AddOval(oval.GetLeft(), oval.GetTop(), oval.GetRight(), oval.GetBottom(), dir);
161 }
162
AddOval(const Rect & oval,unsigned start,PathDirection dir)163 void Path::AddOval(const Rect& oval, unsigned start, PathDirection dir)
164 {
165 impl_->AddOval(oval.GetLeft(), oval.GetTop(), oval.GetRight(), oval.GetBottom(), start, dir);
166 }
167
AddArc(const Rect & oval,scalar startAngle,scalar sweepAngle)168 void Path::AddArc(const Rect& oval, scalar startAngle, scalar sweepAngle)
169 {
170 impl_->AddArc(oval.GetLeft(), oval.GetTop(), oval.GetRight(), oval.GetBottom(), startAngle, sweepAngle);
171 }
172
AddPoly(const std::vector<Point> & points,int count,bool close)173 void Path::AddPoly(const std::vector<Point>& points, int count, bool close)
174 {
175 impl_->AddPoly(points, count, close);
176 }
177
AddCircle(scalar x,scalar y,scalar radius,PathDirection dir)178 void Path::AddCircle(scalar x, scalar y, scalar radius, PathDirection dir)
179 {
180 impl_->AddCircle(x, y, radius, dir);
181 }
182
AddRoundRect(const Rect & rect,scalar xRadius,scalar yRadius,PathDirection dir)183 void Path::AddRoundRect(const Rect& rect, scalar xRadius, scalar yRadius, PathDirection dir)
184 {
185 impl_->AddRoundRect(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom(), xRadius, yRadius, dir);
186 }
187
AddRoundRect(const RoundRect & rrect,PathDirection dir)188 void Path::AddRoundRect(const RoundRect& rrect, PathDirection dir)
189 {
190 impl_->AddRoundRect(rrect, dir);
191 }
192
AddPath(const Path & src,scalar dx,scalar dy,PathAddMode mode)193 void Path::AddPath(const Path& src, scalar dx, scalar dy, PathAddMode mode)
194 {
195 impl_->AddPath(src, dx, dy, mode);
196 }
197
AddPath(const Path & src,PathAddMode mode)198 void Path::AddPath(const Path& src, PathAddMode mode)
199 {
200 impl_->AddPath(src, mode);
201 }
202
AddPath(const Path & src,const Matrix & matrix,PathAddMode mode)203 void Path::AddPath(const Path& src, const Matrix& matrix, PathAddMode mode)
204 {
205 impl_->AddPath(src, matrix, mode);
206 }
207
Contains(scalar x,scalar y) const208 bool Path::Contains(scalar x, scalar y) const
209 {
210 return impl_->Contains(x, y);
211 }
212
ReverseAddPath(const Path & src)213 void Path::ReverseAddPath(const Path& src)
214 {
215 impl_->ReverseAddPath(src);
216 }
217
GetBounds() const218 Rect Path::GetBounds() const
219 {
220 return impl_->GetBounds();
221 }
222
SetFillStyle(PathFillType fillstyle)223 void Path::SetFillStyle(PathFillType fillstyle)
224 {
225 impl_->SetFillStyle(fillstyle);
226 }
227
GetFillStyle() const228 PathFillType Path::GetFillStyle() const
229 {
230 return impl_->GetFillStyle();
231 }
232
Interpolate(const Path & ending,scalar weight,Path & out)233 bool Path::Interpolate(const Path& ending, scalar weight, Path& out)
234 {
235 return impl_->Interpolate(ending, weight, out);
236 }
237
CountVerbs() const238 int Path::CountVerbs() const
239 {
240 return impl_->CountVerbs();
241 }
242
GetPoint(int index) const243 Point Path::GetPoint(int index) const
244 {
245 return impl_->GetPoint(index);
246 }
247
IsInterpolate(const Path & other)248 bool Path::IsInterpolate(const Path& other)
249 {
250 return impl_->IsInterpolate(other);
251 }
252
HandlePathVerbSegments(PathVerb verb,PathIter & pathIter,const Point * points,std::vector<Point> & approxPoints,std::vector<float> & approxLengths,float errorSquared,float errorConic)253 static void HandlePathVerbSegments(PathVerb verb, PathIter& pathIter, const Point* points,
254 std::vector<Point>& approxPoints, std::vector<float>& approxLengths, float errorSquared, float errorConic)
255 {
256 switch (verb) {
257 case PathVerb::MOVE:
258 UtilsPath::AddMove(points[0], approxPoints, approxLengths);
259 break;
260 case PathVerb::CLOSE:
261 UtilsPath::AddLine(points[0], approxPoints, approxLengths);
262 break;
263 case PathVerb::LINE:
264 UtilsPath::AddLine(points[1], approxPoints, approxLengths);
265 break;
266 case PathVerb::QUAD:
267 UtilsPath::AddBezier(points, UtilsPath::CalculateQuadraticBezier, approxPoints, approxLengths,
268 errorSquared, false);
269 break;
270 case PathVerb::CUBIC:
271 UtilsPath::AddBezier(points, UtilsPath::CalculateCubicBezier, approxPoints, approxLengths,
272 errorSquared, true);
273 break;
274 case PathVerb::CONIC:
275 UtilsPath::AddConic(pathIter, points, approxPoints, approxLengths, errorConic);
276 break;
277 default:
278 break;
279 }
280 }
281
Approximate(scalar acceptableError,std::vector<scalar> & outApproxVals)282 void Path::Approximate(scalar acceptableError, std::vector<scalar>& outApproxVals)
283 {
284 if (acceptableError < 0.0f) {
285 LOGE("Path::Approximate acceptableError is invalid");
286 return;
287 }
288 std::vector<Point> approxPoints;
289 std::vector<float> approxLengths;
290 float errorSquared = acceptableError * acceptableError;
291 float errorConic = acceptableError / 2; // 2 Conic error is half of the quadratic error
292 PathIter pathIter(*this, false);
293 Point tpoints[4]; // 4 points for cubic and quad
294 PathVerb verb;
295 while ((verb = pathIter.Next(tpoints)) != PathVerb::DONE) {
296 HandlePathVerbSegments(verb, pathIter, tpoints, approxPoints, approxLengths, errorSquared, errorConic);
297 }
298 if (approxPoints.empty()) {
299 int verbCount = this->CountVerbs();
300 if (verbCount == 1) {
301 Point pt = this->GetPoint(0);
302 UtilsPath::AddMove(pt, approxPoints, approxLengths);
303 } else {
304 UtilsPath::AddMove(Point(0, 0), approxPoints, approxLengths);
305 }
306 }
307 float totalLength = approxLengths.empty() ? 1.0f : approxLengths.back();
308 if (totalLength <= 0.0f) {
309 totalLength = 1.0f;
310 approxPoints.push_back(approxPoints.empty() ? Point(0, 0) : approxPoints.back());
311 approxLengths.push_back(totalLength);
312 }
313 size_t numPoints = approxPoints.size();
314 size_t numValues = numPoints * FRACTION_DIM;
315 if (numValues > MAX_APPROXIMATE_VALUES) {
316 LOGW("Path::Approximate numValues is too large");
317 return;
318 }
319 outApproxVals.resize(numValues);
320 for (size_t i = 0; i < numPoints; i++) {
321 outApproxVals[i * FRACTION_DIM] = approxLengths[i] / totalLength;
322 outApproxVals[i * FRACTION_DIM + 1] = approxPoints[i].GetX();
323 outApproxVals[i * FRACTION_DIM + 2] = approxPoints[i].GetY(); // 2 is y coordinate
324 }
325 }
326
BuildFromInterpolate(const Path & src,const Path & ending,scalar weight)327 bool Path::BuildFromInterpolate(const Path& src, const Path& ending, scalar weight)
328 {
329 return impl_->InitWithInterpolate(src, ending, weight);
330 }
331
TransformWithPerspectiveClip(const Matrix & matrix,Path * dst,bool applyPerspectiveClip)332 void Path::TransformWithPerspectiveClip(const Matrix& matrix, Path* dst, bool applyPerspectiveClip)
333 {
334 impl_->TransformWithPerspectiveClip(matrix, dst, applyPerspectiveClip);
335 }
336
Transform(const Matrix & matrix)337 void Path::Transform(const Matrix& matrix)
338 {
339 impl_->Transform(matrix);
340 }
341
Offset(scalar dx,scalar dy)342 void Path::Offset(scalar dx, scalar dy)
343 {
344 impl_->Offset(dx, dy);
345 }
346
Offset(Path * dst,scalar dx,scalar dy)347 void Path::Offset(Path* dst, scalar dx, scalar dy)
348 {
349 impl_->Offset(dst, dx, dy);
350 }
351
Op(const Path & path1,Path & path2,PathOp op)352 bool Path::Op(const Path& path1, Path& path2, PathOp op)
353 {
354 return impl_->OpWith(path1, path2, op);
355 }
356
IsValid() const357 bool Path::IsValid() const
358 {
359 return impl_->IsValid();
360 }
361
Reset()362 void Path::Reset()
363 {
364 impl_->Reset();
365 }
366
SetLastPoint(scalar x,scalar y)367 void Path::SetLastPoint(scalar x, scalar y)
368 {
369 impl_->SetLastPoint(x, y);
370 }
371
ReWind()372 void Path::ReWind()
373 {
374 impl_->ReWind();
375 }
376
Close()377 void Path::Close()
378 {
379 impl_->Close();
380 }
381
GetLength(bool forceClosed) const382 scalar Path::GetLength(bool forceClosed) const
383 {
384 return impl_->GetLength(forceClosed);
385 }
386
GetPositionAndTangent(scalar distance,Point & position,Point & tangent,bool forceClosed) const387 bool Path::GetPositionAndTangent(scalar distance, Point& position, Point& tangent, bool forceClosed) const
388 {
389 return impl_->GetPositionAndTangent(distance, position, tangent, forceClosed);
390 }
391
GetSegment(scalar start,scalar stop,Path * dst,bool startWithMoveTo,bool forceClosed) const392 bool Path::GetSegment(scalar start, scalar stop, Path* dst, bool startWithMoveTo, bool forceClosed) const
393 {
394 return impl_->GetSegment(start, stop, dst, startWithMoveTo, forceClosed);
395 }
396
IsClosed(bool forceClosed) const397 bool Path::IsClosed(bool forceClosed) const
398 {
399 return impl_->IsClosed(forceClosed);
400 }
401
IsEmpty() const402 bool Path::IsEmpty() const
403 {
404 return impl_->IsEmpty();
405 }
406
IsRect(Rect * rect,bool * isClosed,PathDirection * direction) const407 bool Path::IsRect(Rect* rect, bool* isClosed, PathDirection* direction) const
408 {
409 return impl_->IsRect(rect, isClosed, direction);
410 }
411
SetPath(const Path & path)412 void Path::SetPath(const Path& path)
413 {
414 return impl_->SetPath(path);
415 }
416
GetMatrix(bool forceClosed,float distance,Matrix * matrix,PathMeasureMatrixFlags flag)417 bool Path::GetMatrix(bool forceClosed, float distance, Matrix* matrix, PathMeasureMatrixFlags flag)
418 {
419 return impl_->GetMatrix(forceClosed, distance, matrix, flag);
420 }
421
Serialize() const422 std::shared_ptr<Data> Path::Serialize() const
423 {
424 return impl_->Serialize();
425 }
426
Deserialize(std::shared_ptr<Data> data)427 bool Path::Deserialize(std::shared_ptr<Data> data)
428 {
429 return impl_->Deserialize(data);
430 }
431
432 } // namespace Drawing
433 } // namespace Rosen
434 } // namespace OHOS
435