OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> | 2 * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> |
3 * | 3 * |
4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
8 * | 8 * |
9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 */ | 18 */ |
19 | 19 |
20 #include "config.h" | 20 #include "config.h" |
21 #include "platform/graphics/PathTraversalState.h" | 21 #include "platform/graphics/PathTraversalState.h" |
22 | 22 |
23 #include "wtf/MathExtras.h" | 23 #include "wtf/MathExtras.h" |
24 #include "wtf/Vector.h" | 24 #include "wtf/Vector.h" |
25 | 25 |
26 namespace WebCore { | 26 namespace WebCore { |
27 | 27 |
28 static const float kPathSegmentLengthTolerance = 0.00001f; | |
29 | |
30 static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& sec
ond) | 28 static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& sec
ond) |
31 { | 29 { |
32 return FloatPoint((first.x() + second.x()) / 2.0f, (first.y() + second.y())
/ 2.0f); | 30 return FloatPoint((first.x() + second.x()) / 2.0f, (first.y() + second.y())
/ 2.0f); |
33 } | 31 } |
34 | 32 |
35 static inline float distanceLine(const FloatPoint& start, const FloatPoint& end) | 33 static inline float distanceLine(const FloatPoint& start, const FloatPoint& end) |
36 { | 34 { |
37 return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - star
t.y()) * (end.y() - start.y())); | 35 return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - star
t.y()) * (end.y() - start.y())); |
38 } | 36 } |
39 | 37 |
40 struct QuadraticBezier { | 38 struct QuadraticBezier { |
41 QuadraticBezier() { } | 39 QuadraticBezier() { } |
42 QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint&
e) | 40 QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint&
e) |
43 : start(s) | 41 : start(s) |
44 , control(c) | 42 , control(c) |
45 , end(e) | 43 , end(e) |
| 44 , splitDepth(0) |
46 { | 45 { |
47 } | 46 } |
48 | 47 |
| 48 double magnitudeSquared() const |
| 49 { |
| 50 return ((double)(start.dot(start)) + (double)(control.dot(control)) + (d
ouble)(end.dot(end))) / 9.0; |
| 51 } |
| 52 |
49 float approximateDistance() const | 53 float approximateDistance() const |
50 { | 54 { |
51 return distanceLine(start, control) + distanceLine(control, end); | 55 return distanceLine(start, control) + distanceLine(control, end); |
52 } | 56 } |
53 | 57 |
54 void split(QuadraticBezier& left, QuadraticBezier& right) const | 58 void split(QuadraticBezier& left, QuadraticBezier& right) const |
55 { | 59 { |
56 left.control = midPoint(start, control); | 60 left.control = midPoint(start, control); |
57 right.control = midPoint(control, end); | 61 right.control = midPoint(control, end); |
58 | 62 |
59 FloatPoint leftControlToRightControl = midPoint(left.control, right.cont
rol); | 63 FloatPoint leftControlToRightControl = midPoint(left.control, right.cont
rol); |
60 left.end = leftControlToRightControl; | 64 left.end = leftControlToRightControl; |
61 right.start = leftControlToRightControl; | 65 right.start = leftControlToRightControl; |
62 | 66 |
63 left.start = start; | 67 left.start = start; |
64 right.end = end; | 68 right.end = end; |
| 69 |
| 70 left.splitDepth = right.splitDepth = splitDepth + 1; |
65 } | 71 } |
66 | 72 |
67 FloatPoint start; | 73 FloatPoint start; |
68 FloatPoint control; | 74 FloatPoint control; |
69 FloatPoint end; | 75 FloatPoint end; |
| 76 unsigned short splitDepth; |
70 }; | 77 }; |
71 | 78 |
72 struct CubicBezier { | 79 struct CubicBezier { |
73 CubicBezier() { } | 80 CubicBezier() { } |
74 CubicBezier(const FloatPoint& s, const FloatPoint& c1, const FloatPoint& c2,
const FloatPoint& e) | 81 CubicBezier(const FloatPoint& s, const FloatPoint& c1, const FloatPoint& c2,
const FloatPoint& e) |
75 : start(s) | 82 : start(s) |
76 , control1(c1) | 83 , control1(c1) |
77 , control2(c2) | 84 , control2(c2) |
78 , end(e) | 85 , end(e) |
| 86 , splitDepth(0) |
79 { | 87 { |
80 } | 88 } |
81 | 89 |
| 90 double magnitudeSquared() const |
| 91 { |
| 92 return ((double)(start.dot(start)) + (double)(control1.dot(control1)) +
(double)(control2.dot(control2)) + (double)(end.dot(end))) / 16.0; |
| 93 } |
| 94 |
82 float approximateDistance() const | 95 float approximateDistance() const |
83 { | 96 { |
84 return distanceLine(start, control1) + distanceLine(control1, control2)
+ distanceLine(control2, end); | 97 return distanceLine(start, control1) + distanceLine(control1, control2)
+ distanceLine(control2, end); |
85 } | 98 } |
86 | 99 |
87 void split(CubicBezier& left, CubicBezier& right) const | 100 void split(CubicBezier& left, CubicBezier& right) const |
88 { | 101 { |
89 FloatPoint startToControl1 = midPoint(control1, control2); | 102 FloatPoint startToControl1 = midPoint(control1, control2); |
90 | 103 |
91 left.start = start; | 104 left.start = start; |
92 left.control1 = midPoint(start, control1); | 105 left.control1 = midPoint(start, control1); |
93 left.control2 = midPoint(left.control1, startToControl1); | 106 left.control2 = midPoint(left.control1, startToControl1); |
94 | 107 |
95 right.control2 = midPoint(control2, end); | 108 right.control2 = midPoint(control2, end); |
96 right.control1 = midPoint(right.control2, startToControl1); | 109 right.control1 = midPoint(right.control2, startToControl1); |
97 right.end = end; | 110 right.end = end; |
98 | 111 |
99 FloatPoint leftControl2ToRightControl1 = midPoint(left.control2, right.c
ontrol1); | 112 FloatPoint leftControl2ToRightControl1 = midPoint(left.control2, right.c
ontrol1); |
100 left.end = leftControl2ToRightControl1; | 113 left.end = leftControl2ToRightControl1; |
101 right.start = leftControl2ToRightControl1; | 114 right.start = leftControl2ToRightControl1; |
| 115 |
| 116 left.splitDepth = right.splitDepth = splitDepth + 1; |
102 } | 117 } |
103 | 118 |
104 FloatPoint start; | 119 FloatPoint start; |
105 FloatPoint control1; | 120 FloatPoint control1; |
106 FloatPoint control2; | 121 FloatPoint control2; |
107 FloatPoint end; | 122 FloatPoint end; |
| 123 unsigned short splitDepth; |
108 }; | 124 }; |
109 | 125 |
110 // FIXME: This function is possibly very slow due to the ifs required for proper
path measuring | |
111 // A simple speed-up would be to use an additional boolean template parameter to
control whether | |
112 // to use the "fast" version of this function with no PathTraversalState updatin
g, vs. the slow | |
113 // version which does update the PathTraversalState. We'll have to shark it to s
ee if that's necessary. | |
114 // Another check which is possible up-front (to send us down the fast path) woul
d be to check if | |
115 // approximateDistance() + current total distance > desired distance | |
116 template<class CurveType> | 126 template<class CurveType> |
117 static float curveLength(PathTraversalState& traversalState, CurveType curve) | 127 static float curveLength(PathTraversalState& traversalState, CurveType curve) |
118 { | 128 { |
119 static const unsigned curveStackDepthLimit = 20; | 129 static const unsigned short curveSplitDepthLimit = 20; |
| 130 static const double pathSegmentLengthToleranceSquared = 1.e-16; |
| 131 |
| 132 double curveScaleForToleranceSquared = curve.magnitudeSquared(); |
| 133 if (curveScaleForToleranceSquared < pathSegmentLengthToleranceSquared) |
| 134 return 0; |
120 | 135 |
121 Vector<CurveType> curveStack; | 136 Vector<CurveType> curveStack; |
122 curveStack.append(curve); | 137 curveStack.append(curve); |
123 | 138 |
124 float totalLength = 0; | 139 float totalLength = 0; |
125 do { | 140 do { |
126 float length = curve.approximateDistance(); | 141 float length = curve.approximateDistance(); |
127 if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLength
Tolerance && curveStack.size() <= curveStackDepthLimit) { | 142 double lengthDiscrepancy = length - distanceLine(curve.start, curve.end)
; |
| 143 if ((lengthDiscrepancy * lengthDiscrepancy) / curveScaleForToleranceSqua
red > pathSegmentLengthToleranceSquared && curve.splitDepth < curveSplitDepthLim
it) { |
128 CurveType leftCurve; | 144 CurveType leftCurve; |
129 CurveType rightCurve; | 145 CurveType rightCurve; |
130 curve.split(leftCurve, rightCurve); | 146 curve.split(leftCurve, rightCurve); |
131 curve = leftCurve; | 147 curve = leftCurve; |
132 curveStack.append(rightCurve); | 148 curveStack.append(rightCurve); |
133 } else { | 149 } else { |
134 totalLength += length; | 150 totalLength += length; |
135 if (traversalState.m_action == PathTraversalState::TraversalPointAtL
ength || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLe
ngth) { | 151 if (traversalState.m_action == PathTraversalState::TraversalPointAtL
ength || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLe
ngth) { |
136 traversalState.m_previous = curve.start; | 152 traversalState.m_previous = curve.start; |
137 traversalState.m_current = curve.end; | 153 traversalState.m_current = curve.end; |
(...skipping 14 matching lines...) Expand all Loading... |
152 , m_totalLength(0) | 168 , m_totalLength(0) |
153 , m_segmentIndex(0) | 169 , m_segmentIndex(0) |
154 , m_desiredLength(0) | 170 , m_desiredLength(0) |
155 , m_normalAngle(0) | 171 , m_normalAngle(0) |
156 { | 172 { |
157 } | 173 } |
158 | 174 |
159 float PathTraversalState::closeSubpath() | 175 float PathTraversalState::closeSubpath() |
160 { | 176 { |
161 float distance = distanceLine(m_current, m_start); | 177 float distance = distanceLine(m_current, m_start); |
162 m_current = m_control1 = m_control2 = m_start; | 178 m_current = m_start; |
163 return distance; | 179 return distance; |
164 } | 180 } |
165 | 181 |
166 float PathTraversalState::moveTo(const FloatPoint& point) | 182 float PathTraversalState::moveTo(const FloatPoint& point) |
167 { | 183 { |
168 m_current = m_start = m_control1 = m_control2 = point; | 184 m_current = m_start = point; |
169 return 0; | 185 return 0; |
170 } | 186 } |
171 | 187 |
172 float PathTraversalState::lineTo(const FloatPoint& point) | 188 float PathTraversalState::lineTo(const FloatPoint& point) |
173 { | 189 { |
174 float distance = distanceLine(m_current, point); | 190 float distance = distanceLine(m_current, point); |
175 m_current = m_control1 = m_control2 = point; | 191 m_current = point; |
176 return distance; | 192 return distance; |
177 } | 193 } |
178 | 194 |
179 float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const
FloatPoint& newEnd) | 195 float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const
FloatPoint& newEnd) |
180 { | 196 { |
181 float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_curre
nt, newControl, newEnd)); | 197 float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_curre
nt, newControl, newEnd)); |
182 | 198 |
183 m_control1 = newControl; | |
184 m_control2 = newEnd; | |
185 | |
186 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAt
Length) | 199 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAt
Length) |
187 m_current = newEnd; | 200 m_current = newEnd; |
188 | 201 |
189 return distance; | 202 return distance; |
190 } | 203 } |
191 | 204 |
192 float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const Flo
atPoint& newControl2, const FloatPoint& newEnd) | 205 float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const Flo
atPoint& newControl2, const FloatPoint& newEnd) |
193 { | 206 { |
194 float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newC
ontrol1, newControl2, newEnd)); | 207 float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newC
ontrol1, newControl2, newEnd)); |
195 | 208 |
196 m_control1 = newEnd; | |
197 m_control2 = newControl2; | |
198 | |
199 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAt
Length) | 209 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAt
Length) |
200 m_current = newEnd; | 210 m_current = newEnd; |
201 | 211 |
202 return distance; | 212 return distance; |
203 } | 213 } |
204 | 214 |
205 void PathTraversalState::processSegment() | 215 void PathTraversalState::processSegment() |
206 { | 216 { |
207 if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength
) | 217 if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength
) |
208 m_success = true; | 218 m_success = true; |
209 | 219 |
210 if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleA
tLength) && m_totalLength >= m_desiredLength) { | 220 if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleA
tLength) && m_totalLength >= m_desiredLength) { |
211 float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); | 221 float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); |
212 if (m_action == TraversalPointAtLength) { | 222 if (m_action == TraversalPointAtLength) { |
213 float offset = m_desiredLength - m_totalLength; | 223 float offset = m_desiredLength - m_totalLength; |
214 m_current.move(offset * cosf(slope), offset * sinf(slope)); | 224 m_current.move(offset * cosf(slope), offset * sinf(slope)); |
215 } else { | 225 } else { |
216 m_normalAngle = rad2deg(slope); | 226 m_normalAngle = rad2deg(slope); |
217 } | 227 } |
218 m_success = true; | 228 m_success = true; |
219 } | 229 } |
220 m_previous = m_current; | 230 m_previous = m_current; |
221 } | 231 } |
222 | 232 |
223 } | 233 } |
224 | 234 |
OLD | NEW |