textPath layout performance improvement.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Apr 2015 01:34:25 +0000 (01:34 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Apr 2015 01:34:25 +0000 (01:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141570.

Patch by Said Abou-Hallawa <sabouhallawa@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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182828 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
PerformanceTests/ChangeLog
PerformanceTests/SVG/TextOnPathSimple.html [new file with mode: 0644]
PerformanceTests/SVG/resources/TextOnPathSimple.svg [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/Path.cpp
Source/WebCore/platform/graphics/Path.h
Source/WebCore/platform/graphics/PathTraversalState.cpp
Source/WebCore/platform/graphics/PathTraversalState.h
Source/WebCore/rendering/svg/SVGTextChunk.cpp
Source/WebCore/rendering/svg/SVGTextChunk.h
Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
Source/WebCore/rendering/svg/SVGTextChunkBuilder.h
Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
Source/WebCore/svg/SVGAnimateMotionElement.cpp
Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp
Source/WebCore/svg/SVGPathTraversalStateBuilder.h
Source/WebCore/svg/SVGPathUtilities.cpp

index 8a403b2..09ab3ae 100644 (file)
@@ -1,3 +1,17 @@
+2015-04-14  Said Abou-Hallawa  <sabouhallawa@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  <zalan@apple.com>
 
         Clear up the test content when test is done.
diff --git a/PerformanceTests/SVG/TextOnPathSimple.html b/PerformanceTests/SVG/TextOnPathSimple.html
new file mode 100644 (file)
index 0000000..a9e3f7d
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<body>
+<script src="../resources/runner.js"></script>
+<script>
+  window.onload = function() {
+    PerfTestRunner.measurePageLoadTime({path: "resources/TextOnPathSimple.svg"});
+  }
+</script>
+</body>
diff --git a/PerformanceTests/SVG/resources/TextOnPathSimple.svg b/PerformanceTests/SVG/resources/TextOnPathSimple.svg
new file mode 100644 (file)
index 0000000..f22647a
--- /dev/null
@@ -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="#SimplePath" fill="none" stroke="red"/>
+        <text font-family="Verdana" font-size="42.5">
+          <textPath xlink:href="#SimplePath">
+            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="#TextOnPath" transform="translate(0,40)"/>
+      <use xlink:href="#TextOnPath" transform="translate(0,80)"/>
+      <use xlink:href="#TextOnPath" transform="translate(0,120)"/>
+      <use xlink:href="#TextOnPath" transform="translate(0,160)"/>
+    </g>
+  
+    <use xlink:href="#TextOnPath5" transform="translate(0,200)"/>
+    <use xlink:href="#TextOnPath5" transform="translate(0,400)"/>
+    <use xlink:href="#TextOnPath5" transform="translate(0,600)"/>
+    <use xlink:href="#TextOnPath5" transform="translate(0,800)"/>
+  </g>
+
+  <use xlink:href="#TextOnPath25" transform="translate(2000,0)"/>
+  <use xlink:href="#TextOnPath25" transform="translate(0,1200)"/>
+  <use xlink:href="#TextOnPath25" transform="translate(2000,1200)"/>
+
+  <rect x="1" y="1" width="3999" height="2499" fill="none" stroke="black" stroke-width="2" />
+</svg>
index 5056f2b..63e6b04 100644 (file)
@@ -1,3 +1,195 @@
+2015-04-14  Said Abou-Hallawa  <sabouhallawa@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.fraser@apple.com>
 
         Re-enable custom dilation for antialiased fonts
index 409fd88..1acf729 100644 (file)
@@ -42,54 +42,32 @@ namespace WebCore {
 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)
index cbb3776..c3c4c07 100644 (file)
@@ -60,6 +60,7 @@ namespace WebCore {
     class FloatRoundedRect;
     class FloatSize;
     class GraphicsContext;
+    class PathTraversalState;
     class RoundedRect;
     class StrokeStyleApplier;
 
@@ -100,10 +101,11 @@ namespace WebCore {
         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; }
index cf53717..c6bf7a6 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006, 2007 Eric Seidel <eric@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 FloatPoint midPoint(const FloatPoint& first, const FloatPoint& sec
 
 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,110 +117,149 @@ struct CubicBezier {
 // 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;
-
-    Vector<CurveType> curveStack;
-    curveStack.append(curve);
-
+    CurveType curve = originalCurve;
+    Vector<CurveType, curveStackDepthLimit> curveStack;
     float totalLength = 0;
-    do {
+
+    while (true) {
         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_control1 = newControl;
-    m_control2 = newEnd;
-
-    if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) 
-        m_current = newEnd;
+    m_totalLength += curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current);
+}
 
-    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);
 }
 
 }
index fd9f066..a4c3538 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006, 2007 Eric Seidel <eric@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
 #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);
-
-    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();
+    PathTraversalState(Action, float desiredLength = 0);
 
 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
index 9f7e8e6..99daf5f 100644 (file)
@@ -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
 
 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 @@ float SVGTextChunk::calculateTextAnchorShift(float length) const
     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
index ec8afaf..a0a4c4f 100644 (file)
@@ -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 @@ public:
         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
index 2c47204..68b2d48 100644 (file)
@@ -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 @@ SVGTextChunkBuilder::SVGTextChunkBuilder()
 {
 }
 
-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)
-        return;
-
-    const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
-    unsigned boxCount = boxes.size();
-    if (!boxCount)
-        return;
-
-    // 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);
-
-    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 (!processTextAnchor)
+    if (lineLayoutBoxes.isEmpty())
         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);
