Title: [182828] trunk
Revision
182828
Author
commit-qu...@webkit.org
Date
2015-04-14 18:34:25 -0700 (Tue, 14 Apr 2015)

Log Message

textPath layout performance improvement.
https://bugs.webkit.org/show_bug.cgi?id=141570.

Patch by Said Abou-Hallawa <sabouhall...@apple.com> on 2015-04-14
Reviewed by Darin Adler.

PerformanceTests:

Cut down the time spent in traversing the path for text by 50%. Instead
of traversing the path twice at a certain length: one time for the position
and the second time for the angle, we can merge these two passes into one.

* SVG/TextOnPathSimple.html: Added.
* SVG/resources/TextOnPathSimple.svg: Added.

Source/WebCore:

The bottleneck of the text-on-path performance is the position and angle
calculations for every single character. If the number of characters is
'n' and the number of path elements is 'm', the total number of processing
the path elements is O(2 x n x m). What makes it really worse is, for every
curve we keep splitting the curve till the split curve is almost a straight
line. The changes we need to do are:
1. Merge the position and the angle traversals in one pass since they are
   returning info for the same length on the path. There is a degenerate
   case for the starting point when calculating the angle. The original
   code was solving this problem by passing an epsilon instead of zero but
   because traversing the path for position and angle are now merged, we
   will pass zero for the starting point as is. All we need is to move one
   step ahead without moving the position. We need the extra step forward
   to calculate the slope of the path at the starting point.
2. We need to add a new mode to traversing a path. The new mode will take
   a vector of lengths and returns a vector of arrow vectors. Every arrow
   vector represents a position and an angle on the path at a certain length.
   This requires changing the SVGTextLayoutEngine to calculate the lengths
   of the characters on the curve first and then passing all of them to the
   path traversal function. Instead of traversing the path for every length,
   we are going to get the required point and angle from the vector of arrow
   vectors.

This patch is addressing the first fix only. The second one will require
refactoring the SVGTextLayoutEngine so I am going to address it in a
different patch.

* platform/graphics/Path.cpp:
(WebCore::pathLengthApplierFunction): It is cleaner to move the function
of this method to PathTraversalState::processPathElement().

(WebCore::Path::length): Use new enum Action value and access methods.

(WebCore::Path::traversalStateAtLength): New function which returns the
traversalState at a certain length on a path.

(WebCore::Path::pointAtLength):
(WebCore::Path::normalAngleAtLength): Use traversalStateAtLength() to get
the traversalState and from it return either the position or the angle.

* platform/graphics/Path.h: Define traversalStateAtLength().

* platform/graphics/PathTraversalState.cpp:
(WebCore::distanceLine): Code clean up.

(WebCore::curveLength): Make the setting of m_previous and m_current happens
only in this function.

(WebCore::PathTraversalState::PathTraversalState): Add an optional parameter
for the desired length and move the initialization of the other members to
the class definition.

(WebCore::PathTraversalState::closeSubpath):
(WebCore::PathTraversalState::moveTo):
(WebCore::PathTraversalState::lineTo): Add the distance to the m_totalLength
instead of returning it since this is what all the callers were doing.

(WebCore::PathTraversalState::quadraticBezierTo):
(WebCore::PathTraversalState::cubicBezierTo): Add the distance to the
m_totalLength. Move the setting of m_previous and m_current to curveLength().
Remove unused members m_control1 and m_control2.

(WebCore::PathTraversalState::processSegment): Deleted.
(WebCore::PathTraversalState::finalizeAppendPathElement): Create a new
name for the function. Handle the case of the angle at the starting point
where m_desiredLength is set to zero. The new flag m_isZeroVector will be
set to notify the caller that the  next iteration will be the last one and
it is only needed for the calculating the angle of a zero vector. m_current
should not change by this last iteration.

(WebCore::PathTraversalState::appendPathElement): This code is moved from
pathLengthApplierFunction().

(WebCore::PathTraversalState::processPathElement): This function is used
by the class Path. It is a wrapper for appendPathElement(). If m_isZeroVector
is set we append the new element to a copy for the PathTraversalState just
to get the angle for the zero vector.

* platform/graphics/PathTraversalState.h: Change the enum values to not
not include the class or the enum class. Make the data members private and
expose the needed ones through access methods. Make all the internal methods
to be private.

(WebCore::PathTraversalState::processPathElement):  Another wrapper for
appendPathElement() which is used by SVGPathTraversalStateBuilder.

(WebCore::PathTraversalState::action):
(WebCore::PathTraversalState::setAction):
(WebCore::PathTraversalState::desiredLength):
(WebCore::PathTraversalState::setDesiredLength):
(WebCore::PathTraversalState::success):
(WebCore::PathTraversalState::totalLength):
(WebCore::PathTraversalState::current):
(WebCore::PathTraversalState::normalAngle): New access methods which are now
needed after making the data members private.

* rendering/svg/SVGRootInlineBox.cpp:
(WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes): Make the casting
of the renderer on the caller side.

* rendering/svg/SVGTextChunk.cpp:
(WebCore::SVGTextChunk::SVGTextChunk): The constructor should append the
elements of m_boxes instead of making this from outside the class.

(WebCore::SVGTextChunk::totalCharacters):
(WebCore::SVGTextChunk::totalLength):
(WebCore::SVGTextChunk::calculateLength): Deleted.
Replace calculateLength() by totalCharacters() and totalLength() to make
the interface cleaner.

(WebCore::SVGTextChunk::totalAnchorShift):
(WebCore::SVGTextChunk::calculateTextAnchorShift): Deleted.
Rename the function name.

(WebCore::SVGTextChunk::layout):
(WebCore::SVGTextChunk::processTextLengthSpacingCorrection):
(WebCore::SVGTextChunk::buildBoxTransformations):
(WebCore::SVGTextChunk::boxSpacingAndGlyphsTransform):
(WebCore::SVGTextChunk::processTextAnchorCorrection):
Move the chunk layout code from SVGTextChunkBuilder::layoutTextChunks()
to the SVGTextChunk::layout(). Move all the helper functions as well.

* rendering/svg/SVGTextChunk.h:
(WebCore::SVGTextChunk::hasTextAnchor):
(WebCore::SVGTextChunk::boxes): Deleted.
Add the new methods and change most of the public methods to be private.

* rendering/svg/SVGTextChunkBuilder.cpp:
(WebCore::SVGTextChunkBuilder::totalCharacters):
(WebCore::SVGTextChunkBuilder::totalLength):
(WebCore::SVGTextChunkBuilder::totalAnchorShift): This code is moved from
SVGTextLayoutEngine. It scans the boxes stored in the SVGTextChunkBuilder
and sums up the total values.

(WebCore::SVGTextChunkBuilder::transformationForTextBox):
(WebCore::SVGTextChunkBuilder::buildTextChunks):
(WebCore::SVGTextChunkBuilder::layoutTextChunks): Code clean up.

(WebCore::SVGTextChunkBuilder::addTextChunk): Deleted.
(WebCore::SVGTextChunkBuilder::processTextChunk): Deleted.
(WebCore::SVGTextChunkBuilder::processTextLengthSpacingCorrection): Deleted.
(WebCore::SVGTextChunkBuilder::processTextAnchorCorrection): Deleted.
(WebCore::SVGTextChunkBuilder::buildSpacingAndGlyphsTransform): Deleted.
This code now lives in SVGTextChunk.

* rendering/svg/SVGTextChunkBuilder.h: Add new methods for code which was
moved from SVGTextLayoutEngine and remove methods for code which was removed
to SVGTextChunk.

* rendering/svg/SVGTextLayoutEngine.cpp:
(WebCore::SVGTextLayoutEngine::beginTextPathLayout): Use the sum up methods
from SVGTextChunkBuilder instead of looping through the chunks. Also get a
clean order for defining variables and doing the calculations.

(WebCore::SVGTextLayoutEngine::finalizeTransformMatrices): Code clean up.

(WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath): Do a single path
traversal to get the position and the angle for a length on a path.

* svg/SVGAnimateMotionElement.cpp:
(WebCore::SVGAnimateMotionElement::buildTransformForProgress): Do a single
path traversal to get the position and the angle at a length on a path.

* svg/SVGPathTraversalStateBuilder.cpp:
(WebCore::SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder):
(WebCore::SVGPathTraversalStateBuilder::moveTo):
(WebCore::SVGPathTraversalStateBuilder::lineTo):
(WebCore::SVGPathTraversalStateBuilder::curveToCubic):
(WebCore::SVGPathTraversalStateBuilder::closePath):
(WebCore::SVGPathTraversalStateBuilder::setDesiredLength):
(WebCore::SVGPathTraversalStateBuilder::continueConsuming):
(WebCore::SVGPathTraversalStateBuilder::totalLength):
(WebCore::SVGPathTraversalStateBuilder::currentPoint):
(WebCore::SVGPathTraversalStateBuilder::incrementPathSegmentCount): Deleted.
(WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): Deleted.
* svg/SVGPathTraversalStateBuilder.h:
(WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex):
Code clean up.

* svg/SVGPathUtilities.cpp:
(WebCore::getSVGPathSegAtLengthFromSVGPathByteStream):
(WebCore::getTotalLengthOfSVGPathByteStream):
(WebCore::getPointAtLengthOfSVGPathByteStream): Use new TraversalState::Action
enum values.

Modified Paths

Added Paths

Diff

Modified: trunk/PerformanceTests/ChangeLog (182827 => 182828)


--- trunk/PerformanceTests/ChangeLog	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/PerformanceTests/ChangeLog	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,3 +1,17 @@
+2015-04-14  Said Abou-Hallawa  <sabouhall...@apple.com>
+
+        textPath layout performance improvement.
+        https://bugs.webkit.org/show_bug.cgi?id=141570.
+
+        Reviewed by Darin Adler.
+
+        Cut down the time spent in traversing the path for text by 50%. Instead
+        of traversing the path twice at a certain length: one time for the position
+        and the second time for the angle, we can merge these two passes into one.
+
+        * SVG/TextOnPathSimple.html: Added.
+        * SVG/resources/TextOnPathSimple.svg: Added.
+
 2015-04-13  Zalan Bujtas  <za...@apple.com>
 
         Clear up the test content when test is done.

Added: trunk/PerformanceTests/SVG/TextOnPathSimple.html (0 => 182828)


--- trunk/PerformanceTests/SVG/TextOnPathSimple.html	                        (rev 0)
+++ trunk/PerformanceTests/SVG/TextOnPathSimple.html	2015-04-15 01:34:25 UTC (rev 182828)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<body>
+<script src=""
+<script>
+  window._onload_ = function() {
+    PerfTestRunner.measurePageLoadTime({path: "resources/TextOnPathSimple.svg"});
+  }
+</script>
+</body>

Added: trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg (0 => 182828)


--- trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg	                        (rev 0)
+++ trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg	2015-04-15 01:34:25 UTC (rev 182828)
@@ -0,0 +1,42 @@
+<svg width="100%" height="100%" viewBox="0 0 4000 2500"
+     xmlns="http://www.w3.org/2000/svg" 
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <path id="SimplePath"
+      d="M 100  200 
+         C 200  100 300    0  400  100
+         C 500  200 600  300  700  200
+         C 800  100 900    0  1000 100
+         C 1100 200 1200 300  1300 200
+         C 1400 100 1500   0  1600 100
+         C 1700 200 1800 300  1900 200"/>
+  </defs>
+
+  <g id="TextOnPath25">
+    <g id="TextOnPath5">
+      <g id="TextOnPath">
+        <use xlink:href="" fill="none" stroke="red"/>
+        <text font-family="Verdana" font-size="42.5">
+          <textPath xlink:href=""
+            We go up, then we go down, then up again, then we go down, then we up again, then we go down.
+          </textPath>
+        </text>
+      </g>
+      <use xlink:href="" transform="translate(0,40)"/>
+      <use xlink:href="" transform="translate(0,80)"/>
+      <use xlink:href="" transform="translate(0,120)"/>
+      <use xlink:href="" transform="translate(0,160)"/>
+    </g>
+  
+    <use xlink:href="" transform="translate(0,200)"/>
+    <use xlink:href="" transform="translate(0,400)"/>
+    <use xlink:href="" transform="translate(0,600)"/>
+    <use xlink:href="" transform="translate(0,800)"/>
+  </g>
+
+  <use xlink:href="" transform="translate(2000,0)"/>
+  <use xlink:href="" transform="translate(0,1200)"/>
+  <use xlink:href="" transform="translate(2000,1200)"/>
+
+  <rect x="1" y="1" width="3999" height="2499" fill="none" stroke="black" stroke-width="2" />
+</svg>

Modified: trunk/Source/WebCore/ChangeLog (182827 => 182828)


