• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "utils_path.h"
17 
18 #include <map>
19 
20 #include "include/core/SkPoint.h"
21 #include "src/core/SkGeometry.h"
22 #include "src/core/SkReadBuffer.h"
23 #include "src/core/SkWriteBuffer.h"
24 
25 #include "draw/path.h"
26 #include "utils/log.h"
27 #include "utils/point.h"
28 
29 namespace OHOS {
30 namespace Rosen {
31 namespace Drawing {
32 static constexpr float QUADRATIC_BINOMIAL_COEFF = 2.0f;
33 static constexpr int CONIC_CTRL_POINT_COUNT = 4;
34 static constexpr int CUBIC_BEZIER_BINOMIAL = 3;
35 static constexpr int MAX_BEZIER_SEGMENTS = 5000000; // 5000000 is a safe upper bound.
36 static constexpr int MID_T_DIVISOR = 2;
37 static constexpr int QUADRATIC_BEZIER_POINT_COUNT = 3;
38 static constexpr int QUADRATIC_STRIDE = 2;
39 
ComputeQuadratic(float param,float start,float control,float end)40 float ComputeQuadratic(float param, float start, float control, float end)
41 {
42     float invParam = 1 - param;
43     float invSqr = invParam * invParam;
44     float crossTerm = QUADRATIC_BINOMIAL_COEFF * invParam * param;
45     float paramSqr = param * param;
46     return invSqr * start + crossTerm * control + paramSqr * end;
47 }
48 
ComputeCubic(float param,float start,float control1,float control2,float end)49 static float ComputeCubic(float param, float start, float control1, float control2, float end)
50 {
51     float invParam = 1 - param;
52     float invSqr = invParam * invParam;
53     float invCubed = invSqr * invParam;
54     float paramSqr = param * param;
55     float paramCubed = paramSqr * param;
56     return invCubed * start + CUBIC_BEZIER_BINOMIAL * invSqr * param * control1 +
57         CUBIC_BEZIER_BINOMIAL * invParam * paramSqr * control2 + paramCubed * end;
58 }
59 
ShouldSubdivide(const Point * controlPoints,UtilsPath::BezierFunction calculationBezier,float startT,const Point & startPt,float endT,const Point & endPt,float & midParam,Point & midPt,float maxError)60 static bool ShouldSubdivide(const Point* controlPoints, UtilsPath::BezierFunction calculationBezier, float startT,
61     const Point& startPt, float endT, const Point& endPt, float& midParam, Point& midPt, float maxError)
62 {
63     midParam = (startT + endT) / MID_T_DIVISOR;
64     midPt = calculationBezier(midParam, controlPoints);
65     float avgX = (startPt.GetX() + endPt.GetX()) / MID_T_DIVISOR;
66     float avgY = (startPt.GetY() + endPt.GetY()) / MID_T_DIVISOR;
67     float dx = midPt.GetX() - avgX;
68     float dy = midPt.GetY() - avgY;
69     float error = dx * dx + dy * dy;
70     return error > maxError;
71 }
72 
Distance(const Point & p1,const Point & p2)73 scalar UtilsPath::Distance(const Point& p1, const Point& p2)
74 {
75     return SkPoint::Distance(reinterpret_cast<const SkPoint &>(p1), reinterpret_cast<const SkPoint &>(p2));
76 }
77 
AddMove(const Point & point,std::vector<Point> & approxPoints,std::vector<float> & approxLengths)78 void UtilsPath::AddMove(const Point& point, std::vector<Point>& approxPoints, std::vector<float>& approxLengths)
79 {
80     float length = approxLengths.empty() ? 0.0f : approxLengths.back();
81     approxPoints.push_back(point);
82     approxLengths.push_back(length);
83 }
84 
AddLine(const Point & point,std::vector<Point> & approxPoints,std::vector<float> & approxLengths)85 void UtilsPath::AddLine(const Point& point, std::vector<Point>& approxPoints, std::vector<float>& approxLengths)
86 {
87     if (approxPoints.empty()) {
88         approxPoints.push_back(Point(0, 0));
89         approxLengths.push_back(0);
90     } else if (approxPoints.back() == point) {
91         return;
92     }
93     float length = approxLengths.back() + Distance(approxPoints.back(), point);
94     approxPoints.push_back(point);
95     approxLengths.push_back(length);
96 }
97 
CalculateQuadraticBezier(float t,const Point * points)98 Point UtilsPath::CalculateQuadraticBezier(float t, const Point* points)
99 {
100     // 0, 1, 2 are the control points of a quad bezier curve.
101     float x = ComputeQuadratic(t, points[0].GetX(), points[1].GetX(), points[2].GetX());
102     float y = ComputeQuadratic(t, points[0].GetY(), points[1].GetY(), points[2].GetY());
103     return Point(x, y);
104 }
105 
CalculateCubicBezier(float t,const Point * points)106 Point UtilsPath::CalculateCubicBezier(float t, const Point* points)
107 {
108     // 0, 1, 2, 3 are the control points of a cubic bezier curve.
109     float x = ComputeCubic(t, points[0].GetX(), points[1].GetX(), points[2].GetX(), points[3].GetX());
110     float y = ComputeCubic(t, points[0].GetY(), points[1].GetY(), points[2].GetY(), points[3].GetY());
111     return Point(x, y);
112 }
113 
AddBezier(const Point * points,BezierFunction calculationBezier,std::vector<Point> & approxPoints,std::vector<float> & approxLengths,float errorSquared,bool doubleCheck)114 void UtilsPath::AddBezier(const Point* points, BezierFunction calculationBezier, std::vector<Point>& approxPoints,
115     std::vector<float>& approxLengths, float errorSquared, bool doubleCheck)
116 {
117     using TimePointMap = std::map<float, Point>;
118     TimePointMap timeToPoints;
119     timeToPoints[0] = calculationBezier(0.0f, points);
120     timeToPoints[1] = calculationBezier(1.0f, points);
121     auto it = timeToPoints.begin();
122     auto nextIt = std::next(it);
123     size_t count = 0;
124     while (nextIt != timeToPoints.end()) {
125         count++;
126         if (count > MAX_BEZIER_SEGMENTS) {
127             LOGW("UtilsPath::AddBezier count too large.");
128             break;
129         }
130         float midT;
131         Point midPt;
132         bool split = ShouldSubdivide(points, calculationBezier, it->first, it->second, nextIt->first, nextIt->second,
133             midT, midPt, errorSquared);
134         if (!split && doubleCheck) {
135             float qT;
136             Point qPt;
137             split = ShouldSubdivide(points, calculationBezier, it->first, it->second, midT, midPt, qT, qPt,
138                 errorSquared);
139             if (split) {
140                 doubleCheck = false;
141             }
142         }
143         if (split) {
144             nextIt = timeToPoints.insert(it, { midT, midPt });
145         } else {
146             ++it;
147             nextIt = std::next(it);
148         }
149     }
150     for (const auto& [_, pt] : timeToPoints) {
151         AddLine(pt, approxPoints, approxLengths);
152     }
153 }
154 
AddConic(PathIter & pathIter,const Point * points,std::vector<Point> & approxPoints,std::vector<float> & approxLengths,float errorConic)155 void UtilsPath::AddConic(PathIter& pathIter, const Point* points, std::vector<Point>& approxPoints,
156     std::vector<float>& approxLengths, float errorConic)
157 {
158     SkAutoConicToQuads converter;
159     SkPoint skPoints[CONIC_CTRL_POINT_COUNT];
160     for (int i = 0; i < CONIC_CTRL_POINT_COUNT; ++i) {
161         skPoints[i] = SkPoint::Make(points[i].GetX(), points[i].GetY());
162     }
163     const SkPoint* quads = converter.computeQuads(skPoints, pathIter.ConicWeight(), errorConic);
164     if (quads == nullptr) {
165         LOGE("UtilsPath::AddConic quads is nullptr.");
166         return;
167     }
168     for (int i = 0; i < converter.countQuads(); ++i) {
169         Point quadPoints[QUADRATIC_BEZIER_POINT_COUNT];
170         for (int j = 0; j < QUADRATIC_BEZIER_POINT_COUNT; ++j) {
171             quadPoints[j] = Point(quads[i * QUADRATIC_STRIDE + j].x(), quads[i * QUADRATIC_STRIDE + j].y());
172         }
173         UtilsPath::AddBezier(quadPoints, UtilsPath::CalculateQuadraticBezier, approxPoints, approxLengths, errorConic,
174             false);
175     }
176 }
177 
178 } // namespace Drawing
179 } // namespace Rosen
180 } // namespace OHOS
181