Changeset 182828 in webkit
- Timestamp:
- Apr 14, 2015 6:34:25 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/PerformanceTests/ChangeLog
r182744 r182828 1 2015-04-14 Said Abou-Hallawa <sabouhallawa@apple.com> 2 3 textPath layout performance improvement. 4 https://bugs.webkit.org/show_bug.cgi?id=141570. 5 6 Reviewed by Darin Adler. 7 8 Cut down the time spent in traversing the path for text by 50%. Instead 9 of traversing the path twice at a certain length: one time for the position 10 and the second time for the angle, we can merge these two passes into one. 11 12 * SVG/TextOnPathSimple.html: Added. 13 * SVG/resources/TextOnPathSimple.svg: Added. 14 1 15 2015-04-13 Zalan Bujtas <zalan@apple.com> 2 16 -
trunk/Source/WebCore/ChangeLog
r182824 r182828 1 2015-04-14 Said Abou-Hallawa <sabouhallawa@apple.com> 2 3 textPath layout performance improvement. 4 https://bugs.webkit.org/show_bug.cgi?id=141570. 5 6 Reviewed by Darin Adler. 7 8 The bottleneck of the text-on-path performance is the position and angle 9 calculations for every single character. If the number of characters is 10 'n' and the number of path elements is 'm', the total number of processing 11 the path elements is O(2 x n x m). What makes it really worse is, for every 12 curve we keep splitting the curve till the split curve is almost a straight 13 line. The changes we need to do are: 14 1. Merge the position and the angle traversals in one pass since they are 15 returning info for the same length on the path. There is a degenerate 16 case for the starting point when calculating the angle. The original 17 code was solving this problem by passing an epsilon instead of zero but 18 because traversing the path for position and angle are now merged, we 19 will pass zero for the starting point as is. All we need is to move one 20 step ahead without moving the position. We need the extra step forward 21 to calculate the slope of the path at the starting point. 22 2. We need to add a new mode to traversing a path. The new mode will take 23 a vector of lengths and returns a vector of arrow vectors. Every arrow 24 vector represents a position and an angle on the path at a certain length. 25 This requires changing the SVGTextLayoutEngine to calculate the lengths 26 of the characters on the curve first and then passing all of them to the 27 path traversal function. Instead of traversing the path for every length, 28 we are going to get the required point and angle from the vector of arrow 29 vectors. 30 31 This patch is addressing the first fix only. The second one will require 32 refactoring the SVGTextLayoutEngine so I am going to address it in a 33 different patch. 34 35 * platform/graphics/Path.cpp: 36 (WebCore::pathLengthApplierFunction): It is cleaner to move the function 37 of this method to PathTraversalState::processPathElement(). 38 39 (WebCore::Path::length): Use new enum Action value and access methods. 40 41 (WebCore::Path::traversalStateAtLength): New function which returns the 42 traversalState at a certain length on a path. 43 44 (WebCore::Path::pointAtLength): 45 (WebCore::Path::normalAngleAtLength): Use traversalStateAtLength() to get 46 the traversalState and from it return either the position or the angle. 47 48 * platform/graphics/Path.h: Define traversalStateAtLength(). 49 50 * platform/graphics/PathTraversalState.cpp: 51 (WebCore::distanceLine): Code clean up. 52 53 (WebCore::curveLength): Make the setting of m_previous and m_current happens 54 only in this function. 55 56 (WebCore::PathTraversalState::PathTraversalState): Add an optional parameter 57 for the desired length and move the initialization of the other members to 58 the class definition. 59 60 (WebCore::PathTraversalState::closeSubpath): 61 (WebCore::PathTraversalState::moveTo): 62 (WebCore::PathTraversalState::lineTo): Add the distance to the m_totalLength 63 instead of returning it since this is what all the callers were doing. 64 65 (WebCore::PathTraversalState::quadraticBezierTo): 66 (WebCore::PathTraversalState::cubicBezierTo): Add the distance to the 67 m_totalLength. Move the setting of m_previous and m_current to curveLength(). 68 Remove unused members m_control1 and m_control2. 69 70 (WebCore::PathTraversalState::processSegment): Deleted. 71 (WebCore::PathTraversalState::finalizeAppendPathElement): Create a new 72 name for the function. Handle the case of the angle at the starting point 73 where m_desiredLength is set to zero. The new flag m_isZeroVector will be 74 set to notify the caller that the next iteration will be the last one and 75 it is only needed for the calculating the angle of a zero vector. m_current 76 should not change by this last iteration. 77 78 (WebCore::PathTraversalState::appendPathElement): This code is moved from 79 pathLengthApplierFunction(). 80 81 (WebCore::PathTraversalState::processPathElement): This function is used 82 by the class Path. It is a wrapper for appendPathElement(). If m_isZeroVector 83 is set we append the new element to a copy for the PathTraversalState just 84 to get the angle for the zero vector. 85 86 * platform/graphics/PathTraversalState.h: Change the enum values to not 87 not include the class or the enum class. Make the data members private and 88 expose the needed ones through access methods. Make all the internal methods 89 to be private. 90 91 (WebCore::PathTraversalState::processPathElement): Another wrapper for 92 appendPathElement() which is used by SVGPathTraversalStateBuilder. 93 94 (WebCore::PathTraversalState::action): 95 (WebCore::PathTraversalState::setAction): 96 (WebCore::PathTraversalState::desiredLength): 97 (WebCore::PathTraversalState::setDesiredLength): 98 (WebCore::PathTraversalState::success): 99 (WebCore::PathTraversalState::totalLength): 100 (WebCore::PathTraversalState::current): 101 (WebCore::PathTraversalState::normalAngle): New access methods which are now 102 needed after making the data members private. 103 104 * rendering/svg/SVGRootInlineBox.cpp: 105 (WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes): Make the casting 106 of the renderer on the caller side. 107 108 * rendering/svg/SVGTextChunk.cpp: 109 (WebCore::SVGTextChunk::SVGTextChunk): The constructor should append the 110 elements of m_boxes instead of making this from outside the class. 111 112 (WebCore::SVGTextChunk::totalCharacters): 113 (WebCore::SVGTextChunk::totalLength): 114 (WebCore::SVGTextChunk::calculateLength): Deleted. 115 Replace calculateLength() by totalCharacters() and totalLength() to make 116 the interface cleaner. 117 118 (WebCore::SVGTextChunk::totalAnchorShift): 119 (WebCore::SVGTextChunk::calculateTextAnchorShift): Deleted. 120 Rename the function name. 121 122 (WebCore::SVGTextChunk::layout): 123 (WebCore::SVGTextChunk::processTextLengthSpacingCorrection): 124 (WebCore::SVGTextChunk::buildBoxTransformations): 125 (WebCore::SVGTextChunk::boxSpacingAndGlyphsTransform): 126 (WebCore::SVGTextChunk::processTextAnchorCorrection): 127 Move the chunk layout code from SVGTextChunkBuilder::layoutTextChunks() 128 to the SVGTextChunk::layout(). Move all the helper functions as well. 129 130 * rendering/svg/SVGTextChunk.h: 131 (WebCore::SVGTextChunk::hasTextAnchor): 132 (WebCore::SVGTextChunk::boxes): Deleted. 133 Add the new methods and change most of the public methods to be private. 134 135 * rendering/svg/SVGTextChunkBuilder.cpp: 136 (WebCore::SVGTextChunkBuilder::totalCharacters): 137 (WebCore::SVGTextChunkBuilder::totalLength): 138 (WebCore::SVGTextChunkBuilder::totalAnchorShift): This code is moved from 139 SVGTextLayoutEngine. It scans the boxes stored in the SVGTextChunkBuilder 140 and sums up the total values. 141 142 (WebCore::SVGTextChunkBuilder::transformationForTextBox): 143 (WebCore::SVGTextChunkBuilder::buildTextChunks): 144 (WebCore::SVGTextChunkBuilder::layoutTextChunks): Code clean up. 145 146 (WebCore::SVGTextChunkBuilder::addTextChunk): Deleted. 147 (WebCore::SVGTextChunkBuilder::processTextChunk): Deleted. 148 (WebCore::SVGTextChunkBuilder::processTextLengthSpacingCorrection): Deleted. 149 (WebCore::SVGTextChunkBuilder::processTextAnchorCorrection): Deleted. 150 (WebCore::SVGTextChunkBuilder::buildSpacingAndGlyphsTransform): Deleted. 151 This code now lives in SVGTextChunk. 152 153 * rendering/svg/SVGTextChunkBuilder.h: Add new methods for code which was 154 moved from SVGTextLayoutEngine and remove methods for code which was removed 155 to SVGTextChunk. 156 157 * rendering/svg/SVGTextLayoutEngine.cpp: 158 (WebCore::SVGTextLayoutEngine::beginTextPathLayout): Use the sum up methods 159 from SVGTextChunkBuilder instead of looping through the chunks. Also get a 160 clean order for defining variables and doing the calculations. 161 162 (WebCore::SVGTextLayoutEngine::finalizeTransformMatrices): Code clean up. 163 164 (WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath): Do a single path 165 traversal to get the position and the angle for a length on a path. 166 167 * svg/SVGAnimateMotionElement.cpp: 168 (WebCore::SVGAnimateMotionElement::buildTransformForProgress): Do a single 169 path traversal to get the position and the angle at a length on a path. 170 171 * svg/SVGPathTraversalStateBuilder.cpp: 172 (WebCore::SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder): 173 (WebCore::SVGPathTraversalStateBuilder::moveTo): 174 (WebCore::SVGPathTraversalStateBuilder::lineTo): 175 (WebCore::SVGPathTraversalStateBuilder::curveToCubic): 176 (WebCore::SVGPathTraversalStateBuilder::closePath): 177 (WebCore::SVGPathTraversalStateBuilder::setDesiredLength): 178 (WebCore::SVGPathTraversalStateBuilder::continueConsuming): 179 (WebCore::SVGPathTraversalStateBuilder::totalLength): 180 (WebCore::SVGPathTraversalStateBuilder::currentPoint): 181 (WebCore::SVGPathTraversalStateBuilder::incrementPathSegmentCount): Deleted. 182 (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): Deleted. 183 * svg/SVGPathTraversalStateBuilder.h: 184 (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): 185 Code clean up. 186 187 * svg/SVGPathUtilities.cpp: 188 (WebCore::getSVGPathSegAtLengthFromSVGPathByteStream): 189 (WebCore::getTotalLengthOfSVGPathByteStream): 190 (WebCore::getPointAtLengthOfSVGPathByteStream): Use new TraversalState::Action 191 enum values. 192 1 193 2015-04-14 Simon Fraser <simon.fraser@apple.com> 2 194 -
trunk/Source/WebCore/platform/graphics/Path.cpp
r165676 r182828 43 43 { 44 44 PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info); 45 if (traversalState.m_success) 46 return; 47 FloatPoint* points = element->points; 48 float segmentLength = 0; 49 switch (element->type) { 50 case PathElementMoveToPoint: 51 segmentLength = traversalState.moveTo(points[0]); 52 break; 53 case PathElementAddLineToPoint: 54 segmentLength = traversalState.lineTo(points[0]); 55 break; 56 case PathElementAddQuadCurveToPoint: 57 segmentLength = traversalState.quadraticBezierTo(points[0], points[1]); 58 break; 59 case PathElementAddCurveToPoint: 60 segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]); 61 break; 62 case PathElementCloseSubpath: 63 segmentLength = traversalState.closeSubpath(); 64 break; 65 } 66 traversalState.m_totalLength += segmentLength; 67 traversalState.processSegment(); 45 traversalState.processPathElement(element); 68 46 } 69 47 70 48 float Path::length() const 71 49 { 72 PathTraversalState traversalState(PathTraversalState:: TraversalTotalLength);50 PathTraversalState traversalState(PathTraversalState::Action::TotalLength); 73 51 apply(&traversalState, pathLengthApplierFunction); 74 return traversalState. m_totalLength;52 return traversalState.totalLength(); 75 53 } 76 54 77 FloatPoint Path::pointAtLength(float length, bool& ok) const55 PathTraversalState Path::traversalStateAtLength(float length, bool& success) const 78 56 { 79 PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength); 80 traversalState.m_desiredLength = length; 57 PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length); 81 58 apply(&traversalState, pathLengthApplierFunction); 82 ok = traversalState.m_success;83 return traversalState .m_current;59 success = traversalState.success(); 60 return traversalState; 84 61 } 85 62 86 float Path::normalAngleAtLength(float length, bool& ok) const63 FloatPoint Path::pointAtLength(float length, bool& success) const 87 64 { 88 PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength); 89 traversalState.m_desiredLength = length ? length : std::numeric_limits<float>::epsilon(); 90 apply(&traversalState, pathLengthApplierFunction); 91 ok = traversalState.m_success; 92 return traversalState.m_normalAngle; 65 return traversalStateAtLength(length, success).current(); 66 } 67 68 float Path::normalAngleAtLength(float length, bool& success) const 69 { 70 return traversalStateAtLength(length, success).normalAngle(); 93 71 } 94 72 -
trunk/Source/WebCore/platform/graphics/Path.h
r180882 r182828 61 61 class FloatSize; 62 62 class GraphicsContext; 63 class PathTraversalState; 63 64 class RoundedRect; 64 65 class StrokeStyleApplier; … … 101 102 FloatRect fastBoundingRect() const; 102 103 FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const; 103 104 104 105 float length() const; 105 FloatPoint pointAtLength(float length, bool& ok) const; 106 float normalAngleAtLength(float length, bool& ok) const; 106 PathTraversalState traversalStateAtLength(float length, bool& success) const; 107 FloatPoint pointAtLength(float length, bool& success) const; 108 float normalAngleAtLength(float length, bool& success) const; 107 109 108 110 WEBCORE_EXPORT void clear(); -
trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp
r138800 r182828 1 1 /* 2 2 * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 35 36 static inline float distanceLine(const FloatPoint& start, const FloatPoint& end) 36 37 { 37 return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y())); 38 float dx = end.x() - start.x(); 39 float dy = end.y() - start.y(); 40 return sqrtf(dx * dx + dy * dy); 38 41 } 39 42 … … 115 118 // approximateDistance() + current total distance > desired distance 116 119 template<class CurveType> 117 static float curveLength( PathTraversalState& traversalState, CurveType curve)120 static float curveLength(const PathTraversalState& traversalState, const CurveType& originalCurve, FloatPoint& previous, FloatPoint& current) 118 121 { 119 122 static const unsigned curveStackDepthLimit = 20; 120 121 Vector<CurveType> curveStack; 122 curveStack.append(curve); 123 123 CurveType curve = originalCurve; 124 Vector<CurveType, curveStackDepthLimit> curveStack; 124 125 float totalLength = 0; 125 do { 126 127 while (true) { 126 128 float length = curve.approximateDistance(); 127 if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() <= curveStackDepthLimit) { 129 130 if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() < curveStackDepthLimit) { 128 131 CurveType leftCurve; 129 132 CurveType rightCurve; … … 131 134 curve = leftCurve; 132 135 curveStack.append(rightCurve); 133 } else { 134 totalLength += length; 135 if (traversalState.m_action == PathTraversalState::TraversalPointAtLength 136 || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) { 137 traversalState.m_previous = curve.start; 138 traversalState.m_current = curve.end; 139 if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength) 140 return totalLength; 141 } 142 curve = curveStack.last(); 143 curveStack.removeLast(); 136 continue; 144 137 } 145 } while (!curveStack.isEmpty()); 146 138 139 totalLength += length; 140 if (traversalState.action() == PathTraversalState::Action::VectorAtLength) { 141 previous = curve.start; 142 current = curve.end; 143 if (traversalState.totalLength() + totalLength > traversalState.desiredLength()) 144 break; 145 } 146 147 if (curveStack.isEmpty()) 148 break; 149 150 curve = curveStack.last(); 151 curveStack.removeLast(); 152 } 153 154 if (traversalState.action() != PathTraversalState::Action::VectorAtLength) { 155 ASSERT(curve.end == originalCurve.end); 156 previous = curve.start; 157 current = curve.end; 158 } 159 147 160 return totalLength; 148 161 } 149 162 150 PathTraversalState::PathTraversalState( PathTraversalAction action)163 PathTraversalState::PathTraversalState(Action action, float desiredLength) 151 164 : m_action(action) 152 , m_success(false) 153 , m_totalLength(0) 154 , m_segmentIndex(0) 155 , m_desiredLength(0) 156 , m_normalAngle(0) 157 { 158 } 159 160 float PathTraversalState::closeSubpath() 161 { 162 float distance = distanceLine(m_current, m_start); 163 m_current = m_control1 = m_control2 = m_start; 164 return distance; 165 } 166 167 float PathTraversalState::moveTo(const FloatPoint& point) 168 { 169 m_current = m_start = m_control1 = m_control2 = point; 170 return 0; 171 } 172 173 float PathTraversalState::lineTo(const FloatPoint& point) 174 { 175 float distance = distanceLine(m_current, point); 176 m_current = m_control1 = m_control2 = point; 177 return distance; 178 } 179 180 float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd) 181 { 182 float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd)); 183 184 m_control1 = newControl; 185 m_control2 = newEnd; 186 187 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) 188 m_current = newEnd; 189 190 return distance; 191 } 192 193 float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd) 194 { 195 float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd)); 196 197 m_control1 = newEnd; 198 m_control2 = newControl2; 199 200 if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) 201 m_current = newEnd; 202 203 return distance; 204 } 205 206 void PathTraversalState::processSegment() 207 { 208 if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength) 209 m_success = true; 210 211 if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) && m_totalLength >= m_desiredLength) { 165 , m_desiredLength(desiredLength) 166 { 167 ASSERT(action != Action::TotalLength || !desiredLength); 168 } 169 170 void PathTraversalState::closeSubpath() 171 { 172 m_totalLength += distanceLine(m_current, m_start); 173 m_current = m_start; 174 } 175 176 void PathTraversalState::moveTo(const FloatPoint& point) 177 { 178 m_previous = m_current = m_start = point; 179 } 180 181 void PathTraversalState::lineTo(const FloatPoint& point) 182 { 183 m_totalLength += distanceLine(m_current, point); 184 m_current = point; 185 } 186 187 void PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd) 188 { 189 m_totalLength += curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current); 190 } 191 192 void PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd) 193 { 194 m_totalLength += curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd), m_previous, m_current); 195 } 196 197 bool PathTraversalState::finalizeAppendPathElement() 198 { 199 if (m_action == Action::TotalLength) 200 return false; 201 202 if (m_action == Action::SegmentAtLength) { 203 if (m_totalLength >= m_desiredLength) 204 m_success = true; 205 return m_success; 206 } 207 208 ASSERT(m_action == Action::VectorAtLength); 209 210 if (m_totalLength >= m_desiredLength) { 212 211 float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); 213 if (m_action == TraversalPointAtLength) { 214 float offset = m_desiredLength - m_totalLength; 215 m_current.move(offset * cosf(slope), offset * sinf(slope)); 216 } else 212 float offset = m_desiredLength - m_totalLength; 213 m_current.move(offset * cosf(slope), offset * sinf(slope)); 214 215 if (!m_isZeroVector && !m_desiredLength) 216 m_isZeroVector = true; 217 else { 218 m_success = true; 217 219 m_normalAngle = rad2deg(slope); 218 m_success = true; 219 } 220 } 221 } 222 220 223 m_previous = m_current; 221 } 222 223 } 224 224 return m_success; 225 } 226 227 bool PathTraversalState::appendPathElement(PathElementType type, const FloatPoint* points) 228 { 229 switch (type) { 230 case PathElementMoveToPoint: 231 moveTo(points[0]); 232 break; 233 case PathElementAddLineToPoint: 234 lineTo(points[0]); 235 break; 236 case PathElementAddQuadCurveToPoint: 237 quadraticBezierTo(points[0], points[1]); 238 break; 239 case PathElementAddCurveToPoint: 240 cubicBezierTo(points[0], points[1], points[2]); 241 break; 242 case PathElementCloseSubpath: 243 closeSubpath(); 244 break; 245 } 246 247 return finalizeAppendPathElement(); 248 } 249 250 bool PathTraversalState::processPathElement(PathElementType type, const FloatPoint* points) 251 { 252 if (m_success) 253 return true; 254 255 if (m_isZeroVector) { 256 PathTraversalState traversalState(*this); 257 m_success = traversalState.appendPathElement(type, points); 258 m_normalAngle = traversalState.m_normalAngle; 259 return m_success; 260 } 261 262 return appendPathElement(type, points); 263 } 264 265 } 266 -
trunk/Source/WebCore/platform/graphics/PathTraversalState.h
r165676 r182828 1 1 /* 2 2 * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 28 29 29 30 #include "FloatPoint.h" 31 #include "Path.h" 30 32 31 33 namespace WebCore { … … 33 35 class PathTraversalState { 34 36 public: 35 enum PathTraversalAction { 36 TraversalTotalLength, 37 TraversalPointAtLength, 38 TraversalSegmentAtLength, 39 TraversalNormalAngleAtLength 37 enum class Action { 38 TotalLength, 39 VectorAtLength, 40 SegmentAtLength, 40 41 }; 41 42 42 PathTraversalState(PathTraversalAction); 43 44 float closeSubpath(); 45 float moveTo(const FloatPoint&); 46 float lineTo(const FloatPoint&); 47 float quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd); 48 float cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd); 49 50 void processSegment(); 43 PathTraversalState(Action, float desiredLength = 0); 51 44 52 45 public: 53 PathTraversalAction m_action; 54 bool m_success; 46 bool processPathElement(PathElementType, const FloatPoint*); 47 bool processPathElement(const PathElement* element) { return processPathElement(element->type, element->points); } 48 49 Action action() const { return m_action; } 50 void setAction(Action action) { m_action = action; } 51 float desiredLength() const { return m_desiredLength; } 52 void setDesiredLength(float desiredLength) { m_desiredLength = desiredLength; } 53 54 // Traversing output -- should be read only 55 bool success() const { return m_success; } 56 float totalLength() const { return m_totalLength; } 57 FloatPoint current() const { return m_current; } 58 float normalAngle() const { return m_normalAngle; } 59 60 private: 61 void closeSubpath(); 62 void moveTo(const FloatPoint&); 63 void lineTo(const FloatPoint&); 64 void quadraticBezierTo(const FloatPoint&, const FloatPoint&); 65 void cubicBezierTo(const FloatPoint&, const FloatPoint&, const FloatPoint&); 66 67 bool finalizeAppendPathElement(); 68 bool appendPathElement(PathElementType, const FloatPoint*); 69 70 private: 71 Action m_action; 72 bool m_success { false }; 55 73 56 74 FloatPoint m_current; 57 75 FloatPoint m_start; 58 FloatPoint m_control1;59 FloatPoint m_control2;60 76 61 float m_totalLength; 62 unsigned m_segmentIndex; 63 float m_desiredLength; 77 float m_totalLength { 0 }; 78 float m_desiredLength { 0 }; 64 79 65 80 // For normal calculations 66 81 FloatPoint m_previous; 67 float m_normalAngle; // degrees 68 }; 82 float m_normalAngle { 0 }; // degrees 83 bool m_isZeroVector { false }; 84 }; 69 85 } 70 86 -
trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp
r163440 r182828 1 1 /* 2 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 25 26 namespace WebCore { 26 27 27 SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength) 28 : m_chunkStyle(chunkStyle) 29 , m_desiredTextLength(desiredTextLength) 30 { 31 } 32 33 void SVGTextChunk::calculateLength(float& length, unsigned& characters) const 34 { 35 SVGTextFragment* lastFragment = 0; 36 37 unsigned boxCount = m_boxes.size(); 38 for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { 39 SVGInlineTextBox* textBox = m_boxes.at(boxPosition); 40 Vector<SVGTextFragment>& fragments = textBox->textFragments(); 41 42 unsigned size = fragments.size(); 43 if (!size) 44 continue; 45 46 for (unsigned i = 0; i < size; ++i) { 47 SVGTextFragment& fragment = fragments.at(i); 28 SVGTextChunk::SVGTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned first, unsigned limit) 29 { 30 ASSERT(first < limit); 31 ASSERT(first >= 0 && limit <= lineLayoutBoxes.size()); 32 33 const SVGInlineTextBox* box = lineLayoutBoxes[first]; 34 const RenderStyle& style = box->renderer().style(); 35 const SVGRenderStyle& svgStyle = style.svgStyle(); 36 37 if (!style.isLeftToRightDirection()) 38 m_chunkStyle |= SVGTextChunk::RightToLeftText; 39 40 if (svgStyle.isVerticalWritingMode()) 41 m_chunkStyle |= SVGTextChunk::VerticalText; 42 43 switch (svgStyle.textAnchor()) { 44 case TA_START: 45 break; 46 case TA_MIDDLE: 47 m_chunkStyle |= MiddleAnchor; 48 break; 49 case TA_END: 50 m_chunkStyle |= EndAnchor; 51 break; 52 } 53 54 if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box->renderer().parent())) { 55 SVGLengthContext lengthContext(textContentElement); 56 m_desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext); 57 58 switch (textContentElement->lengthAdjust()) { 59 case SVGLengthAdjustUnknown: 60 break; 61 case SVGLengthAdjustSpacing: 62 m_chunkStyle |= LengthAdjustSpacing; 63 break; 64 case SVGLengthAdjustSpacingAndGlyphs: 65 m_chunkStyle |= LengthAdjustSpacingAndGlyphs; 66 break; 67 } 68 } 69 70 for (unsigned i = first; i < limit; ++i) 71 m_boxes.append(lineLayoutBoxes[i]); 72 } 73 74 unsigned SVGTextChunk::totalCharacters() const 75 { 76 unsigned characters = 0; 77 for (auto* box : m_boxes) { 78 for (auto& fragment : box->textFragments()) 48 79 characters += fragment.length; 49 50 if (m_chunkStyle & VerticalText) 51 length += fragment.height; 52 else 53 length += fragment.width; 54 55 if (!lastFragment) { 56 lastFragment = &fragment; 57 continue; 58 } 59 60 // Resepect gap between chunks. 61 if (m_chunkStyle & VerticalText) 62 length += fragment.y - (lastFragment->y + lastFragment->height); 63 else 64 length += fragment.x - (lastFragment->x + lastFragment->width); 65 66 lastFragment = &fragment; 67 } 68 } 69 } 70 71 float SVGTextChunk::calculateTextAnchorShift(float length) const 72 { 80 } 81 return characters; 82 } 83 84 float SVGTextChunk::totalLength() const 85 { 86 const SVGTextFragment* firstFragment = nullptr; 87 const SVGTextFragment* lastFragment = nullptr; 88 89 for (auto* box : m_boxes) { 90 auto& fragments = box->textFragments(); 91 if (fragments.size()) { 92 firstFragment = &(*fragments.begin()); 93 break; 94 } 95 } 96 97 for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) { 98 auto& fragments = (*it)->textFragments(); 99 if (fragments.size()) { 100 lastFragment = &(*fragments.rbegin()); 101 break; 102 } 103 } 104 105 ASSERT(!firstFragment == !lastFragment); 106 if (!firstFragment) 107 return 0; 108 109 if (m_chunkStyle & VerticalText) 110 return (lastFragment->y + lastFragment->height) - firstFragment->y; 111 112 return (lastFragment->x + lastFragment->width) - firstFragment->x; 113 } 114 115 float SVGTextChunk::totalAnchorShift() const 116 { 117 float length = totalLength(); 73 118 if (m_chunkStyle & MiddleAnchor) 74 119 return -length / 2; … … 78 123 } 79 124 125 void SVGTextChunk::layout(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const 126 { 127 if (hasDesiredTextLength()) { 128 if (hasLengthAdjustSpacing()) 129 processTextLengthSpacingCorrection(); 130 else { 131 ASSERT(hasLengthAdjustSpacingAndGlyphs()); 132 buildBoxTransformations(textBoxTransformations); 133 } 134 } 135 136 if (hasTextAnchor()) 137 processTextAnchorCorrection(); 138 } 139 140 void SVGTextChunk::processTextLengthSpacingCorrection() const 141 { 142 float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters(); 143 bool isVerticalText = m_chunkStyle & VerticalText; 144 unsigned atCharacter = 0; 145 146 for (auto* box : m_boxes) { 147 for (auto& fragment : box->textFragments()) { 148 if (isVerticalText) 149 fragment.y += textLengthShift * atCharacter; 150 else 151 fragment.x += textLengthShift * atCharacter; 152 153 atCharacter += fragment.length; 154 } 155 } 156 } 157 158 void SVGTextChunk::buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const 159 { 160 AffineTransform spacingAndGlyphsTransform; 161 bool foundFirstFragment = false; 162 163 for (auto* box : m_boxes) { 164 if (!foundFirstFragment) { 165 if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform)) 166 continue; 167 foundFirstFragment = true; 168 } 169 170 textBoxTransformations.set(box, spacingAndGlyphsTransform); 171 } 172 } 173 174 bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform& spacingAndGlyphsTransform) const 175 { 176 auto& fragments = box->textFragments(); 177 if (fragments.isEmpty()) 178 return false; 179 180 const SVGTextFragment& fragment = fragments.first(); 181 float scale = desiredTextLength() / totalLength(); 182 183 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); 184 185 if (m_chunkStyle & VerticalText) 186 spacingAndGlyphsTransform.scaleNonUniform(1, scale); 187 else 188 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); 189 190 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); 191 return true; 192 } 193 194 void SVGTextChunk::processTextAnchorCorrection() const 195 { 196 float textAnchorShift = totalAnchorShift(); 197 bool isVerticalText = m_chunkStyle & VerticalText; 198 199 for (auto* box : m_boxes) { 200 for (auto& fragment : box->textFragments()) { 201 if (isVerticalText) 202 fragment.y += textAnchorShift; 203 else 204 fragment.x += textAnchorShift; 205 } 206 } 207 } 208 80 209 } // namespace WebCore -
trunk/Source/WebCore/rendering/svg/SVGTextChunk.h
r163440 r182828 1 1 /* 2 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 41 42 }; 42 43 43 SVGTextChunk( unsigned chunkStyle, float desiredTextLength);44 SVGTextChunk(const Vector<SVGInlineTextBox*>&, unsigned first, unsigned limit); 44 45 45 void calculateLength(float& length, unsigned& characters) const; 46 float calculateTextAnchorShift(float length) const; 46 unsigned totalCharacters() const; 47 float totalLength() const; 48 float totalAnchorShift() const; 49 void layout(HashMap<SVGInlineTextBox*, AffineTransform>&) const; 50 51 private: 52 void processTextAnchorCorrection() const; 53 void buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>&) const; 54 void processTextLengthSpacingCorrection() const; 47 55 48 56 bool isVerticalText() const { return m_chunkStyle & VerticalText; } 49 57 float desiredTextLength() const { return m_desiredTextLength; } 50 58 51 Vector<SVGInlineTextBox*>& boxes() { return m_boxes; }52 const Vector<SVGInlineTextBox*>& boxes() const { return m_boxes; }53 54 59 bool hasDesiredTextLength() const { return m_desiredTextLength > 0 && ((m_chunkStyle & LengthAdjustSpacing) || (m_chunkStyle & LengthAdjustSpacingAndGlyphs)); } 55 bool hasTextAnchor() const { return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & MiddleAnchor) || (m_chunkStyle & EndAnchor); }60 bool hasTextAnchor() const { return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & (MiddleAnchor | EndAnchor)); } 56 61 bool hasLengthAdjustSpacing() const { return m_chunkStyle & LengthAdjustSpacing; } 57 62 bool hasLengthAdjustSpacingAndGlyphs() const { return m_chunkStyle & LengthAdjustSpacingAndGlyphs; } 63 64 bool boxSpacingAndGlyphsTransform(const SVGInlineTextBox*, AffineTransform&) const; 58 65 59 66 private: … … 61 68 Vector<SVGInlineTextBox*> m_boxes; 62 69 63 unsigned m_chunkStyle ;64 float m_desiredTextLength ;70 unsigned m_chunkStyle { DefaultStyle }; 71 float m_desiredTextLength { 0 }; 65 72 }; 66 73 -
trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
r165607 r182828 1 1 /* 2 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 31 32 } 32 33 33 void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const34 unsigned SVGTextChunkBuilder::totalCharacters() const 34 35 { 35 DEPRECATED_DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ()); 36 if (!m_textBoxTransformations.contains(textBox)) { 37 transform = s_identityTransform; 38 return; 39 } 40 41 transform = m_textBoxTransformations.get(textBox); 36 unsigned characters = 0; 37 for (const auto& chunk : m_textChunks) 38 characters += chunk.totalCharacters(); 39 return characters; 42 40 } 43 41 44 void SVGTextChunkBuilder::buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes) 42 float SVGTextChunkBuilder::totalLength() const 43 { 44 float length = 0; 45 for (const auto& chunk : m_textChunks) 46 length += chunk.totalLength(); 47 return length; 48 } 49 50 float SVGTextChunkBuilder::totalAnchorShift() const 51 { 52 float anchorShift = 0; 53 for (const auto& chunk : m_textChunks) 54 anchorShift += chunk.totalAnchorShift(); 55 return anchorShift; 56 } 57 58 AffineTransform SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox) const 59 { 60 auto it = m_textBoxTransformations.find(textBox); 61 return it == m_textBoxTransformations.end() ? AffineTransform() : it->value; 62 } 63 64 void SVGTextChunkBuilder::buildTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes) 45 65 { 46 66 if (lineLayoutBoxes.isEmpty()) 47 67 return; 48 68 49 bool foundStart = false; 50 unsigned lastChunkStartPosition = 0; 51 unsigned boxPosition = 0; 52 unsigned boxCount = lineLayoutBoxes.size(); 53 for (; boxPosition < boxCount; ++boxPosition) { 54 SVGInlineTextBox* textBox = lineLayoutBoxes[boxPosition]; 55 if (!textBox->startsNewTextChunk()) 69 unsigned limit = lineLayoutBoxes.size(); 70 unsigned first = limit; 71 72 for (unsigned i = 0; i < limit; ++i) { 73 if (!lineLayoutBoxes[i]->startsNewTextChunk()) 56 74 continue; 57 75 58 if (!foundStart) { 59 lastChunkStartPosition = boxPosition; 60 foundStart = true; 61 } else { 62 ASSERT_WITH_SECURITY_IMPLICATION(boxPosition > lastChunkStartPosition); 63 addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition); 64 lastChunkStartPosition = boxPosition; 76 if (first == limit) 77 first = i; 78 else { 79 ASSERT_WITH_SECURITY_IMPLICATION(first != i); 80 m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, i)); 81 first = i; 65 82 } 66 83 } 67 84 68 if (!foundStart) 69 return; 70 71 if (boxPosition - lastChunkStartPosition > 0) 72 addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition); 85 if (first != limit) 86 m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, limit)); 73 87 } 74 88 75 void SVGTextChunkBuilder::layoutTextChunks( Vector<SVGInlineTextBox*>& lineLayoutBoxes)89 void SVGTextChunkBuilder::layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes) 76 90 { 77 91 buildTextChunks(lineLayoutBoxes); … … 79 93 return; 80 94 81 unsigned chunkCount = m_textChunks.size(); 82 for (unsigned i = 0; i < chunkCount; ++i) 83 processTextChunk(m_textChunks[i]); 95 for (const auto& chunk : m_textChunks) 96 chunk.layout(m_textBoxTransformations); 84 97 85 98 m_textChunks.clear(); 86 99 } 87 100 88 void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)89 {90 SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart];91 ASSERT(textBox);92 93 const RenderStyle& style = textBox->renderer().style();94 95 const SVGRenderStyle& svgStyle = style.svgStyle();96 97 // Build chunk style flags.98 unsigned chunkStyle = SVGTextChunk::DefaultStyle;99 100 // Handle 'direction' property.101 if (!style.isLeftToRightDirection())102 chunkStyle |= SVGTextChunk::RightToLeftText;103 104 // Handle 'writing-mode' property.105 if (svgStyle.isVerticalWritingMode())106 chunkStyle |= SVGTextChunk::VerticalText;107 108 // Handle 'text-anchor' property.109 switch (svgStyle.textAnchor()) {110 case TA_START:111 break;112 case TA_MIDDLE:113 chunkStyle |= SVGTextChunk::MiddleAnchor;114 break;115 case TA_END:116 chunkStyle |= SVGTextChunk::EndAnchor;117 break;118 };119 120 // Handle 'lengthAdjust' property.121 float desiredTextLength = 0;122 if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textBox->renderer().parent())) {123 SVGLengthContext lengthContext(textContentElement);124 desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);125 126 switch (textContentElement->lengthAdjust()) {127 case SVGLengthAdjustUnknown:128 break;129 case SVGLengthAdjustSpacing:130 chunkStyle |= SVGTextChunk::LengthAdjustSpacing;131 break;132 case SVGLengthAdjustSpacingAndGlyphs:133 chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs;134 break;135 };136 }137 138 SVGTextChunk chunk(chunkStyle, desiredTextLength);139 140 Vector<SVGInlineTextBox*>& boxes = chunk.boxes();141 for (unsigned i = boxStart; i < boxStart + boxCount; ++i)142 boxes.append(lineLayoutBoxes[i]);143 144 m_textChunks.append(chunk);145 101 } 146 147 void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)148 {149 bool processTextLength = chunk.hasDesiredTextLength();150 bool processTextAnchor = chunk.hasTextAnchor();151 if (!processTextAnchor && !processTextLength)152 return;153 154 const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();155 unsigned boxCount = boxes.size();156 if (!boxCount)157 return;158 159 // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).160 float chunkLength = 0;161 unsigned chunkCharacters = 0;162 chunk.calculateLength(chunkLength, chunkCharacters);163 164 bool isVerticalText = chunk.isVerticalText();165 if (processTextLength) {166 if (chunk.hasLengthAdjustSpacing()) {167 float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;168 unsigned atCharacter = 0;169 for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {170 Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();171 if (fragments.isEmpty())172 continue;173 processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);174 }175 } else {176 ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs());177 float textLengthScale = chunk.desiredTextLength() / chunkLength;178 AffineTransform spacingAndGlyphsTransform;179 180 bool foundFirstFragment = false;181 for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {182 SVGInlineTextBox* textBox = boxes[boxPosition];183 Vector<SVGTextFragment>& fragments = textBox->textFragments();184 if (fragments.isEmpty())185 continue;186 187 if (!foundFirstFragment) {188 foundFirstFragment = true;189 buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform);190 }191 192 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);193 }194 }195 }196 197 if (!processTextAnchor)198 return;199 200 // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.201 if (processTextLength && chunk.hasLengthAdjustSpacing()) {202 chunkLength = 0;203 chunkCharacters = 0;204 chunk.calculateLength(chunkLength, chunkCharacters);205 }206 207 float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);208 for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {209 Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();210 if (fragments.isEmpty())211 continue;212 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);213 }214 }215 216 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)217 {218 unsigned fragmentCount = fragments.size();219 for (unsigned i = 0; i < fragmentCount; ++i) {220 SVGTextFragment& fragment = fragments[i];221 222 if (isVerticalText)223 fragment.y += textLengthShift * atCharacter;224 else225 fragment.x += textLengthShift * atCharacter;226 227 atCharacter += fragment.length;228 }229 }230 231 void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments)232 {233 unsigned fragmentCount = fragments.size();234 for (unsigned i = 0; i < fragmentCount; ++i) {235 SVGTextFragment& fragment = fragments[i];236 237 if (isVerticalText)238 fragment.y += textAnchorShift;239 else240 fragment.x += textAnchorShift;241 }242 }243 244 void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)245 {246 spacingAndGlyphsTransform.translate(fragment.x, fragment.y);247 248 if (isVerticalText)249 spacingAndGlyphsTransform.scaleNonUniform(1, scale);250 else251 spacingAndGlyphsTransform.scaleNonUniform(scale, 1);252 253 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);254 }255 256 } -
trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h
r163440 r182828 1 1 /* 2 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * Copyright (C) 2015 Apple Inc. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 41 42 42 43 const Vector<SVGTextChunk>& textChunks() const { return m_textChunks; } 43 void transformationForTextBox(SVGInlineTextBox*, AffineTransform&) const; 44 unsigned totalCharacters() const; 45 float totalLength() const; 46 float totalAnchorShift() const; 47 AffineTransform transformationForTextBox(SVGInlineTextBox*) const; 44 48 45 void buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes); 46 void layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes); 47 48 private: 49 void addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxPosition, unsigned boxCount); 50 void processTextChunk(const SVGTextChunk&); 51 52 void processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>&, unsigned& atCharacter); 53 void processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>&); 54 void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&, AffineTransform&); 49 void buildTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes); 50 void layoutTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes); 55 51 56 52 private: -
trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
r178510 r182828 21 21 #include "SVGTextLayoutEngine.h" 22 22 23 #include "PathTraversalState.h" 23 24 #include "RenderSVGTextPath.h" 24 25 #include "SVGElement.h" … … 171 172 if (m_textPath.isEmpty()) 172 173 return; 174 173 175 m_textPathStartOffset = textPath.startOffset(); 174 176 m_textPathLength = m_textPath.length(); … … 176 178 m_textPathStartOffset *= m_textPathLength; 177 179 178 float totalLength = 0;179 unsigned totalCharacters = 0;180 181 180 lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes); 182 const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks(); 183 184 unsigned size = textChunks.size(); 185 for (unsigned i = 0; i < size; ++i) { 186 const SVGTextChunk& chunk = textChunks.at(i); 187 188 float length = 0; 189 unsigned characters = 0; 190 chunk.calculateLength(length, characters); 191 192 // Handle text-anchor as additional start offset for text paths. 193 m_textPathStartOffset += chunk.calculateTextAnchorShift(length); 194 195 totalLength += length; 196 totalCharacters += characters; 197 } 198 181 182 // Handle text-anchor as additional start offset for text paths. 183 m_textPathStartOffset += lineLayout.m_chunkLayoutBuilder.totalAnchorShift(); 199 184 m_textPathCurrentOffset = m_textPathStartOffset; 200 185 201 186 // Eventually handle textLength adjustments. 202 SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; 203 float desiredTextLength = 0; 204 205 if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath)) { 206 SVGLengthContext lengthContext(textContentElement); 207 lengthAdjust = textContentElement->lengthAdjust(); 208 desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext); 209 } 210 187 auto* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath); 188 if (!textContentElement) 189 return; 190 191 SVGLengthContext lengthContext(textContentElement); 192 float desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext); 211 193 if (!desiredTextLength) 212 194 return; 213 195 214 if (lengthAdjust == SVGLengthAdjustSpacing) 196 float totalLength = lineLayout.m_chunkLayoutBuilder.totalLength(); 197 unsigned totalCharacters = lineLayout.m_chunkLayoutBuilder.totalCharacters(); 198 199 if (textContentElement->lengthAdjust() == SVGLengthAdjustSpacing) 215 200 m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters; 216 201 else … … 291 276 unsigned fragmentCount = fragments.size(); 292 277 for (unsigned i = 0; i < fragmentCount; ++i) { 293 m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);278 textBoxTransformation = m_chunkLayoutBuilder.transformationForTextBox(textBox); 294 279 if (textBoxTransformation.isIdentity()) 295 280 continue; … … 554 539 break; 555 540 556 bool ok = false; 557 FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok); 558 ASSERT(ok); 559 541 bool success = false; 542 auto traversalState(m_textPath.traversalStateAtLength(textPathOffset, success)); 543 ASSERT(success); 544 545 FloatPoint point = traversalState.current(); 560 546 x = point.x(); 561 547 y = point.y(); 562 angle = m_textPath.normalAngleAtLength(textPathOffset, ok); 563 ASSERT(ok);548 549 angle = traversalState.normalAngle(); 564 550 565 551 // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle! -
trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp
r182121 r182828 25 25 #include "AffineTransform.h" 26 26 #include "ElementIterator.h" 27 #include "PathTraversalState.h" 27 28 #include "RenderSVGResource.h" 28 29 #include "SVGImageElement.h" … … 213 214 ASSERT(!m_animationPath.isEmpty()); 214 215 215 bool ok= false;216 bool success = false; 216 217 float positionOnPath = m_animationPath.length() * percentage; 217 FloatPoint position = m_animationPath.pointAtLength(positionOnPath, ok); 218 if (!ok) 219 return; 218 auto traversalState(m_animationPath.traversalStateAtLength(positionOnPath, success)); 219 if (!success) 220 return; 221 222 FloatPoint position = traversalState.current(); 223 float angle = traversalState.normalAngle(); 224 220 225 transform->translate(position.x(), position.y()); 221 226 RotateMode rotateMode = this->rotateMode(); 222 227 if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse) 223 228 return; 224 float angle = m_animationPath.normalAngleAtLength(positionOnPath, ok);225 229 if (rotateMode == RotateAutoReverse) 226 230 angle += 180; -
trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp
r163440 r182828 30 30 SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder() 31 31 : m_traversalState(0) 32 , m_segmentIndex(0) 32 33 { 33 34 } … … 36 37 { 37 38 ASSERT(m_traversalState); 38 m_traversalState-> m_totalLength += m_traversalState->moveTo(targetPoint);39 m_traversalState->processPathElement(PathElementMoveToPoint, &targetPoint); 39 40 } 40 41 … … 42 43 { 43 44 ASSERT(m_traversalState); 44 m_traversalState-> m_totalLength += m_traversalState->lineTo(targetPoint);45 m_traversalState->processPathElement(PathElementAddLineToPoint, &targetPoint); 45 46 } 46 47 47 48 void SVGPathTraversalStateBuilder::curveToCubic(const FloatPoint& point1, const FloatPoint& point2, const FloatPoint& targetPoint, PathCoordinateMode) 48 49 { 50 FloatPoint points[] = { point1, point2, targetPoint }; 49 51 ASSERT(m_traversalState); 50 m_traversalState-> m_totalLength += m_traversalState->cubicBezierTo(point1, point2, targetPoint);52 m_traversalState->processPathElement(PathElementAddCurveToPoint, points); 51 53 } 52 54 … … 54 56 { 55 57 ASSERT(m_traversalState); 56 m_traversalState-> m_totalLength += m_traversalState->closeSubpath();58 m_traversalState->processPathElement(PathElementCloseSubpath, nullptr); 57 59 } 58 60 … … 60 62 { 61 63 ASSERT(m_traversalState); 62 m_traversalState-> m_desiredLength = desiredLength;64 m_traversalState->setDesiredLength(desiredLength); 63 65 } 64 66 … … 66 68 { 67 69 ASSERT(m_traversalState); 68 m_traversalState->processSegment(); 69 return !m_traversalState->m_success; 70 } 71 72 void SVGPathTraversalStateBuilder::incrementPathSegmentCount() 73 { 74 ASSERT(m_traversalState); 75 ++m_traversalState->m_segmentIndex; 76 } 77 78 unsigned SVGPathTraversalStateBuilder::pathSegmentIndex() 79 { 80 ASSERT(m_traversalState); 81 return m_traversalState->m_segmentIndex; 70 return !m_traversalState->success(); 82 71 } 83 72 … … 85 74 { 86 75 ASSERT(m_traversalState); 87 return m_traversalState-> m_totalLength;76 return m_traversalState->totalLength(); 88 77 } 89 78 … … 91 80 { 92 81 ASSERT(m_traversalState); 93 return m_traversalState-> m_current;82 return m_traversalState->current(); 94 83 } 95 84 -
trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h
r163440 r182828 33 33 SVGPathTraversalStateBuilder(); 34 34 35 unsigned pathSegmentIndex() ;35 unsigned pathSegmentIndex() { return m_segmentIndex; } 36 36 float totalLength(); 37 37 SVGPoint currentPoint(); … … 39 39 void setCurrentTraversalState(PathTraversalState* traversalState) { m_traversalState = traversalState; } 40 40 void setDesiredLength(float); 41 virtual void incrementPathSegmentCount() override ;41 virtual void incrementPathSegmentCount() override { ++m_segmentIndex; } 42 42 virtual bool continueConsuming() override; 43 virtual void cleanup() override { m_traversalState = 0; }43 virtual void cleanup() override { m_traversalState = nullptr, m_segmentIndex = 0; } 44 44 45 45 private: … … 60 60 61 61 PathTraversalState* m_traversalState; 62 unsigned m_segmentIndex; 62 63 }; 63 64 -
trunk/Source/WebCore/svg/SVGPathUtilities.cpp
r163440 r182828 286 286 return false; 287 287 288 PathTraversalState traversalState(PathTraversalState:: TraversalSegmentAtLength);288 PathTraversalState traversalState(PathTraversalState::Action::SegmentAtLength); 289 289 SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length); 290 290 … … 303 303 return false; 304 304 305 PathTraversalState traversalState(PathTraversalState:: TraversalTotalLength);305 PathTraversalState traversalState(PathTraversalState::Action::TotalLength); 306 306 SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, 0); 307 307 … … 320 320 return false; 321 321 322 PathTraversalState traversalState(PathTraversalState:: TraversalPointAtLength);322 PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength); 323 323 SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length); 324 324
Note: See TracChangeset
for help on using the changeset viewer.