--- trunk/Source/WebCore/ChangeLog	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/ChangeLog	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,3 +1,195 @@
+2015-04-14  Said Abou-Hallawa  <sabouhall...@apple.com>
+
+        textPath layout performance improvement.
+        https://bugs.webkit.org/show_bug.cgi?id=141570.
+
+        Reviewed by Darin Adler.
+
+        The bottleneck of the text-on-path performance is the position and angle
+        calculations for every single character. If the number of characters is
+        'n' and the number of path elements is 'm', the total number of processing
+        the path elements is O(2 x n x m). What makes it really worse is, for every
+        curve we keep splitting the curve till the split curve is almost a straight
+        line. The changes we need to do are:
+        1. Merge the position and the angle traversals in one pass since they are
+           returning info for the same length on the path. There is a degenerate
+           case for the starting point when calculating the angle. The original
+           code was solving this problem by passing an epsilon instead of zero but
+           because traversing the path for position and angle are now merged, we
+           will pass zero for the starting point as is. All we need is to move one
+           step ahead without moving the position. We need the extra step forward
+           to calculate the slope of the path at the starting point.
+        2. We need to add a new mode to traversing a path. The new mode will take
+           a vector of lengths and returns a vector of arrow vectors. Every arrow
+           vector represents a position and an angle on the path at a certain length.
+           This requires changing the SVGTextLayoutEngine to calculate the lengths
+           of the characters on the curve first and then passing all of them to the
+           path traversal function. Instead of traversing the path for every length,
+           we are going to get the required point and angle from the vector of arrow
+           vectors.
+
+        This patch is addressing the first fix only. The second one will require
+        refactoring the SVGTextLayoutEngine so I am going to address it in a
+        different patch.
+
+        * platform/graphics/Path.cpp:
+        (WebCore::pathLengthApplierFunction): It is cleaner to move the function
+        of this method to PathTraversalState::processPathElement().
+        
+        (WebCore::Path::length): Use new enum Action value and access methods.
+        
+        (WebCore::Path::traversalStateAtLength): New function which returns the
+        traversalState at a certain length on a path.
+        
+        (WebCore::Path::pointAtLength):
+        (WebCore::Path::normalAngleAtLength): Use traversalStateAtLength() to get
+        the traversalState and from it return either the position or the angle.
+        
+        * platform/graphics/Path.h: Define traversalStateAtLength().
+        
+        * platform/graphics/PathTraversalState.cpp:
+        (WebCore::distanceLine): Code clean up.
+        
+        (WebCore::curveLength): Make the setting of m_previous and m_current happens
+        only in this function.
+        
+        (WebCore::PathTraversalState::PathTraversalState): Add an optional parameter
+        for the desired length and move the initialization of the other members to
+        the class definition.
+        
+        (WebCore::PathTraversalState::closeSubpath):
+        (WebCore::PathTraversalState::moveTo):
+        (WebCore::PathTraversalState::lineTo): Add the distance to the m_totalLength
+        instead of returning it since this is what all the callers were doing.
+        
+        (WebCore::PathTraversalState::quadraticBezierTo):
+        (WebCore::PathTraversalState::cubicBezierTo): Add the distance to the
+        m_totalLength. Move the setting of m_previous and m_current to curveLength().
+        Remove unused members m_control1 and m_control2.
+
+        (WebCore::PathTraversalState::processSegment): Deleted.        
+        (WebCore::PathTraversalState::finalizeAppendPathElement): Create a new
+        name for the function. Handle the case of the angle at the starting point
+        where m_desiredLength is set to zero. The new flag m_isZeroVector will be
+        set to notify the caller that the  next iteration will be the last one and
+        it is only needed for the calculating the angle of a zero vector. m_current
+        should not change by this last iteration.
+        
+        (WebCore::PathTraversalState::appendPathElement): This code is moved from
+        pathLengthApplierFunction().
+        
+        (WebCore::PathTraversalState::processPathElement): This function is used
+        by the class Path. It is a wrapper for appendPathElement(). If m_isZeroVector
+        is set we append the new element to a copy for the PathTraversalState just
+        to get the angle for the zero vector.
+
+        * platform/graphics/PathTraversalState.h: Change the enum values to not
+        not include the class or the enum class. Make the data members private and
+        expose the needed ones through access methods. Make all the internal methods
+        to be private.
+        
+        (WebCore::PathTraversalState::processPathElement):  Another wrapper for
+        appendPathElement() which is used by SVGPathTraversalStateBuilder.
+        
+        (WebCore::PathTraversalState::action):
+        (WebCore::PathTraversalState::setAction):
+        (WebCore::PathTraversalState::desiredLength):
+        (WebCore::PathTraversalState::setDesiredLength):
+        (WebCore::PathTraversalState::success):
+        (WebCore::PathTraversalState::totalLength):
+        (WebCore::PathTraversalState::current):
+        (WebCore::PathTraversalState::normalAngle): New access methods which are now
+        needed after making the data members private.
+        
+        * rendering/svg/SVGRootInlineBox.cpp:
+        (WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes): Make the casting
+        of the renderer on the caller side.
+        
+        * rendering/svg/SVGTextChunk.cpp:
+        (WebCore::SVGTextChunk::SVGTextChunk): The constructor should append the
+        elements of m_boxes instead of making this from outside the class.
+        
+        (WebCore::SVGTextChunk::totalCharacters):
+        (WebCore::SVGTextChunk::totalLength):
+        (WebCore::SVGTextChunk::calculateLength): Deleted.
+        Replace calculateLength() by totalCharacters() and totalLength() to make
+        the interface cleaner.
+        
+        (WebCore::SVGTextChunk::totalAnchorShift):
+        (WebCore::SVGTextChunk::calculateTextAnchorShift): Deleted.
+        Rename the function name.
+        
+        (WebCore::SVGTextChunk::layout):
+        (WebCore::SVGTextChunk::processTextLengthSpacingCorrection):
+        (WebCore::SVGTextChunk::buildBoxTransformations):
+        (WebCore::SVGTextChunk::boxSpacingAndGlyphsTransform):
+        (WebCore::SVGTextChunk::processTextAnchorCorrection):
+        Move the chunk layout code from SVGTextChunkBuilder::layoutTextChunks()
+        to the SVGTextChunk::layout(). Move all the helper functions as well.
+        
+        * rendering/svg/SVGTextChunk.h:
+        (WebCore::SVGTextChunk::hasTextAnchor):
+        (WebCore::SVGTextChunk::boxes): Deleted.
+        Add the new methods and change most of the public methods to be private.
+        
+        * rendering/svg/SVGTextChunkBuilder.cpp:
+        (WebCore::SVGTextChunkBuilder::totalCharacters):
+        (WebCore::SVGTextChunkBuilder::totalLength):
+        (WebCore::SVGTextChunkBuilder::totalAnchorShift): This code is moved from
+        SVGTextLayoutEngine. It scans the boxes stored in the SVGTextChunkBuilder
+        and sums up the total values.
+        
+        (WebCore::SVGTextChunkBuilder::transformationForTextBox):
+        (WebCore::SVGTextChunkBuilder::buildTextChunks):
+        (WebCore::SVGTextChunkBuilder::layoutTextChunks): Code clean up.
+        
+        (WebCore::SVGTextChunkBuilder::addTextChunk): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextChunk): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextLengthSpacingCorrection): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextAnchorCorrection): Deleted.
+        (WebCore::SVGTextChunkBuilder::buildSpacingAndGlyphsTransform): Deleted.
+        This code now lives in SVGTextChunk.
+        
+        * rendering/svg/SVGTextChunkBuilder.h: Add new methods for code which was
+        moved from SVGTextLayoutEngine and remove methods for code which was removed
+        to SVGTextChunk.
+        
+        * rendering/svg/SVGTextLayoutEngine.cpp:
+        (WebCore::SVGTextLayoutEngine::beginTextPathLayout): Use the sum up methods
+        from SVGTextChunkBuilder instead of looping through the chunks. Also get a
+        clean order for defining variables and doing the calculations.
+        
+        (WebCore::SVGTextLayoutEngine::finalizeTransformMatrices): Code clean up.
+        
+        (WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath): Do a single path
+        traversal to get the position and the angle for a length on a path.
+        
+        * svg/SVGAnimateMotionElement.cpp:
+        (WebCore::SVGAnimateMotionElement::buildTransformForProgress): Do a single
+        path traversal to get the position and the angle at a length on a path.
+        
+        * svg/SVGPathTraversalStateBuilder.cpp:
+        (WebCore::SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder):
+        (WebCore::SVGPathTraversalStateBuilder::moveTo):
+        (WebCore::SVGPathTraversalStateBuilder::lineTo):
+        (WebCore::SVGPathTraversalStateBuilder::curveToCubic):
+        (WebCore::SVGPathTraversalStateBuilder::closePath):
+        (WebCore::SVGPathTraversalStateBuilder::setDesiredLength):
+        (WebCore::SVGPathTraversalStateBuilder::continueConsuming):
+        (WebCore::SVGPathTraversalStateBuilder::totalLength):
+        (WebCore::SVGPathTraversalStateBuilder::currentPoint):
+        (WebCore::SVGPathTraversalStateBuilder::incrementPathSegmentCount): Deleted.
+        (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): Deleted.
+        * svg/SVGPathTraversalStateBuilder.h:
+        (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex):
+        Code clean up.
+        
+        * svg/SVGPathUtilities.cpp:
+        (WebCore::getSVGPathSegAtLengthFromSVGPathByteStream):
+        (WebCore::getTotalLengthOfSVGPathByteStream):
+        (WebCore::getPointAtLengthOfSVGPathByteStream): Use new TraversalState::Action
+        enum values.
+
 2015-04-14  Simon Fraser  <simon.fra...@apple.com>
 
         Re-enable custom dilation for antialiased fonts

Modified: trunk/Source/WebCore/platform/graphics/Path.cpp (182827 => 182828)


--- trunk/Source/WebCore/platform/graphics/Path.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/Path.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -42,56 +42,34 @@
 static void pathLengthApplierFunction(void* info, const PathElement* element)
 {
     PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info);
-    if (traversalState.m_success)
-        return;
-    FloatPoint* points = element->points;
-    float segmentLength = 0;
-    switch (element->type) {
-        case PathElementMoveToPoint:
-            segmentLength = traversalState.moveTo(points[0]);
-            break;
-        case PathElementAddLineToPoint:
-            segmentLength = traversalState.lineTo(points[0]);
-            break;
-        case PathElementAddQuadCurveToPoint:
-            segmentLength = traversalState.quadraticBezierTo(points[0], points[1]);
-            break;
-        case PathElementAddCurveToPoint:
-            segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]);
-            break;
-        case PathElementCloseSubpath:
-            segmentLength = traversalState.closeSubpath();
-            break;
-    }
-    traversalState.m_totalLength += segmentLength; 
-    traversalState.processSegment();
+    traversalState.processPathElement(element);
 }
 
 float Path::length() const
 {
-    PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
+    PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
     apply(&traversalState, pathLengthApplierFunction);
-    return traversalState.m_totalLength;
+    return traversalState.totalLength();
 }
 
