Index: Source/platform/graphics/PathTraversalState.cpp |
diff --git a/Source/platform/graphics/PathTraversalState.cpp b/Source/platform/graphics/PathTraversalState.cpp |
index 406c5b180cd6a38866b28e5882f423e23e7270ef..862ae8686e0e420da0a389d2827244a0220b0e3d 100644 |
--- a/Source/platform/graphics/PathTraversalState.cpp |
+++ b/Source/platform/graphics/PathTraversalState.cpp |
@@ -25,8 +25,6 @@ |
namespace WebCore { |
-static const float kPathSegmentLengthTolerance = 0.00001f; |
- |
static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& second) |
{ |
return FloatPoint((first.x() + second.x()) / 2.0f, (first.y() + second.y()) / 2.0f); |
@@ -43,7 +41,13 @@ struct QuadraticBezier { |
: start(s) |
, control(c) |
, end(e) |
+ , splitDepth(0) |
+ { |
+ } |
+ |
+ double magnitudeSquared() const |
{ |
+ return ((double)(start.dot(start)) + (double)(control.dot(control)) + (double)(end.dot(end))) / 9.0; |
} |
float approximateDistance() const |
@@ -62,11 +66,14 @@ struct QuadraticBezier { |
left.start = start; |
right.end = end; |
+ |
+ left.splitDepth = right.splitDepth = splitDepth + 1; |
} |
FloatPoint start; |
FloatPoint control; |
FloatPoint end; |
+ unsigned short splitDepth; |
}; |
struct CubicBezier { |
@@ -76,7 +83,13 @@ struct CubicBezier { |
, control1(c1) |
, control2(c2) |
, end(e) |
+ , splitDepth(0) |
+ { |
+ } |
+ |
+ double magnitudeSquared() const |
{ |
+ return ((double)(start.dot(start)) + (double)(control1.dot(control1)) + (double)(control2.dot(control2)) + (double)(end.dot(end))) / 16.0; |
} |
float approximateDistance() const |
@@ -99,24 +112,26 @@ struct CubicBezier { |
FloatPoint leftControl2ToRightControl1 = midPoint(left.control2, right.control1); |
left.end = leftControl2ToRightControl1; |
right.start = leftControl2ToRightControl1; |
+ |
+ left.splitDepth = right.splitDepth = splitDepth + 1; |
} |
FloatPoint start; |
FloatPoint control1; |
FloatPoint control2; |
FloatPoint end; |
+ unsigned short splitDepth; |
}; |
-// FIXME: This function is possibly very slow due to the ifs required for proper path measuring |
-// A simple speed-up would be to use an additional boolean template parameter to control whether |
-// to use the "fast" version of this function with no PathTraversalState updating, vs. the slow |
-// version which does update the PathTraversalState. We'll have to shark it to see if that's necessary. |
-// Another check which is possible up-front (to send us down the fast path) would be to check if |
-// approximateDistance() + current total distance > desired distance |
template<class CurveType> |
static float curveLength(PathTraversalState& traversalState, CurveType curve) |
{ |
- static const unsigned curveStackDepthLimit = 20; |
+ static const unsigned short curveSplitDepthLimit = 20; |
+ static const double pathSegmentLengthToleranceSquared = 1.e-16; |
+ |
+ double curveScaleForToleranceSquared = curve.magnitudeSquared(); |
+ if (curveScaleForToleranceSquared < pathSegmentLengthToleranceSquared) |
+ return 0; |
Vector<CurveType> curveStack; |
curveStack.append(curve); |
@@ -124,7 +139,8 @@ static float curveLength(PathTraversalState& traversalState, CurveType curve) |
float totalLength = 0; |
do { |
float length = curve.approximateDistance(); |
- if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() <= curveStackDepthLimit) { |
+ double lengthDiscrepancy = length - distanceLine(curve.start, curve.end); |
+ if ((lengthDiscrepancy * lengthDiscrepancy) / curveScaleForToleranceSquared > pathSegmentLengthToleranceSquared && curve.splitDepth < curveSplitDepthLimit) { |
CurveType leftCurve; |
CurveType rightCurve; |
curve.split(leftCurve, rightCurve); |
@@ -159,20 +175,20 @@ PathTraversalState::PathTraversalState(PathTraversalAction action) |
float PathTraversalState::closeSubpath() |
{ |
float distance = distanceLine(m_current, m_start); |
- m_current = m_control1 = m_control2 = m_start; |
+ m_current = m_start; |
return distance; |
} |
float PathTraversalState::moveTo(const FloatPoint& point) |
{ |
- m_current = m_start = m_control1 = m_control2 = point; |
+ m_current = m_start = point; |
return 0; |
} |
float PathTraversalState::lineTo(const FloatPoint& point) |
{ |
float distance = distanceLine(m_current, point); |
- m_current = m_control1 = m_control2 = point; |
+ m_current = point; |
return distance; |
} |
@@ -180,9 +196,6 @@ float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const |
{ |
float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd)); |
- m_control1 = newControl; |
- m_control2 = newEnd; |
- |
if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) |
m_current = newEnd; |
@@ -193,9 +206,6 @@ float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const Flo |
{ |
float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd)); |
- m_control1 = newEnd; |
- m_control2 = newControl2; |
- |
if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) |
m_current = newEnd; |