1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "xfa/fde/cfde_path.h"
8
9 #include "third_party/base/stl_util.h"
10 #include "xfa/fde/fde_object.h"
11
CloseFigure()12 void CFDE_Path::CloseFigure() {
13 m_Path.ClosePath();
14 }
15
FigureClosed() const16 bool CFDE_Path::FigureClosed() const {
17 const std::vector<FX_PATHPOINT>& points = m_Path.GetPoints();
18 return points.empty() ? true : points.back().m_CloseFigure;
19 }
20
MoveTo(const CFX_PointF & point)21 void CFDE_Path::MoveTo(const CFX_PointF& point) {
22 m_Path.AppendPoint(point, FXPT_TYPE::MoveTo, false);
23 }
24
LineTo(const CFX_PointF & point)25 void CFDE_Path::LineTo(const CFX_PointF& point) {
26 m_Path.AppendPoint(point, FXPT_TYPE::LineTo, false);
27 }
28
BezierTo(const CFX_PointF & p1,const CFX_PointF & p2,const CFX_PointF & p3)29 void CFDE_Path::BezierTo(const CFX_PointF& p1,
30 const CFX_PointF& p2,
31 const CFX_PointF& p3) {
32 m_Path.AppendPoint(p1, FXPT_TYPE::BezierTo, false);
33 m_Path.AppendPoint(p2, FXPT_TYPE::BezierTo, false);
34 m_Path.AppendPoint(p3, FXPT_TYPE::BezierTo, false);
35 }
36
ArcTo(bool bStart,const CFX_RectF & rect,FX_FLOAT startAngle,FX_FLOAT endAngle)37 void CFDE_Path::ArcTo(bool bStart,
38 const CFX_RectF& rect,
39 FX_FLOAT startAngle,
40 FX_FLOAT endAngle) {
41 FX_FLOAT rx = rect.width / 2;
42 FX_FLOAT ry = rect.height / 2;
43 FX_FLOAT cx = rect.left + rx;
44 FX_FLOAT cy = rect.top + ry;
45 FX_FLOAT alpha =
46 FXSYS_atan2(rx * FXSYS_sin(startAngle), ry * FXSYS_cos(startAngle));
47 FX_FLOAT beta =
48 FXSYS_atan2(rx * FXSYS_sin(endAngle), ry * FXSYS_cos(endAngle));
49 if (FXSYS_fabs(beta - alpha) > FX_PI) {
50 if (beta > alpha)
51 beta -= 2 * FX_PI;
52 else
53 alpha -= 2 * FX_PI;
54 }
55
56 FX_FLOAT half_delta = (beta - alpha) / 2;
57 FX_FLOAT bcp = 4.0f / 3 * (1 - FXSYS_cos(half_delta)) / FXSYS_sin(half_delta);
58 FX_FLOAT sin_alpha = FXSYS_sin(alpha);
59 FX_FLOAT sin_beta = FXSYS_sin(beta);
60 FX_FLOAT cos_alpha = FXSYS_cos(alpha);
61 FX_FLOAT cos_beta = FXSYS_cos(beta);
62 if (bStart)
63 MoveTo(CFX_PointF(cx + rx * cos_alpha, cy + ry * sin_alpha));
64
65 BezierTo(CFX_PointF(cx + rx * (cos_alpha - bcp * sin_alpha),
66 cy + ry * (sin_alpha + bcp * cos_alpha)),
67 CFX_PointF(cx + rx * (cos_beta + bcp * sin_beta),
68 cy + ry * (sin_beta - bcp * cos_beta)),
69 CFX_PointF(cx + rx * cos_beta, cy + ry * sin_beta));
70 }
71
AddBezier(const std::vector<CFX_PointF> & points)72 void CFDE_Path::AddBezier(const std::vector<CFX_PointF>& points) {
73 if (points.size() != 4)
74 return;
75
76 MoveTo(points[0]);
77 BezierTo(points[1], points[2], points[3]);
78 }
79
AddBeziers(const std::vector<CFX_PointF> & points)80 void CFDE_Path::AddBeziers(const std::vector<CFX_PointF>& points) {
81 int32_t iCount = points.size();
82 if (iCount < 4)
83 return;
84
85 const CFX_PointF* p = points.data();
86 const CFX_PointF* pEnd = p + iCount;
87 MoveTo(p[0]);
88 for (++p; p <= pEnd - 3; p += 3)
89 BezierTo(p[0], p[1], p[2]);
90 }
91
GetCurveTangents(const std::vector<CFX_PointF> & points,std::vector<CFX_PointF> * tangents,bool bClosed,FX_FLOAT fTension) const92 void CFDE_Path::GetCurveTangents(const std::vector<CFX_PointF>& points,
93 std::vector<CFX_PointF>* tangents,
94 bool bClosed,
95 FX_FLOAT fTension) const {
96 int32_t iCount = pdfium::CollectionSize<int32_t>(points);
97 tangents->resize(iCount);
98 if (iCount < 3)
99 return;
100
101 FX_FLOAT fCoefficient = fTension / 3.0f;
102 const CFX_PointF* pPoints = points.data();
103 CFX_PointF* pTangents = tangents->data();
104 for (int32_t i = 0; i < iCount; ++i) {
105 int32_t r = i + 1;
106 int32_t s = i - 1;
107 if (r >= iCount)
108 r = bClosed ? (r - iCount) : (iCount - 1);
109 if (s < 0)
110 s = bClosed ? (s + iCount) : 0;
111
112 pTangents[i].x += (fCoefficient * (pPoints[r].x - pPoints[s].x));
113 pTangents[i].y += (fCoefficient * (pPoints[r].y - pPoints[s].y));
114 }
115 }
116
AddCurve(const std::vector<CFX_PointF> & points,bool bClosed,FX_FLOAT fTension)117 void CFDE_Path::AddCurve(const std::vector<CFX_PointF>& points,
118 bool bClosed,
119 FX_FLOAT fTension) {
120 int32_t iLast = pdfium::CollectionSize<int32_t>(points) - 1;
121 if (iLast < 1)
122 return;
123
124 std::vector<CFX_PointF> tangents;
125 GetCurveTangents(points, &tangents, bClosed, fTension);
126 const CFX_PointF* pPoints = points.data();
127 CFX_PointF* pTangents = tangents.data();
128 MoveTo(pPoints[0]);
129 for (int32_t i = 0; i < iLast; ++i) {
130 BezierTo(CFX_PointF(pPoints[i].x + pTangents[i].x,
131 pPoints[i].y + pTangents[i].y),
132 CFX_PointF(pPoints[i + 1].x - pTangents[i + 1].x,
133 pPoints[i + 1].y - pTangents[i + 1].y),
134 CFX_PointF(pPoints[i + 1].x, pPoints[i + 1].y));
135 }
136 if (bClosed) {
137 BezierTo(CFX_PointF(pPoints[iLast].x + pTangents[iLast].x,
138 pPoints[iLast].y + pTangents[iLast].y),
139 CFX_PointF(pPoints[0].x - pTangents[0].x,
140 pPoints[0].y - pTangents[0].y),
141 CFX_PointF(pPoints[0].x, pPoints[0].y));
142 CloseFigure();
143 }
144 }
145
AddEllipse(const CFX_RectF & rect)146 void CFDE_Path::AddEllipse(const CFX_RectF& rect) {
147 FX_FLOAT fStartAngle = 0;
148 FX_FLOAT fEndAngle = FX_PI / 2;
149 for (int32_t i = 0; i < 4; ++i) {
150 ArcTo(i == 0, rect, fStartAngle, fEndAngle);
151 fStartAngle += FX_PI / 2;
152 fEndAngle += FX_PI / 2;
153 }
154 CloseFigure();
155 }
156
AddLine(const CFX_PointF & pt1,const CFX_PointF & pt2)157 void CFDE_Path::AddLine(const CFX_PointF& pt1, const CFX_PointF& pt2) {
158 std::vector<FX_PATHPOINT>& points = m_Path.GetPoints();
159 if (points.empty() || FXSYS_fabs(points.back().m_Point.x - pt1.x) > 0.001 ||
160 FXSYS_fabs(points.back().m_Point.y - pt1.y) > 0.001) {
161 MoveTo(pt1);
162 }
163 LineTo(pt2);
164 }
165
AddPath(const CFDE_Path * pSrc,bool bConnect)166 void CFDE_Path::AddPath(const CFDE_Path* pSrc, bool bConnect) {
167 if (!pSrc)
168 return;
169
170 if (pSrc->m_Path.GetPoints().empty())
171 return;
172 if (bConnect)
173 LineTo(pSrc->m_Path.GetPoint(0));
174
175 m_Path.Append(&pSrc->m_Path, nullptr);
176 }
177
AddPolygon(const std::vector<CFX_PointF> & points)178 void CFDE_Path::AddPolygon(const std::vector<CFX_PointF>& points) {
179 size_t iCount = points.size();
180 if (iCount < 2)
181 return;
182
183 AddLines(points);
184 const CFX_PointF* p = points.data();
185 if (FXSYS_fabs(p[0].x - p[iCount - 1].x) < 0.01f ||
186 FXSYS_fabs(p[0].y - p[iCount - 1].y) < 0.01f) {
187 LineTo(p[0]);
188 }
189 CloseFigure();
190 }
191
AddLines(const std::vector<CFX_PointF> & points)192 void CFDE_Path::AddLines(const std::vector<CFX_PointF>& points) {
193 size_t iCount = points.size();
194 if (iCount < 2)
195 return;
196
197 const CFX_PointF* p = points.data();
198 const CFX_PointF* pEnd = p + iCount;
199 MoveTo(p[0]);
200 for (++p; p < pEnd; ++p)
201 LineTo(*p);
202 }
203
AddRectangle(const CFX_RectF & rect)204 void CFDE_Path::AddRectangle(const CFX_RectF& rect) {
205 MoveTo(rect.TopLeft());
206 LineTo(rect.TopRight());
207 LineTo(rect.BottomRight());
208 LineTo(rect.BottomLeft());
209 CloseFigure();
210 }
211
GetBBox() const212 CFX_RectF CFDE_Path::GetBBox() const {
213 CFX_FloatRect rect = m_Path.GetBoundingBox();
214 CFX_RectF bbox = CFX_RectF(rect.left, rect.top, rect.Width(), rect.Height());
215 bbox.Normalize();
216 return bbox;
217 }
218
GetBBox(FX_FLOAT fLineWidth,FX_FLOAT fMiterLimit) const219 CFX_RectF CFDE_Path::GetBBox(FX_FLOAT fLineWidth, FX_FLOAT fMiterLimit) const {
220 CFX_FloatRect rect = m_Path.GetBoundingBox(fLineWidth, fMiterLimit);
221 CFX_RectF bbox = CFX_RectF(rect.left, rect.top, rect.Width(), rect.Height());
222 bbox.Normalize();
223 return bbox;
224 }
225