-FloatPoint Path::pointAtLength(float length, bool& ok) const
+PathTraversalState Path::traversalStateAtLength(float length, bool& success) const
 {
-    PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
-    traversalState.m_desiredLength = length;
+    PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
     apply(&traversalState, pathLengthApplierFunction);
-    ok = traversalState.m_success;
-    return traversalState.m_current;
+    success = traversalState.success();
+    return traversalState;
 }
 
-float Path::normalAngleAtLength(float length, bool& ok) const
+FloatPoint Path::pointAtLength(float length, bool& success) const
 {
-    PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
-    traversalState.m_desiredLength = length ? length : std::numeric_limits<float>::epsilon();
-    apply(&traversalState, pathLengthApplierFunction);
-    ok = traversalState.m_success;
-    return traversalState.m_normalAngle;
+    return traversalStateAtLength(length, success).current();
 }
 
+float Path::normalAngleAtLength(float length, bool& success) const
+{
+    return traversalStateAtLength(length, success).normalAngle();
+}
+
 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
 {
     if (rect.isEmpty())

Modified: trunk/Source/WebCore/platform/graphics/Path.h (182827 => 182828)


--- trunk/Source/WebCore/platform/graphics/Path.h	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/Path.h	2015-04-15 01:34:25 UTC (rev 182828)
@@ -60,6 +60,7 @@
     class FloatRoundedRect;
     class FloatSize;
     class GraphicsContext;
+    class PathTraversalState;
     class RoundedRect;
     class StrokeStyleApplier;
 
@@ -100,10 +101,11 @@
         FloatRect boundingRect() const;
         FloatRect fastBoundingRect() const;
         FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const;
-        
+
         float length() const;
-        FloatPoint pointAtLength(float length, bool& ok) const;
-        float normalAngleAtLength(float length, bool& ok) const;
+        PathTraversalState traversalStateAtLength(float length, bool& success) const;
+        FloatPoint pointAtLength(float length, bool& success) const;
+        float normalAngleAtLength(float length, bool& success) const;
 
         WEBCORE_EXPORT void clear();
         bool isNull() const { return !m_path; }

Modified: trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp (182827 => 182828)


--- trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006, 2007 Eric Seidel <e...@webkit.org>
+ * Copyright (C) 2015 Apple Inc.  All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -34,7 +35,9 @@
 
 static inline float distanceLine(const FloatPoint& start, const FloatPoint& end)
 {
-    return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y()));
+    float dx = end.x() - start.x();
+    float dy = end.y() - start.y();
+    return sqrtf(dx * dx + dy * dy);
 }
 
 struct QuadraticBezier {
@@ -114,111 +117,150 @@
 // 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 float curveLength(const PathTraversalState& traversalState, const CurveType& originalCurve, FloatPoint& previous, FloatPoint& current)
 {
     static const unsigned curveStackDepthLimit = 20;
+    CurveType curve = originalCurve;
+    Vector<CurveType, curveStackDepthLimit> curveStack;
+    float totalLength = 0;
 
-    Vector<CurveType> curveStack;
-    curveStack.append(curve);
+    while (true) {
+        float length = curve.approximateDistance();
 
-    float totalLength = 0;
-    do {
-        float length = curve.approximateDistance();
-        if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() <= curveStackDepthLimit) {
+        if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() < curveStackDepthLimit) {
             CurveType leftCurve;
             CurveType rightCurve;
             curve.split(leftCurve, rightCurve);
             curve = leftCurve;
             curveStack.append(rightCurve);
-        } else {
-            totalLength += length;
-            if (traversalState.m_action == PathTraversalState::TraversalPointAtLength
-             || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) {
-                traversalState.m_previous = curve.start;
-                traversalState.m_current = curve.end;
-                if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength)
-                    return totalLength;
-            }
-            curve = curveStack.last();
-            curveStack.removeLast();
+            continue;
         }
-    } while (!curveStack.isEmpty());
-    
+
+        totalLength += length;
+        if (traversalState.action() == PathTraversalState::Action::VectorAtLength) {
+            previous = curve.start;
+            current = curve.end;
+            if (traversalState.totalLength() + totalLength > traversalState.desiredLength())
+                break;
+        }
+
+        if (curveStack.isEmpty())
+            break;
+
+        curve = curveStack.last();
+        curveStack.removeLast();
+    }
+
+    if (traversalState.action() != PathTraversalState::Action::VectorAtLength) {
+        ASSERT(curve.end == originalCurve.end);
+        previous = curve.start;
+        current = curve.end;
+    }
+
     return totalLength;
 }
 
-PathTraversalState::PathTraversalState(PathTraversalAction action)
+PathTraversalState::PathTraversalState(Action action, float desiredLength)
     : m_action(action)
-    , m_success(false)
-    , m_totalLength(0)
-    , m_segmentIndex(0)
-    , m_desiredLength(0)
-    , m_normalAngle(0)
+    , m_desiredLength(desiredLength)
 {
+    ASSERT(action != Action::TotalLength || !desiredLength);
 }
 
-float PathTraversalState::closeSubpath()
+void PathTraversalState::closeSubpath()
 {
-    float distance = distanceLine(m_current, m_start);
-    m_current = m_control1 = m_control2 = m_start;
-    return distance;
+    m_totalLength += distanceLine(m_current, m_start);
+    m_current = m_start;
 }
 
-float PathTraversalState::moveTo(const FloatPoint& point)
+void PathTraversalState::moveTo(const FloatPoint& point)
 {
-    m_current = m_start = m_control1 = m_control2 = point;
-    return 0;
+    m_previous = m_current = m_start = point;
 }
 
-float PathTraversalState::lineTo(const FloatPoint& point)
+void PathTraversalState::lineTo(const FloatPoint& point)
 {
-    float distance = distanceLine(m_current, point);
-    m_current = m_control1 = m_control2 = point;
-    return distance;
+    m_totalLength += distanceLine(m_current, point);
+    m_current = point;
 }
 
-float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd)
+void PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd)
 {
-    float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd));
+    m_totalLength += curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current);
+}
 
-    m_control1 = newControl;
-    m_control2 = newEnd;
-
-    if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) 
-        m_current = newEnd;
-
-    return distance;
+void PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
+{
+    m_totalLength += curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd), m_previous, m_current);
 }
 
-float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
+bool PathTraversalState::finalizeAppendPathElement()
 {
-    float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd));
+    if (m_action == Action::TotalLength)
+        return false;
 
-    m_control1 = newEnd;
-    m_control2 = newControl2;
- 
-    if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) 
-        m_current = newEnd;
+    if (m_action == Action::SegmentAtLength) {
+        if (m_totalLength >= m_desiredLength)
+            m_success = true;
+        return m_success;
+    }
 
-    return distance;
-}
+    ASSERT(m_action == Action::VectorAtLength);
 
-void PathTraversalState::processSegment()
-{
-    if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength)
-        m_success = true;
-        
-    if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) && m_totalLength >= m_desiredLength) {
+    if (m_totalLength >= m_desiredLength) {
         float slope = FloatPoint(m_current - m_previous).slopeAngleRadians();
-        if (m_action == TraversalPointAtLength) {
-            float offset = m_desiredLength - m_totalLength;
-            m_current.move(offset * cosf(slope), offset * sinf(slope));
-        } else
+        float offset = m_desiredLength - m_totalLength;
+        m_current.move(offset * cosf(slope), offset * sinf(slope));
+
+        if (!m_isZeroVector && !m_desiredLength)
+            m_isZeroVector = true;
+        else {
+            m_success = true;
             m_normalAngle = rad2deg(slope);
-        m_success = true;
+        }
     }
+
     m_previous = m_current;
+    return m_success;
 }
 
