| Index: Source/core/svg/SVGPathStringSource.cpp
|
| diff --git a/Source/core/svg/SVGPathStringSource.cpp b/Source/core/svg/SVGPathStringSource.cpp
|
| index 8eb442ad686306fcde2583a9d6fe2a597d197903..069640ead9f61b2356281350276d4ce9c4ebcecc 100644
|
| --- a/Source/core/svg/SVGPathStringSource.cpp
|
| +++ b/Source/core/svg/SVGPathStringSource.cpp
|
| @@ -19,7 +19,6 @@
|
| */
|
|
|
| #include "config.h"
|
| -
|
| #include "core/svg/SVGPathStringSource.h"
|
|
|
| #include "core/svg/SVGParserUtilities.h"
|
| @@ -30,6 +29,8 @@ namespace blink {
|
| SVGPathStringSource::SVGPathStringSource(const String& string)
|
| : m_string(string)
|
| , m_is8BitSource(string.is8Bit())
|
| + , m_seenError(false)
|
| + , m_previousCommand(PathSegUnknown)
|
| {
|
| ASSERT(!string.isEmpty());
|
|
|
| @@ -40,6 +41,7 @@ SVGPathStringSource::SVGPathStringSource(const String& string)
|
| m_current.m_character16 = string.characters16();
|
| m_end.m_character16 = m_current.m_character16 + string.length();
|
| }
|
| + eatWhitespace();
|
| }
|
|
|
| bool SVGPathStringSource::hasMoreData() const
|
| @@ -49,93 +51,65 @@ bool SVGPathStringSource::hasMoreData() const
|
| return m_current.m_character16 < m_end.m_character16;
|
| }
|
|
|
| -bool SVGPathStringSource::moveToNextToken()
|
| +void SVGPathStringSource::eatWhitespace()
|
| {
|
| if (m_is8BitSource)
|
| - return skipOptionalSVGSpaces(m_current.m_character8, m_end.m_character8);
|
| - return skipOptionalSVGSpaces(m_current.m_character16, m_end.m_character16);
|
| + skipOptionalSVGSpaces(m_current.m_character8, m_end.m_character8);
|
| + else
|
| + skipOptionalSVGSpaces(m_current.m_character16, m_end.m_character16);
|
| }
|
|
|
| -template <typename CharacterType>
|
| -static bool parseSVGSegmentTypeHelper(const CharacterType*& current, SVGPathSegType& pathSegType)
|
| +static SVGPathSegType parseSVGSegmentTypeHelper(unsigned lookahead)
|
| {
|
| - switch (*(current++)) {
|
| + switch (lookahead) {
|
| case 'Z':
|
| case 'z':
|
| - pathSegType = PathSegClosePath;
|
| - break;
|
| + return PathSegClosePath;
|
| case 'M':
|
| - pathSegType = PathSegMoveToAbs;
|
| - break;
|
| + return PathSegMoveToAbs;
|
| case 'm':
|
| - pathSegType = PathSegMoveToRel;
|
| - break;
|
| + return PathSegMoveToRel;
|
| case 'L':
|
| - pathSegType = PathSegLineToAbs;
|
| - break;
|
| + return PathSegLineToAbs;
|
| case 'l':
|
| - pathSegType = PathSegLineToRel;
|
| - break;
|
| + return PathSegLineToRel;
|
| case 'C':
|
| - pathSegType = PathSegCurveToCubicAbs;
|
| - break;
|
| + return PathSegCurveToCubicAbs;
|
| case 'c':
|
| - pathSegType = PathSegCurveToCubicRel;
|
| - break;
|
| + return PathSegCurveToCubicRel;
|
| case 'Q':
|
| - pathSegType = PathSegCurveToQuadraticAbs;
|
| - break;
|
| + return PathSegCurveToQuadraticAbs;
|
| case 'q':
|
| - pathSegType = PathSegCurveToQuadraticRel;
|
| - break;
|
| + return PathSegCurveToQuadraticRel;
|
| case 'A':
|
| - pathSegType = PathSegArcAbs;
|
| - break;
|
| + return PathSegArcAbs;
|
| case 'a':
|
| - pathSegType = PathSegArcRel;
|
| - break;
|
| + return PathSegArcRel;
|
| case 'H':
|
| - pathSegType = PathSegLineToHorizontalAbs;
|
| - break;
|
| + return PathSegLineToHorizontalAbs;
|
| case 'h':
|
| - pathSegType = PathSegLineToHorizontalRel;
|
| - break;
|
| + return PathSegLineToHorizontalRel;
|
| case 'V':
|
| - pathSegType = PathSegLineToVerticalAbs;
|
| - break;
|
| + return PathSegLineToVerticalAbs;
|
| case 'v':
|
| - pathSegType = PathSegLineToVerticalRel;
|
| - break;
|
| + return PathSegLineToVerticalRel;
|
| case 'S':
|
| - pathSegType = PathSegCurveToCubicSmoothAbs;
|
| - break;
|
| + return PathSegCurveToCubicSmoothAbs;
|
| case 's':
|
| - pathSegType = PathSegCurveToCubicSmoothRel;
|
| - break;
|
| + return PathSegCurveToCubicSmoothRel;
|
| case 'T':
|
| - pathSegType = PathSegCurveToQuadraticSmoothAbs;
|
| - break;
|
| + return PathSegCurveToQuadraticSmoothAbs;
|
| case 't':
|
| - pathSegType = PathSegCurveToQuadraticSmoothRel;
|
| - break;
|
| + return PathSegCurveToQuadraticSmoothRel;
|
| default:
|
| - pathSegType = PathSegUnknown;
|
| + return PathSegUnknown;
|
| }
|
| - return true;
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseSVGSegmentType(SVGPathSegType& pathSegType)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseSVGSegmentTypeHelper(m_current.m_character8, pathSegType);
|
| - return parseSVGSegmentTypeHelper(m_current.m_character16, pathSegType);
|
| }
|
|
|
| -template <typename CharacterType>
|
| -static bool nextCommandHelper(const CharacterType*& current, SVGPathSegType previousCommand, SVGPathSegType& nextCommand)
|
| +static bool nextCommandHelper(unsigned lookahead, SVGPathSegType previousCommand, SVGPathSegType& nextCommand)
|
| {
|
| // Check for remaining coordinates in the current command.
|
| - if ((*current == '+' || *current == '-' || *current == '.' || (*current >= '0' && *current <= '9'))
|
| + if ((lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9'))
|
| && previousCommand != PathSegClosePath) {
|
| if (previousCommand == PathSegMoveToAbs) {
|
| nextCommand = PathSegLineToAbs;
|
| @@ -148,103 +122,116 @@ static bool nextCommandHelper(const CharacterType*& current, SVGPathSegType prev
|
| nextCommand = previousCommand;
|
| return true;
|
| }
|
| -
|
| return false;
|
| }
|
|
|
| -SVGPathSegType SVGPathStringSource::nextCommand(SVGPathSegType previousCommand)
|
| -{
|
| - SVGPathSegType nextCommand;
|
| - if (m_is8BitSource) {
|
| - if (nextCommandHelper(m_current.m_character8, previousCommand, nextCommand))
|
| - return nextCommand;
|
| - } else {
|
| - if (nextCommandHelper(m_current.m_character16, previousCommand, nextCommand))
|
| - return nextCommand;
|
| - }
|
| -
|
| - parseSVGSegmentType(nextCommand);
|
| - return nextCommand;
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseMoveToSegment(FloatPoint& targetPoint)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
|
| - return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseLineToSegment(FloatPoint& targetPoint)
|
| +float SVGPathStringSource::parseNumberWithError()
|
| {
|
| + float numberValue = 0;
|
| if (m_is8BitSource)
|
| - return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
|
| - return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
|
| + m_seenError |= !parseNumber(m_current.m_character8, m_end.m_character8, numberValue);
|
| + else
|
| + m_seenError |= !parseNumber(m_current.m_character16, m_end.m_character16, numberValue);
|
| + return numberValue;
|
| }
|
|
|
| -bool SVGPathStringSource::parseLineToHorizontalSegment(float& x)
|
| +bool SVGPathStringSource::parseArcFlagWithError()
|
| {
|
| + bool flagValue = false;
|
| if (m_is8BitSource)
|
| - return parseNumber(m_current.m_character8, m_end.m_character8, x);
|
| - return parseNumber(m_current.m_character16, m_end.m_character16, x);
|
| + m_seenError |= !parseArcFlag(m_current.m_character8, m_end.m_character8, flagValue);
|
| + else
|
| + m_seenError |= !parseArcFlag(m_current.m_character16, m_end.m_character16, flagValue);
|
| + return flagValue;
|
| }
|
|
|
| -bool SVGPathStringSource::parseLineToVerticalSegment(float& y)
|
| +SVGPathSegType SVGPathStringSource::peekSegmentType()
|
| {
|
| - if (m_is8BitSource)
|
| - return parseNumber(m_current.m_character8, m_end.m_character8, y);
|
| - return parseNumber(m_current.m_character16, m_end.m_character16, y);
|
| + ASSERT(hasMoreData());
|
| + // This won't work in all cases because of the state required to "detect" implicit commands.
|
| + unsigned lookahead = m_is8BitSource ? *m_current.m_character8 : *m_current.m_character16;
|
| + return parseSVGSegmentTypeHelper(lookahead);
|
| }
|
|
|
| -bool SVGPathStringSource::parseCurveToCubicSegment(FloatPoint& point1, FloatPoint& point2, FloatPoint& targetPoint)
|
| +PathSegmentData SVGPathStringSource::parseSegment()
|
| {
|
| - if (m_is8BitSource)
|
| - return parseFloatPoint3(m_current.m_character8, m_end.m_character8, point1, point2, targetPoint);
|
| - return parseFloatPoint3(m_current.m_character16, m_end.m_character16, point1, point2, targetPoint);
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseCurveToCubicSmoothSegment(FloatPoint& point1, FloatPoint& targetPoint)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseFloatPoint2(m_current.m_character8, m_end.m_character8, point1, targetPoint);
|
| - return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point1, targetPoint);
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseCurveToQuadraticSegment(FloatPoint& point2, FloatPoint& targetPoint)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseFloatPoint2(m_current.m_character8, m_end.m_character8, point2, targetPoint);
|
| - return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point2, targetPoint);
|
| -}
|
| -
|
| -bool SVGPathStringSource::parseCurveToQuadraticSmoothSegment(FloatPoint& targetPoint)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
|
| - return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
|
| -}
|
| + ASSERT(hasMoreData());
|
| + PathSegmentData segment;
|
| + unsigned lookahead = m_is8BitSource ? *m_current.m_character8 : *m_current.m_character16;
|
| + SVGPathSegType command = parseSVGSegmentTypeHelper(lookahead);
|
| + if (command == PathSegUnknown) {
|
| + // Possibly an implicit command. Not allowed if this is the first command.
|
| + if (m_previousCommand == PathSegUnknown)
|
| + return segment;
|
| + if (!nextCommandHelper(lookahead, m_previousCommand, command))
|
| + return segment;
|
| + } else {
|
| + // Valid explicit command.
|
| + if (m_is8BitSource)
|
| + m_current.m_character8++;
|
| + else
|
| + m_current.m_character16++;
|
| + }
|
|
|
| -template <typename CharacterType>
|
| -static bool parseArcToSegmentHelper(const CharacterType*& current, const CharacterType* end, float& rx, float& ry, float& angle, bool& largeArc, bool& sweep, FloatPoint& targetPoint)
|
| -{
|
| - float toX;
|
| - float toY;
|
| - if (!parseNumber(current, end, rx)
|
| - || !parseNumber(current, end, ry)
|
| - || !parseNumber(current, end, angle)
|
| - || !parseArcFlag(current, end, largeArc)
|
| - || !parseArcFlag(current, end, sweep)
|
| - || !parseNumber(current, end, toX)
|
| - || !parseNumber(current, end, toY))
|
| - return false;
|
| - targetPoint = FloatPoint(toX, toY);
|
| - return true;
|
| -}
|
| + segment.command = m_previousCommand = command;
|
| +
|
| + ASSERT(!m_seenError);
|
| +
|
| + switch (segment.command) {
|
| + case PathSegCurveToCubicRel:
|
| + case PathSegCurveToCubicAbs:
|
| + segment.point1.setX(parseNumberWithError());
|
| + segment.point1.setY(parseNumberWithError());
|
| + /* fall through */
|
| + case PathSegCurveToCubicSmoothRel:
|
| + case PathSegCurveToCubicSmoothAbs:
|
| + segment.point2.setX(parseNumberWithError());
|
| + segment.point2.setY(parseNumberWithError());
|
| + /* fall through */
|
| + case PathSegMoveToRel:
|
| + case PathSegMoveToAbs:
|
| + case PathSegLineToRel:
|
| + case PathSegLineToAbs:
|
| + case PathSegCurveToQuadraticSmoothRel:
|
| + case PathSegCurveToQuadraticSmoothAbs:
|
| + segment.targetPoint.setX(parseNumberWithError());
|
| + segment.targetPoint.setY(parseNumberWithError());
|
| + break;
|
| + case PathSegLineToHorizontalRel:
|
| + case PathSegLineToHorizontalAbs:
|
| + segment.targetPoint.setX(parseNumberWithError());
|
| + break;
|
| + case PathSegLineToVerticalRel:
|
| + case PathSegLineToVerticalAbs:
|
| + segment.targetPoint.setY(parseNumberWithError());
|
| + break;
|
| + case PathSegClosePath:
|
| + eatWhitespace();
|
| + break;
|
| + case PathSegCurveToQuadraticRel:
|
| + case PathSegCurveToQuadraticAbs:
|
| + segment.point1.setX(parseNumberWithError());
|
| + segment.point1.setY(parseNumberWithError());
|
| + segment.targetPoint.setX(parseNumberWithError());
|
| + segment.targetPoint.setY(parseNumberWithError());
|
| + break;
|
| + case PathSegArcRel:
|
| + case PathSegArcAbs:
|
| + segment.point1.setX(parseNumberWithError()); // rx
|
| + segment.point1.setY(parseNumberWithError()); // ry
|
| + segment.point2.setX(parseNumberWithError()); // angle
|
| + segment.arcLarge = parseArcFlagWithError();
|
| + segment.arcSweep = parseArcFlagWithError();
|
| + segment.targetPoint.setX(parseNumberWithError());
|
| + segment.targetPoint.setY(parseNumberWithError());
|
| + break;
|
| + case PathSegUnknown:
|
| + ASSERT_NOT_REACHED();
|
| + }
|
|
|
| -bool SVGPathStringSource::parseArcToSegment(float& rx, float& ry, float& angle, bool& largeArc, bool& sweep, FloatPoint& targetPoint)
|
| -{
|
| - if (m_is8BitSource)
|
| - return parseArcToSegmentHelper(m_current.m_character8, m_end.m_character8, rx, ry, angle, largeArc, sweep, targetPoint);
|
| - return parseArcToSegmentHelper(m_current.m_character16, m_end.m_character16, rx, ry, angle, largeArc, sweep, targetPoint);
|
| + if (UNLIKELY(m_seenError))
|
| + segment.command = PathSegUnknown;
|
| + return segment;
|
| }
|
|
|
| } // namespace blink
|
|
|