Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfx_path.cpp
Go to the documentation of this file.
1// Copyright 2016 The PDFium Authors
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 "core/fxge/cfx_path.h"
8
9#include <math.h>
10
11#include <algorithm>
12#include <iterator>
13
14#include "core/fxcrt/fx_system.h"
15#include "third_party/base/check_op.h"
16
17namespace {
18
19bool IsRectPreTransform(const std::vector<CFX_Path::Point>& points) {
20 if (points.size() != 5 && points.size() != 4)
21 return false;
22
23 if (points.size() == 5 && points[0].m_Point != points[4].m_Point)
24 return false;
25
26 if (points[0].m_Point == points[2].m_Point ||
27 points[1].m_Point == points[3].m_Point) {
28 return false;
29 }
30
31 for (size_t i = 1; i < points.size(); ++i) {
32 if (points[i].m_Type != CFX_Path::Point::Type::kLine)
33 return false;
34 }
35 return true;
36}
37
38bool XYBothNotEqual(const CFX_PointF& p1, const CFX_PointF& p2) {
39 return p1.x != p2.x && p1.y != p2.y;
40}
41
42bool IsRectImpl(const std::vector<CFX_Path::Point>& points) {
43 if (!IsRectPreTransform(points))
44 return false;
45
46 for (int i = 1; i < 4; i++) {
47 if (XYBothNotEqual(points[i].m_Point, points[i - 1].m_Point))
48 return false;
49 }
50
51 if (XYBothNotEqual(points[0].m_Point, points[3].m_Point))
52 return false;
53
54 return true;
55}
56
57CFX_FloatRect CreateRectFromPoints(const CFX_PointF& p1, const CFX_PointF& p2) {
58 CFX_FloatRect rect(p1.x, p1.y, p2.x, p2.y);
59 rect.Normalize();
60 return rect;
61}
62
63bool PathPointsNeedNormalization(const std::vector<CFX_Path::Point>& points) {
64 return points.size() > 5;
65}
66
67std::vector<CFX_Path::Point> GetNormalizedPoints(
68 const std::vector<CFX_Path::Point>& points) {
69 DCHECK(PathPointsNeedNormalization(points));
70
71 if (points[0].m_Point != points.back().m_Point)
72 return {};
73
74 std::vector<CFX_Path::Point> normalized;
75 normalized.reserve(6);
76 normalized.push_back(points[0]);
77 for (auto it = points.begin() + 1; it != points.end(); ++it) {
78 // Exactly 5 points left. Stop normalizing and take what is left.
79 if (normalized.size() + std::distance(it, points.end()) == 5) {
80 std::copy(it, points.end(), std::back_inserter(normalized));
81 break;
82 }
83
84 // If the line does not move, skip this point.
85 const auto& point = *it;
86 if (point.m_Type == CFX_Path::Point::Type::kLine && !point.m_CloseFigure &&
87 !normalized.back().m_CloseFigure &&
88 point.m_Point == normalized.back().m_Point) {
89 continue;
90 }
91
92 normalized.push_back(point);
93
94 // Too many points. Not considered as a rectangle.
95 if (normalized.size() > 5)
96 return {};
97 }
98
99 DCHECK_EQ(5u, normalized.size());
100 return normalized;
101}
102
103void UpdateLineEndPoints(CFX_FloatRect* rect,
104 const CFX_PointF& start_pos,
105 const CFX_PointF& end_pos,
106 float hw) {
107 if (start_pos.x == end_pos.x) {
108 if (start_pos.y == end_pos.y) {
109 rect->UpdateRect(end_pos + CFX_PointF(hw, hw));
110 rect->UpdateRect(end_pos - CFX_PointF(hw, hw));
111 return;
112 }
113
114 float point_y;
115 if (end_pos.y < start_pos.y)
116 point_y = end_pos.y - hw;
117 else
118 point_y = end_pos.y + hw;
119
120 rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y));
121 rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y));
122 return;
123 }
124
125 if (start_pos.y == end_pos.y) {
126 float point_x;
127 if (end_pos.x < start_pos.x)
128 point_x = end_pos.x - hw;
129 else
130 point_x = end_pos.x + hw;
131
132 rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw));
133 rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw));
134 return;
135 }
136
137 CFX_PointF diff = end_pos - start_pos;
138 float ll = FXSYS_sqrt2(diff.x, diff.y);
139 float mx = end_pos.x + hw * diff.x / ll;
140 float my = end_pos.y + hw * diff.y / ll;
141 float dx1 = hw * diff.y / ll;
142 float dy1 = hw * diff.x / ll;
143 rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1));
144 rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1));
145}
146
147void UpdateLineJoinPoints(CFX_FloatRect* rect,
148 const CFX_PointF& start_pos,
149 const CFX_PointF& mid_pos,
150 const CFX_PointF& end_pos,
151 float half_width,
152 float miter_limit) {
153 float start_k = 0;
154 float start_c = 0;
155 float end_k = 0;
156 float end_c = 0;
157 float start_len = 0;
158 float start_dc = 0;
159 float end_len = 0;
160 float end_dc = 0;
161 float one_twentieth = 1.0f / 20;
162
163 bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth;
164 bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth;
165 if (bStartVert && bEndVert) {
166 int start_dir = mid_pos.y > start_pos.y ? 1 : -1;
167 float point_y = mid_pos.y + half_width * start_dir;
168 rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y));
169 rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y));
170 return;
171 }
172
173 if (!bStartVert) {
174 CFX_PointF start_to_mid = start_pos - mid_pos;
175 start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x);
176 start_c = mid_pos.y - (start_k * mid_pos.x);
177 start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y);
178 start_dc = fabsf(half_width * start_len / start_to_mid.x);
179 }
180 if (!bEndVert) {
181 CFX_PointF end_to_mid = end_pos - mid_pos;
182 end_k = end_to_mid.y / end_to_mid.x;
183 end_c = mid_pos.y - (end_k * mid_pos.x);
184 end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y);
185 end_dc = fabs(half_width * end_len / end_to_mid.x);
186 }
187 if (bStartVert) {
188 CFX_PointF outside(start_pos.x, 0);
189 if (end_pos.x < start_pos.x)
190 outside.x += half_width;
191 else
192 outside.x -= half_width;
193
194 if (start_pos.y < (end_k * start_pos.x) + end_c)
195 outside.y = (end_k * outside.x) + end_c + end_dc;
196 else
197 outside.y = (end_k * outside.x) + end_c - end_dc;
198
199 rect->UpdateRect(outside);
200 return;
201 }
202
203 if (bEndVert) {
204 CFX_PointF outside(end_pos.x, 0);
205 if (start_pos.x < end_pos.x)
206 outside.x += half_width;
207 else
208 outside.x -= half_width;
209
210 if (end_pos.y < (start_k * end_pos.x) + start_c)
211 outside.y = (start_k * outside.x) + start_c + start_dc;
212 else
213 outside.y = (start_k * outside.x) + start_c - start_dc;
214
215 rect->UpdateRect(outside);
216 return;
217 }
218
219 if (fabs(start_k - end_k) < one_twentieth) {
220 int start_dir = mid_pos.x > start_pos.x ? 1 : -1;
221 int end_dir = end_pos.x > mid_pos.x ? 1 : -1;
222 if (start_dir == end_dir)
223 UpdateLineEndPoints(rect, mid_pos, end_pos, half_width);
224 else
225 UpdateLineEndPoints(rect, start_pos, mid_pos, half_width);
226 return;
227 }
228
229 float start_outside_c = start_c;
230 if (end_pos.y < (start_k * end_pos.x) + start_c)
231 start_outside_c += start_dc;
232 else
233 start_outside_c -= start_dc;
234
235 float end_outside_c = end_c;
236 if (start_pos.y < (end_k * start_pos.x) + end_c)
237 end_outside_c += end_dc;
238 else
239 end_outside_c -= end_dc;
240
241 float join_x = (end_outside_c - start_outside_c) / (start_k - end_k);
242 float join_y = start_k * join_x + start_outside_c;
243 rect->UpdateRect(CFX_PointF(join_x, join_y));
244}
245
246} // namespace
247
248CFX_Path::Point::Point() = default;
249
250CFX_Path::Point::Point(const CFX_PointF& point, Type type, bool close)
251 : m_Point(point), m_Type(type), m_CloseFigure(close) {}
252
253CFX_Path::Point::Point(const Point& other) = default;
254
255CFX_Path::Point::~Point() = default;
256
257CFX_Path::CFX_Path() = default;
258
259CFX_Path::CFX_Path(const CFX_Path& src) = default;
260
261CFX_Path::CFX_Path(CFX_Path&& src) noexcept = default;
262
263CFX_Path::~CFX_Path() = default;
264
266 m_Points.clear();
267}
268
270 if (m_Points.empty())
271 return;
272 m_Points.back().m_CloseFigure = true;
273}
274
275void CFX_Path::Append(const CFX_Path& src, const CFX_Matrix* matrix) {
276 if (src.m_Points.empty())
277 return;
278
279 size_t cur_size = m_Points.size();
280 m_Points.insert(m_Points.end(), src.m_Points.begin(), src.m_Points.end());
281
282 if (!matrix)
283 return;
284
285 for (size_t i = cur_size; i < m_Points.size(); i++)
286 m_Points[i].m_Point = matrix->Transform(m_Points[i].m_Point);
287}
288
289void CFX_Path::AppendPoint(const CFX_PointF& point, Point::Type type) {
290 m_Points.emplace_back(point, type, /*close=*/false);
291}
292
293void CFX_Path::AppendPointAndClose(const CFX_PointF& point, Point::Type type) {
294 m_Points.emplace_back(point, type, /*close=*/true);
295}
296
297void CFX_Path::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) {
298 if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 ||
299 fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) {
301 }
303}
304
306 return AppendRect(rect.left, rect.bottom, rect.right, rect.top);
307}
308
309void CFX_Path::AppendRect(float left, float bottom, float right, float top) {
310 CFX_PointF left_bottom(left, bottom);
311 CFX_PointF left_top(left, top);
312 CFX_PointF right_top(right, top);
313 CFX_PointF right_bottom(right, bottom);
314
315 AppendLine(left_bottom, left_top);
316 AppendLine(left_top, right_top);
317 AppendLine(right_top, right_bottom);
318 AppendLine(right_bottom, left_bottom);
320}
321
323 if (m_Points.empty())
324 return CFX_FloatRect();
325
326 CFX_FloatRect rect(m_Points[0].m_Point);
327 for (size_t i = 1; i < m_Points.size(); ++i)
328 rect.UpdateRect(m_Points[i].m_Point);
329 return rect;
330}
331
333 float miter_limit) const {
334 CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f);
335 size_t iPoint = 0;
336 float half_width = line_width;
337 size_t iStartPoint = 0;
338 size_t iEndPoint = 0;
339 size_t iMiddlePoint = 0;
340 bool bJoin;
341 while (iPoint < m_Points.size()) {
342 if (m_Points[iPoint].m_Type == CFX_Path::Point::Type::kMove) {
343 if (iPoint + 1 == m_Points.size()) {
344 if (m_Points[iPoint].m_CloseFigure) {
345 // Update `rect` right away since this is the final point to be drawn.
346 rect.UpdateRect(m_Points[iPoint].m_Point);
347 }
348 break;
349 }
350
351 iStartPoint = iPoint + 1;
352 iEndPoint = iPoint;
353 bJoin = false;
354 } else {
355 if (m_Points[iPoint].IsTypeAndOpen(CFX_Path::Point::Type::kBezier)) {
356 // Callers are responsible for adding Beziers in sets of 3.
357 CHECK_LT(iPoint + 2, m_Points.size());
358 DCHECK_EQ(m_Points[iPoint + 1].m_Type, CFX_Path::Point::Type::kBezier);
359 DCHECK_EQ(m_Points[iPoint + 2].m_Type, CFX_Path::Point::Type::kBezier);
360 rect.UpdateRect(m_Points[iPoint].m_Point);
361 rect.UpdateRect(m_Points[iPoint + 1].m_Point);
362 iPoint += 2;
363 }
364 if (iPoint + 1 == m_Points.size() ||
365 m_Points[iPoint + 1].m_Type == CFX_Path::Point::Type::kMove) {
366 iStartPoint = iPoint - 1;
367 iEndPoint = iPoint;
368 bJoin = false;
369 } else {
370 iStartPoint = iPoint - 1;
371 iMiddlePoint = iPoint;
372 iEndPoint = iPoint + 1;
373 bJoin = true;
374 }
375 }
376 CHECK_LT(iStartPoint, m_Points.size());
377 CHECK_LT(iEndPoint, m_Points.size());
378 if (bJoin) {
379 CHECK_LT(iMiddlePoint, m_Points.size());
380 UpdateLineJoinPoints(
381 &rect, m_Points[iStartPoint].m_Point, m_Points[iMiddlePoint].m_Point,
382 m_Points[iEndPoint].m_Point, half_width, miter_limit);
383 } else {
384 UpdateLineEndPoints(&rect, m_Points[iStartPoint].m_Point,
385 m_Points[iEndPoint].m_Point, half_width);
386 }
387 ++iPoint;
388 }
389 return rect;
390}
391
392void CFX_Path::Transform(const CFX_Matrix& matrix) {
393 for (auto& point : m_Points)
394 point.m_Point = matrix.Transform(point.m_Point);
395}
396
397bool CFX_Path::IsRect() const {
398 if (PathPointsNeedNormalization(m_Points))
399 return IsRectImpl(GetNormalizedPoints(m_Points));
400 return IsRectImpl(m_Points);
401}
402
404 const CFX_Matrix* matrix) const {
405 bool do_normalize = PathPointsNeedNormalization(m_Points);
406 std::vector<Point> normalized;
407 if (do_normalize)
408 normalized = GetNormalizedPoints(m_Points);
409 const std::vector<Point>& path_points = do_normalize ? normalized : m_Points;
410
411 if (!matrix) {
412 if (!IsRectImpl(path_points))
413 return absl::nullopt;
414
415 return CreateRectFromPoints(path_points[0].m_Point, path_points[2].m_Point);
416 }
417
418 if (!IsRectPreTransform(path_points))
419 return absl::nullopt;
420
421 CFX_PointF points[5];
422 for (size_t i = 0; i < path_points.size(); ++i) {
423 points[i] = matrix->Transform(path_points[i].m_Point);
424
425 if (i == 0)
426 continue;
427 if (XYBothNotEqual(points[i], points[i - 1]))
428 return absl::nullopt;
429 }
430
431 if (XYBothNotEqual(points[0], points[3]))
432 return absl::nullopt;
433
434 return CreateRectFromPoints(points[0], points[2]);
435}
436
437CFX_RetainablePath::CFX_RetainablePath() = default;
438
439// Note: can't default the copy constructor since Retainable<> has a deleted
440// copy constructor (as it should). Instead, we want the default Retainable<>
441// constructor to be invoked so as to create a copy with a ref-count of 1 as
442// of the time it is created, then populate the remainder of the members from
443// the |src| object.
444CFX_RetainablePath::CFX_RetainablePath(const CFX_RetainablePath& src)
445 : CFX_Path(src) {}
446
447CFX_RetainablePath::~CFX_RetainablePath() = default;
448
449RetainPtr<CFX_RetainablePath> CFX_RetainablePath::Clone() const {
450 return pdfium::MakeRetain<CFX_RetainablePath>(*this);
451}
constexpr CFX_FloatRect(float l, float b, float r, float t)
constexpr CFX_FloatRect()=default
void UpdateRect(const CFX_PointF &point)
CFX_PointF Transform(const CFX_PointF &point) const
Point(const CFX_PointF &point, Type type, bool close)
Definition cfx_path.cpp:250
bool m_CloseFigure
Definition cfx_path.h:35
Point(const Point &other)
CFX_FloatRect GetBoundingBox() const
Definition cfx_path.cpp:322
void Transform(const CFX_Matrix &matrix)
Definition cfx_path.cpp:392
void Clear()
Definition cfx_path.cpp:265
void AppendRect(float left, float bottom, float right, float top)
Definition cfx_path.cpp:309
CFX_Path(CFX_Path &&src) noexcept
void Append(const CFX_Path &src, const CFX_Matrix *matrix)
Definition cfx_path.cpp:275
absl::optional< CFX_FloatRect > GetRect(const CFX_Matrix *matrix) const
Definition cfx_path.cpp:403
CFX_FloatRect GetBoundingBoxForStrokePath(float line_width, float miter_limit) const
Definition cfx_path.cpp:332
void AppendFloatRect(const CFX_FloatRect &rect)
Definition cfx_path.cpp:305
void AppendPointAndClose(const CFX_PointF &point, Point::Type type)
Definition cfx_path.cpp:293
void AppendLine(const CFX_PointF &pt1, const CFX_PointF &pt2)
Definition cfx_path.cpp:297
CFX_Path(const CFX_Path &src)
void ClosePath()
Definition cfx_path.cpp:269
void AppendPoint(const CFX_PointF &point, Point::Type type)
Definition cfx_path.cpp:289
bool IsRect() const
Definition cfx_path.cpp:397
~CFX_RetainablePath() override
RetainPtr< CFX_RetainablePath > Clone() const
Definition cfx_path.cpp:449