Index: Source/core/svg/SVGPathParser.cpp |
diff --git a/Source/core/svg/SVGPathParser.cpp b/Source/core/svg/SVGPathParser.cpp |
index 52bb80920523598dd41f8df2663b1c064da9fe22..509fd6c89b47ee5c73dc5e7498ab44f98775c918 100644 |
--- a/Source/core/svg/SVGPathParser.cpp |
+++ b/Source/core/svg/SVGPathParser.cpp |
@@ -47,81 +47,34 @@ bool SVGPathParser::initialCommandIsMoveTo() |
return command == PathSegMoveToAbs || command == PathSegMoveToRel; |
} |
-void SVGPathParser::emitMoveToSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) |
- m_currentPoint += segment.targetPoint; |
- else |
- m_currentPoint = segment.targetPoint; |
- m_subPathPoint = m_currentPoint; |
- m_consumer->moveTo(m_currentPoint, AbsoluteCoordinates); |
-} |
-void SVGPathParser::emitLineToSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) |
- m_currentPoint += segment.targetPoint; |
- else |
- m_currentPoint = segment.targetPoint; |
- m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates); |
-} |
+class NormalizingConsumer { |
+ STACK_ALLOCATED(); |
+public: |
+ NormalizingConsumer(SVGPathConsumer* consumer) |
+ : m_consumer(consumer) |
+ , m_lastCommand(PathSegUnknown) |
+ { |
+ ASSERT(m_consumer); |
+ } |
-void SVGPathParser::emitLineToHorizontalSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) |
- m_currentPoint += segment.targetPoint; |
- else |
- m_currentPoint.setX(segment.targetPoint.x()); |
- m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates); |
-} |
+ void emitSegment(PathSegmentData&); |
-void SVGPathParser::emitLineToVerticalSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) |
- m_currentPoint += segment.targetPoint; |
- else |
- m_currentPoint.setY(segment.targetPoint.y()); |
- m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates); |
-} |
+private: |
+ bool decomposeArcToCubic(const FloatPoint& currentPoint, const PathSegmentData&); |
-void SVGPathParser::emitCurveToCubicSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) { |
- segment.point1 += m_currentPoint; |
- segment.point2 += m_currentPoint; |
- segment.targetPoint += m_currentPoint; |
- } |
- m_consumer->curveToCubic(segment.point1, segment.point2, segment.targetPoint, AbsoluteCoordinates); |
- |
- m_controlPoint = segment.point2; |
- m_currentPoint = segment.targetPoint; |
-} |
+ RawPtrWillBeMember<SVGPathConsumer> m_consumer; |
+ FloatPoint m_controlPoint; |
+ FloatPoint m_currentPoint; |
+ FloatPoint m_subPathPoint; |
+ SVGPathSegType m_lastCommand; |
+}; |
static FloatPoint reflectedPoint(const FloatPoint& reflectIn, const FloatPoint& pointToReflect) |
{ |
return FloatPoint(2 * reflectIn.x() - pointToReflect.x(), 2 * reflectIn.y() - pointToReflect.y()); |
} |
-void SVGPathParser::emitCurveToCubicSmoothSegment(PathSegmentData& segment) |
-{ |
- if (m_lastCommand != PathSegCurveToCubicAbs |
- && m_lastCommand != PathSegCurveToCubicRel |
- && m_lastCommand != PathSegCurveToCubicSmoothAbs |
- && m_lastCommand != PathSegCurveToCubicSmoothRel) |
- m_controlPoint = m_currentPoint; |
- |
- FloatPoint point1 = reflectedPoint(m_currentPoint, m_controlPoint); |
- if (m_mode == RelativeCoordinates) { |
- segment.point2 += m_currentPoint; |
- segment.targetPoint += m_currentPoint; |
- } |
- |
- m_consumer->curveToCubic(point1, segment.point2, segment.targetPoint, AbsoluteCoordinates); |
- |
- m_controlPoint = segment.point2; |
- m_currentPoint = segment.targetPoint; |
-} |
- |
// Blend the points with a ratio (1/3):(2/3). |
static FloatPoint blendPoints(const FloatPoint& p1, const FloatPoint& p2) |
{ |
@@ -129,50 +82,146 @@ static FloatPoint blendPoints(const FloatPoint& p1, const FloatPoint& p2) |
return FloatPoint((p1.x() + 2 * p2.x()) * oneOverThree, (p1.y() + 2 * p2.y()) * oneOverThree); |
} |
-void SVGPathParser::emitCurveToQuadraticSegment(PathSegmentData& segment) |
+static inline bool isCubicCommand(SVGPathSegType command) |
{ |
- m_controlPoint = segment.point1; |
- |
- if (m_mode == RelativeCoordinates) { |
- m_controlPoint += m_currentPoint; |
- segment.targetPoint += m_currentPoint; |
- } |
- segment.point1 = blendPoints(m_currentPoint, m_controlPoint); |
- FloatPoint point2 = blendPoints(segment.targetPoint, m_controlPoint); |
- |
- m_consumer->curveToCubic(segment.point1, point2, segment.targetPoint, AbsoluteCoordinates); |
+ return command == PathSegCurveToCubicAbs |
+ || command == PathSegCurveToCubicRel |
+ || command == PathSegCurveToCubicSmoothAbs |
+ || command == PathSegCurveToCubicSmoothRel; |
+} |
- m_currentPoint = segment.targetPoint; |
+static inline bool isQuadraticCommand(SVGPathSegType command) |
+{ |
+ return command == PathSegCurveToQuadraticAbs |
+ || command == PathSegCurveToQuadraticRel |
+ || command == PathSegCurveToQuadraticSmoothAbs |
+ || command == PathSegCurveToQuadraticSmoothRel; |
} |
-void SVGPathParser::emitCurveToQuadraticSmoothSegment(PathSegmentData& segment) |
+void NormalizingConsumer::emitSegment(PathSegmentData& segment) |
{ |
- if (m_lastCommand != PathSegCurveToQuadraticAbs |
- && m_lastCommand != PathSegCurveToQuadraticRel |
- && m_lastCommand != PathSegCurveToQuadraticSmoothAbs |
- && m_lastCommand != PathSegCurveToQuadraticSmoothRel) |
- m_controlPoint = m_currentPoint; |
+ SVGPathSegType originalCommand = segment.command; |
- if (m_mode == RelativeCoordinates) |
+ // Convert relative points to absolute points. |
+ switch (segment.command) { |
+ case PathSegCurveToQuadraticRel: |
+ segment.point1 += m_currentPoint; |
+ segment.targetPoint += m_currentPoint; |
+ break; |
+ case PathSegCurveToCubicRel: |
+ segment.point1 += m_currentPoint; |
+ /* fall through */ |
+ case PathSegCurveToCubicSmoothRel: |
+ segment.point2 += m_currentPoint; |
+ /* fall through */ |
+ case PathSegMoveToRel: |
+ case PathSegLineToRel: |
+ case PathSegLineToHorizontalRel: |
+ case PathSegLineToVerticalRel: |
+ case PathSegCurveToQuadraticSmoothRel: |
+ case PathSegArcRel: |
segment.targetPoint += m_currentPoint; |
+ break; |
+ case PathSegLineToHorizontalAbs: |
+ segment.targetPoint.setY(m_currentPoint.y()); |
+ break; |
+ case PathSegLineToVerticalAbs: |
+ segment.targetPoint.setX(m_currentPoint.x()); |
+ break; |
+ case PathSegClosePath: |
+ // Reset m_currentPoint for the next path. |
+ segment.targetPoint = m_subPathPoint; |
+ break; |
+ default: |
+ break; |
+ } |
- m_controlPoint = reflectedPoint(m_currentPoint, m_controlPoint); |
- FloatPoint point1 = blendPoints(m_currentPoint, m_controlPoint); |
- FloatPoint point2 = blendPoints(segment.targetPoint, m_controlPoint); |
+ // Update command verb, handle smooth segments and convert quadratic curve |
+ // segments to cubics. |
+ switch (segment.command) { |
+ case PathSegMoveToRel: |
+ case PathSegMoveToAbs: |
+ m_subPathPoint = segment.targetPoint; |
+ segment.command = PathSegMoveToAbs; |
+ break; |
+ case PathSegLineToRel: |
+ case PathSegLineToAbs: |
+ case PathSegLineToHorizontalRel: |
+ case PathSegLineToHorizontalAbs: |
+ case PathSegLineToVerticalRel: |
+ case PathSegLineToVerticalAbs: |
+ segment.command = PathSegLineToAbs; |
+ break; |
+ case PathSegClosePath: |
+ break; |
+ case PathSegCurveToCubicSmoothRel: |
+ case PathSegCurveToCubicSmoothAbs: |
+ if (!isCubicCommand(m_lastCommand)) |
+ segment.point1 = m_currentPoint; |
+ else |
+ segment.point1 = reflectedPoint(m_currentPoint, m_controlPoint); |
+ /* fall through */ |
+ case PathSegCurveToCubicRel: |
+ case PathSegCurveToCubicAbs: |
+ m_controlPoint = segment.point2; |
+ segment.command = PathSegCurveToCubicAbs; |
+ break; |
+ case PathSegCurveToQuadraticSmoothRel: |
+ case PathSegCurveToQuadraticSmoothAbs: |
+ if (!isQuadraticCommand(m_lastCommand)) |
+ segment.point1 = m_currentPoint; |
+ else |
+ segment.point1 = reflectedPoint(m_currentPoint, m_controlPoint); |
+ /* fall through */ |
+ case PathSegCurveToQuadraticRel: |
+ case PathSegCurveToQuadraticAbs: |
+ // Save the unmodified control point. |
+ m_controlPoint = segment.point1; |
+ segment.point1 = blendPoints(m_currentPoint, m_controlPoint); |
+ segment.point2 = blendPoints(segment.targetPoint, m_controlPoint); |
+ segment.command = PathSegCurveToCubicAbs; |
+ break; |
+ case PathSegArcRel: |
+ case PathSegArcAbs: |
+ if (!decomposeArcToCubic(m_currentPoint, segment)) { |
+ // On failure, emit a line segment to the target point. |
+ segment.command = PathSegLineToAbs; |
+ } else { |
+ // decomposeArcToCubic() has already emitted the normalized |
+ // segments, so set command to PathSegArcAbs, to skip any further |
+ // emit. |
+ segment.command = PathSegArcAbs; |
+ } |
+ break; |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ } |
- m_consumer->curveToCubic(point1, point2, segment.targetPoint, AbsoluteCoordinates); |
+ switch (segment.command) { |
+ case PathSegMoveToAbs: |
+ m_consumer->moveTo(segment.targetPoint, AbsoluteCoordinates); |
+ break; |
+ case PathSegLineToAbs: |
+ m_consumer->lineTo(segment.targetPoint, AbsoluteCoordinates); |
+ break; |
+ case PathSegClosePath: |
+ m_consumer->closePath(); |
+ break; |
+ case PathSegCurveToCubicAbs: |
+ m_consumer->curveToCubic(segment.point1, segment.point2, segment.targetPoint, AbsoluteCoordinates); |
+ break; |
+ case PathSegArcAbs: |
+ break; |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ } |
m_currentPoint = segment.targetPoint; |
-} |
-void SVGPathParser::emitArcToSegment(PathSegmentData& segment) |
-{ |
- if (m_mode == RelativeCoordinates) |
- segment.targetPoint += m_currentPoint; |
+ if (!isCubicCommand(originalCommand) && !isQuadraticCommand(originalCommand)) |
+ m_controlPoint = m_currentPoint; |
- if (!decomposeArcToCubic(m_currentPoint, segment)) |
- m_consumer->lineTo(segment.targetPoint, AbsoluteCoordinates); |
- m_currentPoint = segment.targetPoint; |
+ m_lastCommand = originalCommand; |
} |
bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, bool checkForInitialMoveTo) |
@@ -180,76 +229,18 @@ bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, boo |
ASSERT(m_source); |
ASSERT(m_consumer); |
- m_controlPoint = FloatPoint(); |
- m_currentPoint = FloatPoint(); |
- m_subPathPoint = FloatPoint(); |
- |
if (checkForInitialMoveTo && !initialCommandIsMoveTo()) |
return false; |
- m_lastCommand = PathSegUnknown; |
+ NormalizingConsumer normalizer(m_consumer); |
+ |
while (m_source->hasMoreData()) { |
PathSegmentData segment = m_source->parseSegment(); |
if (segment.command == PathSegUnknown) |
return false; |
if (pathParsingMode == NormalizedParsing) { |
- m_mode = AbsoluteCoordinates; |
- |
- switch (segment.command) { |
- case PathSegMoveToRel: |
- m_mode = RelativeCoordinates; |
- case PathSegMoveToAbs: |
- emitMoveToSegment(segment); |
- break; |
- case PathSegLineToRel: |
- m_mode = RelativeCoordinates; |
- case PathSegLineToAbs: |
- emitLineToSegment(segment); |
- break; |
- case PathSegLineToHorizontalRel: |
- m_mode = RelativeCoordinates; |
- case PathSegLineToHorizontalAbs: |
- emitLineToHorizontalSegment(segment); |
- break; |
- case PathSegLineToVerticalRel: |
- m_mode = RelativeCoordinates; |
- case PathSegLineToVerticalAbs: |
- emitLineToVerticalSegment(segment); |
- break; |
- case PathSegClosePath: |
- m_consumer->closePath(); |
- // Reset m_currentPoint for the next path. |
- m_currentPoint = m_subPathPoint; |
- break; |
- case PathSegCurveToCubicRel: |
- m_mode = RelativeCoordinates; |
- case PathSegCurveToCubicAbs: |
- emitCurveToCubicSegment(segment); |
- break; |
- case PathSegCurveToCubicSmoothRel: |
- m_mode = RelativeCoordinates; |
- case PathSegCurveToCubicSmoothAbs: |
- emitCurveToCubicSmoothSegment(segment); |
- break; |
- case PathSegCurveToQuadraticRel: |
- m_mode = RelativeCoordinates; |
- case PathSegCurveToQuadraticAbs: |
- emitCurveToQuadraticSegment(segment); |
- break; |
- case PathSegCurveToQuadraticSmoothRel: |
- m_mode = RelativeCoordinates; |
- case PathSegCurveToQuadraticSmoothAbs: |
- emitCurveToQuadraticSmoothSegment(segment); |
- break; |
- case PathSegArcRel: |
- m_mode = RelativeCoordinates; |
- case PathSegArcAbs: |
- emitArcToSegment(segment); |
- break; |
- default: |
- ASSERT_NOT_REACHED(); |
- } |
+ normalizer.emitSegment(segment); |
} else { |
PathCoordinateMode mode = AbsoluteCoordinates; |
@@ -310,18 +301,6 @@ bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, boo |
if (!m_consumer->continueConsuming()) |
return true; |
- m_lastCommand = segment.command; |
- |
- if (m_lastCommand != PathSegCurveToCubicAbs |
- && m_lastCommand != PathSegCurveToCubicRel |
- && m_lastCommand != PathSegCurveToCubicSmoothAbs |
- && m_lastCommand != PathSegCurveToCubicSmoothRel |
- && m_lastCommand != PathSegCurveToQuadraticAbs |
- && m_lastCommand != PathSegCurveToQuadraticRel |
- && m_lastCommand != PathSegCurveToQuadraticSmoothAbs |
- && m_lastCommand != PathSegCurveToQuadraticSmoothRel) |
- m_controlPoint = m_currentPoint; |
- |
if (m_source->hasMoreData()) |
m_consumer->incrementPathSegmentCount(); |
} |
@@ -331,7 +310,7 @@ bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, boo |
// This works by converting the SVG arc to "simple" beziers. |
// Partly adapted from Niko's code in kdelibs/kdecore/svgicons. |
// See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter |
-bool SVGPathParser::decomposeArcToCubic(const FloatPoint& currentPoint, const PathSegmentData& arcSegment) |
+bool NormalizingConsumer::decomposeArcToCubic(const FloatPoint& currentPoint, const PathSegmentData& arcSegment) |
{ |
// If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") joining the endpoints. |
// http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters |