changeset 9259:aa51df7e1d41

8090035: JavaFX browser can get stuck in an infinite loop when calling path.getTotalLength() Reviewed-by: kcr, vadim, azvegint Contributed-by: guru.hb@oracle.com
author azvegint
date Fri, 18 Sep 2015 16:16:07 +0300
parents d015af1e73c5
children 705c90b4d54d
files modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.cpp modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.h
diffstat 2 files changed, 29 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.cpp	Fri Sep 18 09:09:22 2015 -0400
+++ b/modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.cpp	Fri Sep 18 16:16:07 2015 +0300
@@ -25,7 +25,6 @@
 
 namespace WebCore {
 
-static const float kPathSegmentLengthTolerance = 0.00001f;
 
 static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& second)
 {
@@ -43,9 +42,15 @@
         : 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
     {
         return distanceLine(start, control) + distanceLine(control, end);
@@ -62,11 +67,14 @@
 
         left.start = start;
         right.end = end;
+
+        left.splitDepth = right.splitDepth = splitDepth + 1;
     }
     
     FloatPoint start;
     FloatPoint control;
     FloatPoint end;
+    unsigned short splitDepth;
 };
 
 struct CubicBezier {
@@ -76,9 +84,15 @@
         , 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
     {
         return distanceLine(start, control1) + distanceLine(control1, control2) + distanceLine(control2, end);
@@ -99,24 +113,26 @@
         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 +140,8 @@
     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);
@@ -160,20 +177,20 @@
 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;
 }
 
@@ -181,9 +198,6 @@
 {
     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 +207,6 @@
 float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
 {
     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;
--- a/modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.h	Fri Sep 18 09:09:22 2015 -0400
+++ b/modules/web/src/main/native/Source/WebCore/platform/graphics/PathTraversalState.h	Fri Sep 18 16:16:07 2015 +0300
@@ -55,8 +55,6 @@
 
     FloatPoint m_current;
     FloatPoint m_start;
-    FloatPoint m_control1;
-    FloatPoint m_control2;
 
     float m_totalLength;
     unsigned m_segmentIndex;