blob: 96f43a2071da30b3cfe6f500a62725d4ef548e8c [file] [log] [blame]
/*
* Copyright (C) 2002, 2003 The Karbon Developers
* Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
* Copyright (C) 2006, 2007 Rob Buis <buis@kde.org>
* Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "core/svg/SVGPathBuilder.h"
#include "platform/graphics/Path.h"
namespace blink {
FloatPoint SVGPathBuilder::smoothControl(bool isCompatibleSegment) const
{
// The control point is assumed to be the reflection of the control point on
// the previous command relative to the current point. If there is no previous
// command or if the previous command was not a [quad/cubic], assume the control
// point is coincident with the current point.
// [https://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands]
// [https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands]
FloatPoint controlPoint = m_currentPoint;
if (isCompatibleSegment)
controlPoint += m_currentPoint - m_lastControlPoint;
return controlPoint;
}
void SVGPathBuilder::emitClose()
{
m_path.closeSubpath();
// At the end of the [closepath] command, the new current
// point is set to the initial point of the current subpath.
// [https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand]
m_currentPoint = m_subpathPoint;
}
void SVGPathBuilder::emitMoveTo(const FloatPoint& p)
{
m_path.moveTo(p);
m_subpathPoint = p;
m_currentPoint = p;
}
void SVGPathBuilder::emitLineTo(const FloatPoint& p)
{
m_path.addLineTo(p);
m_currentPoint = p;
}
void SVGPathBuilder::emitQuadTo(const FloatPoint& c0, const FloatPoint& p)
{
m_path.addQuadCurveTo(c0, p);
m_lastControlPoint = c0;
m_currentPoint = p;
}
void SVGPathBuilder::emitSmoothQuadTo(const FloatPoint& p)
{
bool lastWasQuadratic = m_lastCommand == PathSegCurveToQuadraticAbs
|| m_lastCommand == PathSegCurveToQuadraticRel
|| m_lastCommand == PathSegCurveToQuadraticSmoothAbs
|| m_lastCommand == PathSegCurveToQuadraticSmoothRel;
emitQuadTo(smoothControl(lastWasQuadratic), p);
}
void SVGPathBuilder::emitCubicTo(const FloatPoint& c0, const FloatPoint& c1, const FloatPoint& p)
{
m_path.addBezierCurveTo(c0, c1, p);
m_lastControlPoint = c1;
m_currentPoint = p;
}
void SVGPathBuilder::emitSmoothCubicTo(const FloatPoint& c1, const FloatPoint& p)
{
bool lastWasCubic = m_lastCommand == PathSegCurveToCubicAbs
|| m_lastCommand == PathSegCurveToCubicRel
|| m_lastCommand == PathSegCurveToCubicSmoothAbs
|| m_lastCommand == PathSegCurveToCubicSmoothRel;
emitCubicTo(smoothControl(lastWasCubic), c1, p);
}
void SVGPathBuilder::emitArcTo(const FloatPoint& p, const FloatSize& r, float rotate,
bool largeArc, bool sweep)
{
m_path.addArcTo(p, r, rotate, largeArc, sweep);
m_currentPoint = p;
}
void SVGPathBuilder::emitSegment(const PathSegmentData& segment)
{
switch (segment.command) {
case PathSegClosePath:
emitClose();
break;
case PathSegMoveToAbs:
emitMoveTo(
segment.targetPoint);
break;
case PathSegMoveToRel:
emitMoveTo(
m_currentPoint + segment.targetPoint);
break;
case PathSegLineToAbs:
emitLineTo(
segment.targetPoint);
break;
case PathSegLineToRel:
emitLineTo(
m_currentPoint + segment.targetPoint);
break;
case PathSegLineToHorizontalAbs:
emitLineTo(
FloatPoint(segment.targetPoint.x(), m_currentPoint.y()));
break;
case PathSegLineToHorizontalRel:
emitLineTo(
m_currentPoint + FloatSize(segment.targetPoint.x(), 0));
break;
case PathSegLineToVerticalAbs:
emitLineTo(
FloatPoint(m_currentPoint.x(), segment.targetPoint.y()));
break;
case PathSegLineToVerticalRel:
emitLineTo(
m_currentPoint + FloatSize(0, segment.targetPoint.y()));
break;
case PathSegCurveToQuadraticAbs:
emitQuadTo(
segment.point1,
segment.targetPoint);
break;
case PathSegCurveToQuadraticRel:
emitQuadTo(
m_currentPoint + segment.point1,
m_currentPoint + segment.targetPoint);
break;
case PathSegCurveToQuadraticSmoothAbs:
emitSmoothQuadTo(
segment.targetPoint);
break;
case PathSegCurveToQuadraticSmoothRel:
emitSmoothQuadTo(
m_currentPoint + segment.targetPoint);
break;
case PathSegCurveToCubicAbs:
emitCubicTo(
segment.point1,
segment.point2,
segment.targetPoint);
break;
case PathSegCurveToCubicRel:
emitCubicTo(
m_currentPoint + segment.point1,
m_currentPoint + segment.point2,
m_currentPoint + segment.targetPoint);
break;
case PathSegCurveToCubicSmoothAbs:
emitSmoothCubicTo(
segment.point2,
segment.targetPoint);
break;
case PathSegCurveToCubicSmoothRel:
emitSmoothCubicTo(
m_currentPoint + segment.point2,
m_currentPoint + segment.targetPoint);
break;
case PathSegArcAbs:
emitArcTo(
segment.targetPoint,
toFloatSize(segment.arcRadii()),
segment.arcAngle(),
segment.largeArcFlag(),
segment.sweepFlag());
break;
case PathSegArcRel:
emitArcTo(
m_currentPoint + segment.targetPoint,
toFloatSize(segment.arcRadii()),
segment.arcAngle(),
segment.largeArcFlag(),
segment.sweepFlag());
break;
default:
ASSERT_NOT_REACHED();
}
m_lastCommand = segment.command;
}
} // namespace blink