1 /*
2 * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "third_party/blink/renderer/platform/graphics/path_traversal_state.h"
21
22 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
23 #include "third_party/blink/renderer/platform/wtf/vector.h"
24
25 namespace blink {
26
MidPoint(const FloatPoint & first,const FloatPoint & second)27 static inline FloatPoint MidPoint(const FloatPoint& first,
28 const FloatPoint& second) {
29 return FloatPoint((first.X() + second.X()) / 2.0f,
30 (first.Y() + second.Y()) / 2.0f);
31 }
32
DistanceLine(const FloatPoint & start,const FloatPoint & end)33 static inline float DistanceLine(const FloatPoint& start,
34 const FloatPoint& end) {
35 return sqrtf((end.X() - start.X()) * (end.X() - start.X()) +
36 (end.Y() - start.Y()) * (end.Y() - start.Y()));
37 }
38
39 struct QuadraticBezier {
40 DISALLOW_NEW();
41 QuadraticBezier() = default;
QuadraticBezierblink::QuadraticBezier42 QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint& e)
43 : start(s), control(c), end(e), split_depth(0) {}
44
MagnitudeSquaredblink::QuadraticBezier45 double MagnitudeSquared() const {
46 return ((double)(start.Dot(start)) + (double)(control.Dot(control)) +
47 (double)(end.Dot(end))) /
48 9.0;
49 }
50
ApproximateDistanceblink::QuadraticBezier51 float ApproximateDistance() const {
52 return DistanceLine(start, control) + DistanceLine(control, end);
53 }
54
Splitblink::QuadraticBezier55 void Split(QuadraticBezier& left, QuadraticBezier& right) const {
56 left.control = MidPoint(start, control);
57 right.control = MidPoint(control, end);
58
59 FloatPoint left_control_to_right_control =
60 MidPoint(left.control, right.control);
61 left.end = left_control_to_right_control;
62 right.start = left_control_to_right_control;
63
64 left.start = start;
65 right.end = end;
66
67 left.split_depth = right.split_depth = split_depth + 1;
68 }
69
70 FloatPoint start;
71 FloatPoint control;
72 FloatPoint end;
73 uint16_t split_depth;
74 };
75
76 struct CubicBezier {
77 DISALLOW_NEW();
78 CubicBezier() = default;
CubicBezierblink::CubicBezier79 CubicBezier(const FloatPoint& s,
80 const FloatPoint& c1,
81 const FloatPoint& c2,
82 const FloatPoint& e)
83 : start(s), control1(c1), control2(c2), end(e), split_depth(0) {}
84
MagnitudeSquaredblink::CubicBezier85 double MagnitudeSquared() const {
86 return ((double)(start.Dot(start)) + (double)(control1.Dot(control1)) +
87 (double)(control2.Dot(control2)) + (double)(end.Dot(end))) /
88 16.0;
89 }
90
ApproximateDistanceblink::CubicBezier91 float ApproximateDistance() const {
92 return DistanceLine(start, control1) + DistanceLine(control1, control2) +
93 DistanceLine(control2, end);
94 }
95
Splitblink::CubicBezier96 void Split(CubicBezier& left, CubicBezier& right) const {
97 FloatPoint start_to_control1 = MidPoint(control1, control2);
98
99 left.start = start;
100 left.control1 = MidPoint(start, control1);
101 left.control2 = MidPoint(left.control1, start_to_control1);
102
103 right.control2 = MidPoint(control2, end);
104 right.control1 = MidPoint(right.control2, start_to_control1);
105 right.end = end;
106
107 FloatPoint left_control2_to_right_control1 =
108 MidPoint(left.control2, right.control1);
109 left.end = left_control2_to_right_control1;
110 right.start = left_control2_to_right_control1;
111
112 left.split_depth = right.split_depth = split_depth + 1;
113 }
114
115 FloatPoint start;
116 FloatPoint control1;
117 FloatPoint control2;
118 FloatPoint end;
119 uint16_t split_depth;
120 };
121
122 template <class CurveType>
CurveLength(PathTraversalState & traversal_state,CurveType curve)123 static float CurveLength(PathTraversalState& traversal_state, CurveType curve) {
124 static const uint16_t kCurveSplitDepthLimit = 20;
125 static const double kPathSegmentLengthToleranceSquared = 1.e-16;
126
127 double curve_scale_for_tolerance_squared = curve.MagnitudeSquared();
128 if (curve_scale_for_tolerance_squared < kPathSegmentLengthToleranceSquared)
129 return 0;
130
131 Vector<CurveType> curve_stack;
132 curve_stack.push_back(curve);
133
134 float total_length = 0;
135 do {
136 float length = curve.ApproximateDistance();
137 double length_discrepancy = length - DistanceLine(curve.start, curve.end);
138 if ((length_discrepancy * length_discrepancy) /
139 curve_scale_for_tolerance_squared >
140 kPathSegmentLengthToleranceSquared &&
141 curve.split_depth < kCurveSplitDepthLimit) {
142 CurveType left_curve;
143 CurveType right_curve;
144 curve.Split(left_curve, right_curve);
145 curve = left_curve;
146 curve_stack.push_back(right_curve);
147 } else {
148 total_length += length;
149 if (traversal_state.action_ ==
150 PathTraversalState::kTraversalPointAtLength ||
151 traversal_state.action_ ==
152 PathTraversalState::kTraversalNormalAngleAtLength) {
153 traversal_state.previous_ = curve.start;
154 traversal_state.current_ = curve.end;
155 if (traversal_state.total_length_ + total_length >
156 traversal_state.desired_length_)
157 return total_length;
158 }
159 curve = curve_stack.back();
160 curve_stack.pop_back();
161 }
162 } while (!curve_stack.IsEmpty());
163
164 return total_length;
165 }
166
PathTraversalState(PathTraversalAction action)167 PathTraversalState::PathTraversalState(PathTraversalAction action)
168 : action_(action),
169 success_(false),
170 total_length_(0),
171 desired_length_(0),
172 normal_angle_(0) {}
173
CloseSubpath()174 float PathTraversalState::CloseSubpath() {
175 float distance = DistanceLine(current_, start_);
176 current_ = start_;
177 return distance;
178 }
179
MoveTo(const FloatPoint & point)180 float PathTraversalState::MoveTo(const FloatPoint& point) {
181 current_ = start_ = point;
182 return 0;
183 }
184
LineTo(const FloatPoint & point)185 float PathTraversalState::LineTo(const FloatPoint& point) {
186 float distance = DistanceLine(current_, point);
187 current_ = point;
188 return distance;
189 }
190
CubicBezierTo(const FloatPoint & new_control1,const FloatPoint & new_control2,const FloatPoint & new_end)191 float PathTraversalState::CubicBezierTo(const FloatPoint& new_control1,
192 const FloatPoint& new_control2,
193 const FloatPoint& new_end) {
194 float distance = CurveLength<CubicBezier>(
195 *this, CubicBezier(current_, new_control1, new_control2, new_end));
196
197 if (action_ != kTraversalPointAtLength &&
198 action_ != kTraversalNormalAngleAtLength)
199 current_ = new_end;
200
201 return distance;
202 }
203
ProcessSegment()204 void PathTraversalState::ProcessSegment() {
205 if ((action_ == kTraversalPointAtLength ||
206 action_ == kTraversalNormalAngleAtLength) &&
207 total_length_ >= desired_length_) {
208 float slope = FloatPoint(current_ - previous_).SlopeAngleRadians();
209 if (action_ == kTraversalPointAtLength) {
210 float offset = desired_length_ - total_length_;
211 current_.Move(offset * cosf(slope), offset * sinf(slope));
212 } else {
213 normal_angle_ = rad2deg(slope);
214 }
215 success_ = true;
216 }
217 previous_ = current_;
218 }
219
220 } // namespace blink
221