Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1928)

Unified Diff: Source/core/svg/SVGPathParser.cpp

Issue 1033643003: Rework SVG path normalization code (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/svg/SVGPathParser.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « Source/core/svg/SVGPathParser.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698