+bool PathTraversalState::appendPathElement(PathElementType type, const FloatPoint* points)
+{
+    switch (type) {
+    case PathElementMoveToPoint:
+        moveTo(points[0]);
+        break;
+    case PathElementAddLineToPoint:
+        lineTo(points[0]);
+        break;
+    case PathElementAddQuadCurveToPoint:
+        quadraticBezierTo(points[0], points[1]);
+        break;
+    case PathElementAddCurveToPoint:
+        cubicBezierTo(points[0], points[1], points[2]);
+        break;
+    case PathElementCloseSubpath:
+        closeSubpath();
+        break;
+    }
+    
+    return finalizeAppendPathElement();
 }
 
+bool PathTraversalState::processPathElement(PathElementType type, const FloatPoint* points)
+{
+    if (m_success)
+        return true;
+
+    if (m_isZeroVector) {
+        PathTraversalState traversalState(*this);
+        m_success = traversalState.appendPathElement(type, points);
+        m_normalAngle = traversalState.m_normalAngle;
+        return m_success;
+    }
+
+    return appendPathElement(type, points);
+}
+
+}
+

Modified: trunk/Source/WebCore/platform/graphics/PathTraversalState.h (182827 => 182828)


--- trunk/Source/WebCore/platform/graphics/PathTraversalState.h	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/PathTraversalState.h	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006, 2007 Eric Seidel <e...@webkit.org>
+ * Copyright (C) 2015 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,45 +28,60 @@
 #define PathTraversalState_h
 
 #include "FloatPoint.h"
+#include "Path.h"
 
 namespace WebCore {
 
 class PathTraversalState {
 public:
-    enum PathTraversalAction {
-        TraversalTotalLength,
-        TraversalPointAtLength,
-        TraversalSegmentAtLength,
-        TraversalNormalAngleAtLength
+    enum class Action {
+        TotalLength,
+        VectorAtLength,
+        SegmentAtLength,
     };
 
-    PathTraversalState(PathTraversalAction);
+    PathTraversalState(Action, float desiredLength = 0);
 
-    float closeSubpath();
-    float moveTo(const FloatPoint&);
-    float lineTo(const FloatPoint&);
-    float quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd);
-    float cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd);
-    
-    void processSegment();
-
 public:
-    PathTraversalAction m_action;
-    bool m_success;
+    bool processPathElement(PathElementType, const FloatPoint*);
+    bool processPathElement(const PathElement* element) { return processPathElement(element->type, element->points); }
 
+    Action action() const { return m_action; }
+    void setAction(Action action) { m_action = action; }
+    float desiredLength() const { return m_desiredLength; }
+    void setDesiredLength(float desiredLength) { m_desiredLength = desiredLength; }
+
+    // Traversing output -- should be read only
+    bool success() const { return m_success; }
+    float totalLength() const { return m_totalLength; }
+    FloatPoint current() const { return m_current; }
+    float normalAngle() const { return m_normalAngle; }
+
+private:
+    void closeSubpath();
+    void moveTo(const FloatPoint&);
+    void lineTo(const FloatPoint&);
+    void quadraticBezierTo(const FloatPoint&, const FloatPoint&);
+    void cubicBezierTo(const FloatPoint&, const FloatPoint&, const FloatPoint&);
+
+    bool finalizeAppendPathElement();
+    bool appendPathElement(PathElementType, const FloatPoint*);
+
+private:
+    Action m_action;
+    bool m_success { false };
+
     FloatPoint m_current;
     FloatPoint m_start;
-    FloatPoint m_control1;
-    FloatPoint m_control2;
 
-    float m_totalLength;
-    unsigned m_segmentIndex;
-    float m_desiredLength;
+    float m_totalLength { 0 };
+    float m_desiredLength { 0 };
 
     // For normal calculations
     FloatPoint m_previous;
-    float m_normalAngle; // degrees
-};    
+    float m_normalAngle { 0 }; // degrees
+    bool m_isZeroVector { false };
+};
 }
 
 #endif

Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp (182827 => 182828)


--- trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -24,52 +25,96 @@
 
 namespace WebCore {
 
-SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength)
-    : m_chunkStyle(chunkStyle)
-    , m_desiredTextLength(desiredTextLength)
+SVGTextChunk::SVGTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned first, unsigned limit)
 {
-}
+    ASSERT(first < limit);
+    ASSERT(first >= 0 && limit <= lineLayoutBoxes.size());
 
-void SVGTextChunk::calculateLength(float& length, unsigned& characters) const
-{
-    SVGTextFragment* lastFragment = 0;
+    const SVGInlineTextBox* box = lineLayoutBoxes[first];
+    const RenderStyle& style = box->renderer().style();
+    const SVGRenderStyle& svgStyle = style.svgStyle();
 
-    unsigned boxCount = m_boxes.size();
-    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
-        SVGInlineTextBox* textBox = m_boxes.at(boxPosition);
-        Vector<SVGTextFragment>& fragments = textBox->textFragments();
+    if (!style.isLeftToRightDirection())
+        m_chunkStyle |= SVGTextChunk::RightToLeftText;
 
-        unsigned size = fragments.size();
-        if (!size)
-            continue;
+    if (svgStyle.isVerticalWritingMode())
+        m_chunkStyle |= SVGTextChunk::VerticalText;
+    
+    switch (svgStyle.textAnchor()) {
+    case TA_START:
+        break;
+    case TA_MIDDLE:
+        m_chunkStyle |= MiddleAnchor;
+        break;
+    case TA_END:
+        m_chunkStyle |= EndAnchor;
+        break;
+    }
 
-        for (unsigned i = 0; i < size; ++i) {
-            SVGTextFragment& fragment = fragments.at(i);
-            characters += fragment.length;
+    if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box->renderer().parent())) {
+        SVGLengthContext lengthContext(textContentElement);
+        m_desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
 
-            if (m_chunkStyle & VerticalText)
-                length += fragment.height;
-            else
-                length += fragment.width;
+        switch (textContentElement->lengthAdjust()) {
+        case SVGLengthAdjustUnknown:
+            break;
+        case SVGLengthAdjustSpacing:
+            m_chunkStyle |= LengthAdjustSpacing;
+            break;
+        case SVGLengthAdjustSpacingAndGlyphs:
+            m_chunkStyle |= LengthAdjustSpacingAndGlyphs;
+            break;
+        }
+    }
 
-            if (!lastFragment) {
-                lastFragment = &fragment;
-                continue;
-            }
+    for (unsigned i = first; i < limit; ++i)
+        m_boxes.append(lineLayoutBoxes[i]);
+}
 
-            // Resepect gap between chunks.
-            if (m_chunkStyle & VerticalText)
-                 length += fragment.y - (lastFragment->y + lastFragment->height);
-            else
-                 length += fragment.x - (lastFragment->x + lastFragment->width);
+unsigned SVGTextChunk::totalCharacters() const
+{
+    unsigned characters = 0;
+    for (auto* box : m_boxes) {
+        for (auto& fragment : box->textFragments())
+            characters += fragment.length;
+    }
+    return characters;
+}
 
-            lastFragment = &fragment;
+float SVGTextChunk::totalLength() const
+{
+    const SVGTextFragment* firstFragment = nullptr;
+    const SVGTextFragment* lastFragment = nullptr;
+
+    for (auto* box : m_boxes) {
+        auto& fragments = box->textFragments();
+        if (fragments.size()) {
+            firstFragment = &(*fragments.begin());
+            break;
         }
     }
+
+    for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) {
+        auto& fragments = (*it)->textFragments();
+        if (fragments.size()) {
+            lastFragment = &(*fragments.rbegin());
+            break;
+        }
+    }
+
+    ASSERT(!firstFragment == !lastFragment);
+    if (!firstFragment)
+        return 0;
+
+    if (m_chunkStyle & VerticalText)
+        return (lastFragment->y + lastFragment->height) - firstFragment->y;
+
+    return (lastFragment->x + lastFragment->width) - firstFragment->x;
 }
 
