| 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 |