-    }
+    unsigned limit = lineLayoutBoxes.size();
+    unsigned first = limit;
 
-    float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
-    for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
-        Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
-        if (fragments.isEmpty())
+    for (unsigned i = 0; i < limit; ++i) {
+        if (!lineLayoutBoxes[i]->startsNewTextChunk())
             continue;
-        processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
-    }
-}
-
-void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)
-{
-    unsigned fragmentCount = fragments.size();
-    for (unsigned i = 0; i < fragmentCount; ++i) {
-        SVGTextFragment& fragment = fragments[i];
 
-        if (isVerticalText)
-            fragment.y += textLengthShift * atCharacter;
-        else
-            fragment.x += textLengthShift * atCharacter;
-
-        atCharacter += fragment.length;
+        if (first == limit)
+            first = i;
+        else {
+            ASSERT_WITH_SECURITY_IMPLICATION(first != i);
+            m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, i));
+            first = i;
+        }
     }
-}
 
-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;
-    }
+    if (first != limit)
+        m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, limit));
 }
 
-void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)
+void SVGTextChunkBuilder::layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes)
 {
-    spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+    buildTextChunks(lineLayoutBoxes);
+    if (m_textChunks.isEmpty())
+        return;
 
-    if (isVerticalText)
-        spacingAndGlyphsTransform.scaleNonUniform(1, scale);
-    else
-        spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+    for (const auto& chunk : m_textChunks)
+        chunk.layout(m_textBoxTransformations);
 
-    spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+    m_textChunks.clear();
 }
 
 }
index 55d6600..6ecf081 100644 (file)
@@ -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,18 +41,13 @@ public:
     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);
-
-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&);
+    void buildTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes);
+    void layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes);
 
 private:
     Vector<SVGTextChunk> m_textChunks;
index fc95f37..e9c5350 100644 (file)
@@ -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 @@ void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayou
     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;
-
-    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath)) {
-        SVGLengthContext lengthContext(textContentElement);
-        lengthAdjust = textContentElement->lengthAdjust();
-        desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
-    }
+    auto* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath);
+    if (!textContentElement)
+        return;
 
+    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 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b
 
         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,14 +538,15 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend
             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)
index 03627bd..496c2f3 100644 (file)
@@ -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 @@ void SVGAnimateMotionElement::buildTransformForProgress(AffineTransform* transfo
 {
     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);
index 16e342c..4478be1 100644 (file)
@@ -29,68 +29,57 @@ namespace WebCore {
 
 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;
-}
-
-void SVGPathTraversalStateBuilder::incrementPathSegmentCount()
-{
-    ASSERT(m_traversalState);
-    ++m_traversalState->m_segmentIndex;
-}
-
-unsigned SVGPathTraversalStateBuilder::pathSegmentIndex()
-{
-    ASSERT(m_traversalState);
-    return m_traversalState->m_segmentIndex;
+    return !m_traversalState->success();
 }
 
 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();
 }
 
 }
index 9b0b455..f2cfcd7 100644 (file)
@@ -32,15 +32,15 @@ class SVGPathTraversalStateBuilder : public SVGPathConsumer {
 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 @@ private:
     virtual void arcTo(float, float, float, bool, bool, const FloatPoint&, PathCoordinateMode) override { ASSERT_NOT_REACHED(); }
 
     PathTraversalState* m_traversalState;
+    unsigned m_segmentIndex;
 };
 
 } // namespace WebCore
index 8057899..c323952 100644 (file)
@@ -285,7 +285,7 @@ bool getSVGPathSegAtLengthFromSVGPathByteStream(SVGPathByteStream* stream, float
     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 @@ bool getTotalLengthOfSVGPathByteStream(SVGPathByteStream* stream, float& totalLe
     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 @@ bool getPointAtLengthOfSVGPathByteStream(SVGPathByteStream* stream, float length
     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);