-float SVGTextChunk::calculateTextAnchorShift(float length) const
+float SVGTextChunk::totalAnchorShift() const
 {
+    float length = totalLength();
     if (m_chunkStyle & MiddleAnchor)
         return -length / 2;
     if (m_chunkStyle & EndAnchor)
@@ -77,4 +122,88 @@
     return m_chunkStyle & RightToLeftText ? -length : 0;
 }
 
+void SVGTextChunk::layout(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
+{
+    if (hasDesiredTextLength()) {
+        if (hasLengthAdjustSpacing())
+            processTextLengthSpacingCorrection();
+        else {
+            ASSERT(hasLengthAdjustSpacingAndGlyphs());
+            buildBoxTransformations(textBoxTransformations);
+        }
+    }
+
+    if (hasTextAnchor())
+        processTextAnchorCorrection();
+}
+
+void SVGTextChunk::processTextLengthSpacingCorrection() const
+{
+    float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters();
+    bool isVerticalText = m_chunkStyle & VerticalText;
+    unsigned atCharacter = 0;
+
+    for (auto* box : m_boxes) {
+        for (auto& fragment : box->textFragments()) {
+            if (isVerticalText)
+                fragment.y += textLengthShift * atCharacter;
+            else
+                fragment.x += textLengthShift * atCharacter;
+            
+            atCharacter += fragment.length;
+        }
+    }
+}
+
+void SVGTextChunk::buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
+{
+    AffineTransform spacingAndGlyphsTransform;
+    bool foundFirstFragment = false;
+
+    for (auto* box : m_boxes) {
+        if (!foundFirstFragment) {
+            if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform))
+                continue;
+            foundFirstFragment = true;
+        }
+
+        textBoxTransformations.set(box, spacingAndGlyphsTransform);
+    }
+}
+
+bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform& spacingAndGlyphsTransform) const
+{
+    auto& fragments = box->textFragments();
+    if (fragments.isEmpty())
+        return false;
+
+    const SVGTextFragment& fragment = fragments.first();
+    float scale = desiredTextLength() / totalLength();
+
+    spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+
+    if (m_chunkStyle & VerticalText)
+        spacingAndGlyphsTransform.scaleNonUniform(1, scale);
+    else
+        spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+
+    spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+    return true;
+}
+
+void SVGTextChunk::processTextAnchorCorrection() const
+{
+    float textAnchorShift = totalAnchorShift();
+    bool isVerticalText = m_chunkStyle & VerticalText;
+
+    for (auto* box : m_boxes) {
+        for (auto& fragment : box->textFragments()) {
+            if (isVerticalText)
+                fragment.y += textAnchorShift;
+            else
+                fragment.x += textAnchorShift;
+        }
+    }
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunk.h (182827 => 182828)


--- trunk/Source/WebCore/rendering/svg/SVGTextChunk.h	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunk.h	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -40,28 +41,34 @@
         LengthAdjustSpacingAndGlyphs = 1 << 6
     };
 
-    SVGTextChunk(unsigned chunkStyle, float desiredTextLength);
+    SVGTextChunk(const Vector<SVGInlineTextBox*>&, unsigned first, unsigned limit);
 
-    void calculateLength(float& length, unsigned& characters) const;
-    float calculateTextAnchorShift(float length) const;
+    unsigned totalCharacters() const;
+    float totalLength() const;
+    float totalAnchorShift() const;
+    void layout(HashMap<SVGInlineTextBox*, AffineTransform>&) const;
 
+private:
+    void processTextAnchorCorrection() const;
+    void buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>&) const;
+    void processTextLengthSpacingCorrection() const;
+
     bool isVerticalText() const { return m_chunkStyle & VerticalText; }
     float desiredTextLength() const { return m_desiredTextLength; }
 
-    Vector<SVGInlineTextBox*>& boxes() { return m_boxes; }
-    const Vector<SVGInlineTextBox*>& boxes() const { return m_boxes; }
-
     bool hasDesiredTextLength() const { return m_desiredTextLength > 0 && ((m_chunkStyle & LengthAdjustSpacing) || (m_chunkStyle & LengthAdjustSpacingAndGlyphs)); }
-    bool hasTextAnchor() const {  return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & MiddleAnchor) || (m_chunkStyle & EndAnchor); }
+    bool hasTextAnchor() const {  return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & (MiddleAnchor | EndAnchor)); }
     bool hasLengthAdjustSpacing() const { return m_chunkStyle & LengthAdjustSpacing; }
     bool hasLengthAdjustSpacingAndGlyphs() const { return m_chunkStyle & LengthAdjustSpacingAndGlyphs; }
 
+    bool boxSpacingAndGlyphsTransform(const SVGInlineTextBox*, AffineTransform&) const;
+
 private:
     // Contains all SVGInlineTextBoxes this chunk spans.
     Vector<SVGInlineTextBox*> m_boxes;
 
-    unsigned m_chunkStyle;
-    float m_desiredTextLength;
+    unsigned m_chunkStyle { DefaultStyle };
+    float m_desiredTextLength { 0 };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp (182827 => 182828)


--- trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -30,227 +31,71 @@
 {
 }
 
-void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const
+unsigned SVGTextChunkBuilder::totalCharacters() const
 {
-    DEPRECATED_DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ());
-    if (!m_textBoxTransformations.contains(textBox)) {
-        transform = s_identityTransform;
-        return;
-    }
-
-    transform = m_textBoxTransformations.get(textBox);
+    unsigned characters = 0;
+    for (const auto& chunk : m_textChunks)
+        characters += chunk.totalCharacters();
+    return characters;
 }
 
-void SVGTextChunkBuilder::buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
+float SVGTextChunkBuilder::totalLength() const
 {
-    if (lineLayoutBoxes.isEmpty())
-        return;
-
-    bool foundStart = false;
-    unsigned lastChunkStartPosition = 0;
-    unsigned boxPosition = 0;
-    unsigned boxCount = lineLayoutBoxes.size();
-    for (; boxPosition < boxCount; ++boxPosition) {
-        SVGInlineTextBox* textBox = lineLayoutBoxes[boxPosition];
-        if (!textBox->startsNewTextChunk())
-            continue;
-
-        if (!foundStart) {
-            lastChunkStartPosition = boxPosition;
-            foundStart = true;
-        } else {
-            ASSERT_WITH_SECURITY_IMPLICATION(boxPosition > lastChunkStartPosition);
-            addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
-            lastChunkStartPosition = boxPosition;
-        }
-    }
-
-    if (!foundStart)
-        return;
-
-    if (boxPosition - lastChunkStartPosition > 0)
-        addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
+    float length = 0;
+    for (const auto& chunk : m_textChunks)
+        length += chunk.totalLength();
+    return length;
 }
 
-void SVGTextChunkBuilder::layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
+float SVGTextChunkBuilder::totalAnchorShift() const
 {
-    buildTextChunks(lineLayoutBoxes);
-    if (m_textChunks.isEmpty())
-        return;
-
-    unsigned chunkCount = m_textChunks.size();
-    for (unsigned i = 0; i < chunkCount; ++i)
-        processTextChunk(m_textChunks[i]);
-
-    m_textChunks.clear();
+    float anchorShift = 0;
+    for (const auto& chunk : m_textChunks)
+        anchorShift += chunk.totalAnchorShift();
+    return anchorShift;
 }
 
-void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
+AffineTransform SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox) const
 {
-    SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart];
-    ASSERT(textBox);
-
-    const RenderStyle& style = textBox->renderer().style();
-
-    const SVGRenderStyle& svgStyle = style.svgStyle();
-
-    // Build chunk style flags.
-    unsigned chunkStyle = SVGTextChunk::DefaultStyle;
-
-    // Handle 'direction' property.
-    if (!style.isLeftToRightDirection())
-        chunkStyle |= SVGTextChunk::RightToLeftText;
-
-    // Handle 'writing-mode' property.
-    if (svgStyle.isVerticalWritingMode())
-        chunkStyle |= SVGTextChunk::VerticalText;
-
-    // Handle 'text-anchor' property.
-    switch (svgStyle.textAnchor()) {
-    case TA_START:
-        break;
-    case TA_MIDDLE:
-        chunkStyle |= SVGTextChunk::MiddleAnchor;
-        break;
-    case TA_END:
-        chunkStyle |= SVGTextChunk::EndAnchor;
-        break;
-    };
-
-    // Handle 'lengthAdjust' property.
-    float desiredTextLength = 0;
-    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textBox->renderer().parent())) {
-        SVGLengthContext lengthContext(textContentElement);
-        desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
-
-        switch (textContentElement->lengthAdjust()) {
-        case SVGLengthAdjustUnknown:
-            break;
-        case SVGLengthAdjustSpacing:
-            chunkStyle |= SVGTextChunk::LengthAdjustSpacing;
-            break;
-        case SVGLengthAdjustSpacingAndGlyphs:
-            chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs;
-            break;
-        };
-    }
-
-    SVGTextChunk chunk(chunkStyle, desiredTextLength);
-
-    Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
-    for (unsigned i = boxStart; i < boxStart + boxCount; ++i)
-        boxes.append(lineLayoutBoxes[i]);
-
-    m_textChunks.append(chunk);
+    auto it = m_textBoxTransformations.find(textBox);
+    return it == m_textBoxTransformations.end() ? AffineTransform() : it->value;
 }
 
-void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)
+void SVGTextChunkBuilder::buildTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes)
 {
-    bool processTextLength = chunk.hasDesiredTextLength();
-    bool processTextAnchor = chunk.hasTextAnchor();
-    if (!processTextAnchor && !processTextLength)
+    if (lineLayoutBoxes.isEmpty())
         return;
 
-    const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
-    unsigned boxCount = boxes.size();
-    if (!boxCount)
-        return;
+    unsigned limit = lineLayoutBoxes.size();
+    unsigned first = limit;
 
-    // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).
-    float chunkLength = 0;
-    unsigned chunkCharacters = 0;
-    chunk.calculateLength(chunkLength, chunkCharacters);
+    for (unsigned i = 0; i < limit; ++i) {
+        if (!lineLayoutBoxes[i]->startsNewTextChunk())
+            continue;
 
-    bool isVerticalText = chunk.isVerticalText();
-    if (processTextLength) {
-        if (chunk.hasLengthAdjustSpacing()) {
-            float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
-            unsigned atCharacter = 0;
-            for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
-                Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
-                if (fragments.isEmpty())
-                    continue;
-                processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
-            }
-        } else {
-            ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs());
-            float textLengthScale = chunk.desiredTextLength() / chunkLength;
-            AffineTransform spacingAndGlyphsTransform;
-
-            bool foundFirstFragment = false;
-            for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
-                SVGInlineTextBox* textBox = boxes[boxPosition];
-                Vector<SVGTextFragment>& fragments = textBox->textFragments();
-                if (fragments.isEmpty())
-                    continue;
-
-                if (!foundFirstFragment) {
-                    foundFirstFragment = true;
-                    buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform);
-                }
-
-                m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
-            }
+        if (first == limit)
+            first = i;
+        else {
+            ASSERT_WITH_SECURITY_IMPLICATION(first != i);
+            m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, i));
+            first = i;
         }
     }
 
-    if (!processTextAnchor)
-        return;
-
-    // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.
-    if (processTextLength && chunk.hasLengthAdjustSpacing()) {
-        chunkLength = 0;
-        chunkCharacters = 0;
-        chunk.calculateLength(chunkLength, chunkCharacters);
-    }
-
-    float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
-    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
-        Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
-        if (fragments.isEmpty())
-            continue;
-        processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
-    }
+    if (first != limit)
+        m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, limit));
 }
 
-void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)
+void SVGTextChunkBuilder::layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes)
 {
-    unsigned fragmentCount = fragments.size();
-    for (unsigned i = 0; i < fragmentCount; ++i) {
-        SVGTextFragment& fragment = fragments[i];
+    buildTextChunks(lineLayoutBoxes);
+    if (m_textChunks.isEmpty())
+        return;
 
-        if (isVerticalText)
-            fragment.y += textLengthShift * atCharacter;
-        else
-            fragment.x += textLengthShift * atCharacter;
+    for (const auto& chunk : m_textChunks)
+        chunk.layout(m_textBoxTransformations);
 
-        atCharacter += fragment.length;
-    }
+    m_textChunks.clear();
 }
 
-void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments)
-{
-    unsigned fragmentCount = fragments.size();
-    for (unsigned i = 0; i < fragmentCount; ++i) {
-        SVGTextFragment& fragment = fragments[i];
-
-        if (isVerticalText)
-            fragment.y += textAnchorShift;
-        else
-            fragment.x += textAnchorShift;
-    }
 }
-
-void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)
-{
-    spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
-
-    if (isVerticalText)
-        spacingAndGlyphsTransform.scaleNonUniform(1, scale);
-    else
-        spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
-
-    spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
-}
-
-}

Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h (182827 => 182828)


--- trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h	2015-04-15 01:34:25 UTC (rev 182828)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -40,20 +41,15 @@
     SVGTextChunkBuilder();
 
     const Vector<SVGTextChunk>& textChunks() const { return m_textChunks; }
-    void transformationForTextBox(SVGInlineTextBox*, AffineTransform&) const;
+    unsigned totalCharacters() const;
+    float totalLength() const;
+    float totalAnchorShift() const;
+    AffineTransform transformationForTextBox(SVGInlineTextBox*) const;
 
-    void buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes);
-    void layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes);
+    void buildTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes);
+    void layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes);
 
 private:
-    void addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxPosition, unsigned boxCount);
-    void processTextChunk(const SVGTextChunk&);
-
-    void processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>&, unsigned& atCharacter);
-    void processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>&);
-    void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&, AffineTransform&);
-
-private:
     Vector<SVGTextChunk> m_textChunks;
     HashMap<SVGInlineTextBox*, AffineTransform> m_textBoxTransformations;
 };

Modified: trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp (182827 => 182828)


--- trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "SVGTextLayoutEngine.h"
 
+#include "PathTraversalState.h"
 #include "RenderSVGTextPath.h"
 #include "SVGElement.h"
 #include "SVGInlineTextBox.h"
@@ -170,48 +171,32 @@
     m_textPath = textPath.layoutPath();
     if (m_textPath.isEmpty())
         return;
+
     m_textPathStartOffset = textPath.startOffset();
     m_textPathLength = m_textPath.length();
     if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1)
         m_textPathStartOffset *= m_textPathLength;
 
-    float totalLength = 0;
-    unsigned totalCharacters = 0;
-
     lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes);
-    const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks();
 
-    unsigned size = textChunks.size();
-    for (unsigned i = 0; i < size; ++i) {
-        const SVGTextChunk& chunk = textChunks.at(i);
-
-        float length = 0;
-        unsigned characters = 0;
-        chunk.calculateLength(length, characters);
-
-        // Handle text-anchor as additional start offset for text paths.
-        m_textPathStartOffset += chunk.calculateTextAnchorShift(length);
-
-        totalLength += length;
-        totalCharacters += characters;
-    }
-
+    // Handle text-anchor as additional start offset for text paths.
+    m_textPathStartOffset += lineLayout.m_chunkLayoutBuilder.totalAnchorShift();
     m_textPathCurrentOffset = m_textPathStartOffset;
 
     // Eventually handle textLength adjustments.
-    SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown;
-    float desiredTextLength = 0;
+    auto* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath);
+    if (!textContentElement)
+        return;
 
-    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath)) {
-        SVGLengthContext lengthContext(textContentElement);
-        lengthAdjust = textContentElement->lengthAdjust();
-        desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
-    }
-
+    SVGLengthContext lengthContext(textContentElement);
+    float desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
     if (!desiredTextLength)
         return;
 
-    if (lengthAdjust == SVGLengthAdjustSpacing)
+    float totalLength = lineLayout.m_chunkLayoutBuilder.totalLength();
+    unsigned totalCharacters = lineLayout.m_chunkLayoutBuilder.totalCharacters();
+
+    if (textContentElement->lengthAdjust() == SVGLengthAdjustSpacing)
         m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters;
     else
         m_textPathScaling = desiredTextLength / totalLength;
@@ -290,7 +275,7 @@
 
         unsigned fragmentCount = fragments.size();
         for (unsigned i = 0; i < fragmentCount; ++i) {
-            m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);
+            textBoxTransformation = m_chunkLayoutBuilder.transformationForTextBox(textBox);
             if (textBoxTransformation.isIdentity())
                 continue;
             ASSERT(fragments[i].lengthAdjustTransform.isIdentity());
@@ -553,15 +538,16 @@
             if (textPathOffset > m_textPathLength)
                 break;
 
-            bool ok = false;
-            FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
-            ASSERT(ok);
+            bool success = false;
+            auto traversalState(m_textPath.traversalStateAtLength(textPathOffset, success));
+            ASSERT(success);
 
+            FloatPoint point = traversalState.current();
             x = point.x();
             y = point.y();
-            angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
-            ASSERT(ok);
 
+            angle = traversalState.normalAngle();
+
             // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
             if (m_isVerticalText)
                 angle -= 90;

Modified: trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp (182827 => 182828)


--- trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -24,6 +24,7 @@
 
 #include "AffineTransform.h"
 #include "ElementIterator.h"
+#include "PathTraversalState.h"
 #include "RenderSVGResource.h"
 #include "SVGImageElement.h"
 #include "SVGMPathElement.h"
@@ -212,16 +213,19 @@
 {
     ASSERT(!m_animationPath.isEmpty());
 
-    bool ok = false;
+    bool success = false;
     float positionOnPath = m_animationPath.length() * percentage;
-    FloatPoint position = m_animationPath.pointAtLength(positionOnPath, ok);
-    if (!ok)
+    auto traversalState(m_animationPath.traversalStateAtLength(positionOnPath, success));
+    if (!success)
         return;
+
+    FloatPoint position = traversalState.current();
+    float angle = traversalState.normalAngle();
+
     transform->translate(position.x(), position.y());
     RotateMode rotateMode = this->rotateMode();
     if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
         return;
-    float angle = m_animationPath.normalAngleAtLength(positionOnPath, ok);
     if (rotateMode == RotateAutoReverse)
         angle += 180;
     transform->rotate(angle);

Modified: trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp (182827 => 182828)


--- trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -29,68 +29,57 @@
 
 SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder()
     : m_traversalState(0)
+    , m_segmentIndex(0)
 {
 }
 
 void SVGPathTraversalStateBuilder::moveTo(const FloatPoint& targetPoint, bool, PathCoordinateMode)
 {
     ASSERT(m_traversalState);
-    m_traversalState->m_totalLength += m_traversalState->moveTo(targetPoint);
+    m_traversalState->processPathElement(PathElementMoveToPoint, &targetPoint);
 }
 
 void SVGPathTraversalStateBuilder::lineTo(const FloatPoint& targetPoint, PathCoordinateMode)
 {
     ASSERT(m_traversalState);
-    m_traversalState->m_totalLength += m_traversalState->lineTo(targetPoint);
+    m_traversalState->processPathElement(PathElementAddLineToPoint, &targetPoint);
 }
 
 void SVGPathTraversalStateBuilder::curveToCubic(const FloatPoint& point1, const FloatPoint& point2, const FloatPoint& targetPoint, PathCoordinateMode)
 {
+    FloatPoint points[] = { point1, point2, targetPoint };
     ASSERT(m_traversalState);
-    m_traversalState->m_totalLength += m_traversalState->cubicBezierTo(point1, point2, targetPoint);
+    m_traversalState->processPathElement(PathElementAddCurveToPoint, points);
 }
 
 void SVGPathTraversalStateBuilder::closePath()
 {
     ASSERT(m_traversalState);
-    m_traversalState->m_totalLength += m_traversalState->closeSubpath();
+    m_traversalState->processPathElement(PathElementCloseSubpath, nullptr);
 }
 
 void SVGPathTraversalStateBuilder::setDesiredLength(float desiredLength)
 {
     ASSERT(m_traversalState);
-    m_traversalState->m_desiredLength = desiredLength;
+    m_traversalState->setDesiredLength(desiredLength);
 }
 
 bool SVGPathTraversalStateBuilder::continueConsuming()
 {
     ASSERT(m_traversalState);    
-    m_traversalState->processSegment();
-    return !m_traversalState->m_success;
+    return !m_traversalState->success();
 }
 
-void SVGPathTraversalStateBuilder::incrementPathSegmentCount()
-{
-    ASSERT(m_traversalState);
-    ++m_traversalState->m_segmentIndex;
-}
-
-unsigned SVGPathTraversalStateBuilder::pathSegmentIndex()
-{
-    ASSERT(m_traversalState);
-    return m_traversalState->m_segmentIndex;
-}
-
 float SVGPathTraversalStateBuilder::totalLength()
 {
     ASSERT(m_traversalState);
-    return m_traversalState->m_totalLength;
+    return m_traversalState->totalLength();
 }
 
 SVGPoint SVGPathTraversalStateBuilder::currentPoint()
 {
     ASSERT(m_traversalState);
-    return m_traversalState->m_current;
+    return m_traversalState->current();
 }
 
 }

Modified: trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h (182827 => 182828)


--- trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h	2015-04-15 01:34:25 UTC (rev 182828)
@@ -32,15 +32,15 @@
 public:
     SVGPathTraversalStateBuilder();
 
-    unsigned pathSegmentIndex();
+    unsigned pathSegmentIndex() { return m_segmentIndex; }
     float totalLength();
     SVGPoint currentPoint();
 
     void setCurrentTraversalState(PathTraversalState* traversalState) { m_traversalState = traversalState; }
     void setDesiredLength(float);
-    virtual void incrementPathSegmentCount() override;
+    virtual void incrementPathSegmentCount() override { ++m_segmentIndex; }
     virtual bool continueConsuming() override;
-    virtual void cleanup() override { m_traversalState = 0; }
+    virtual void cleanup() override { m_traversalState = nullptr, m_segmentIndex = 0; }
 
 private:
     // Used in UnalteredParsing/NormalizedParsing modes.
@@ -59,6 +59,7 @@
     virtual void arcTo(float, float, float, bool, bool, const FloatPoint&, PathCoordinateMode) override { ASSERT_NOT_REACHED(); }
 
     PathTraversalState* m_traversalState;
+    unsigned m_segmentIndex;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/svg/SVGPathUtilities.cpp (182827 => 182828)


--- trunk/Source/WebCore/svg/SVGPathUtilities.cpp	2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathUtilities.cpp	2015-04-15 01:34:25 UTC (rev 182828)
@@ -285,7 +285,7 @@
     if (stream->isEmpty())
         return false;
 
-    PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength);
+    PathTraversalState traversalState(PathTraversalState::Action::SegmentAtLength);
     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length);
 
     auto source = std::make_unique<SVGPathByteStreamSource>(stream);
@@ -302,7 +302,7 @@
     if (stream->isEmpty())
         return false;
 
-    PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
+    PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, 0);
 
     auto source = std::make_unique<SVGPathByteStreamSource>(stream);
@@ -319,7 +319,7 @@
     if (stream->isEmpty())
         return false;
 
-    PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
+    PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength);
     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length);
 
     auto source = std::make_unique<SVGPathByteStreamSource>(stream);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to