/* Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann 2004, 2005 Rob Buis Copyright (C) 2007 Eric Seidel This file is part of the WebKit project 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 "config.h" #if ENABLE(SVG) #include "SVGPathSegList.h" #include "FloatPoint.h" #include "Path.h" #include "PathTraversalState.h" #include "SVGPathSegArc.h" #include "SVGPathSegClosePath.h" #include "SVGPathSegMoveto.h" #include "SVGPathSegLineto.h" #include "SVGPathSegLinetoHorizontal.h" #include "SVGPathSegLinetoVertical.h" #include "SVGPathSegCurvetoCubic.h" #include "SVGPathSegCurvetoCubicSmooth.h" #include "SVGPathSegCurvetoQuadratic.h" #include "SVGPathSegCurvetoQuadraticSmooth.h" namespace WebCore { SVGPathSegList::SVGPathSegList(const QualifiedName& attributeName) : SVGList >(attributeName) { } SVGPathSegList::~SVGPathSegList() { } unsigned SVGPathSegList::getPathSegAtLength(double) { // FIXME : to be useful this will need to support non-normalized SVGPathSegLists ExceptionCode ec = 0; int len = numberOfItems(); // FIXME: Eventually this will likely move to a "path applier"-like model, until then PathTraversalState is less useful as we could just use locals PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength); for (int i = 0; i < len; ++i) { SVGPathSeg* segment = getItem(i, ec).get(); float segmentLength = 0; switch (segment->pathSegType()) { case SVGPathSeg::PATHSEG_MOVETO_ABS: { SVGPathSegMovetoAbs* moveTo = static_cast(segment); segmentLength = traversalState.moveTo(FloatPoint(moveTo->x(), moveTo->y())); break; } case SVGPathSeg::PATHSEG_LINETO_ABS: { SVGPathSegLinetoAbs* lineTo = static_cast(segment); segmentLength = traversalState.lineTo(FloatPoint(lineTo->x(), lineTo->y())); break; } case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: { SVGPathSegCurvetoCubicAbs* curveTo = static_cast(segment); segmentLength = traversalState.cubicBezierTo(FloatPoint(curveTo->x1(), curveTo->y1()), FloatPoint(curveTo->x2(), curveTo->y2()), FloatPoint(curveTo->x(), curveTo->y())); break; } case SVGPathSeg::PATHSEG_CLOSEPATH: segmentLength = traversalState.closeSubpath(); break; default: ASSERT(false); // FIXME: This only works with normalized/processed path data. break; } traversalState.m_totalLength += segmentLength; if ((traversalState.m_action == PathTraversalState::TraversalSegmentAtLength) && (traversalState.m_totalLength > traversalState.m_desiredLength)) { return traversalState.m_segmentIndex; } traversalState.m_segmentIndex++; } return 0; // The SVG spec is unclear as to what to return when the distance is not on the path } Path SVGPathSegList::toPathData() { // FIXME : This should also support non-normalized PathSegLists Path pathData; ExceptionCode ec = 0; int len = numberOfItems(); for (int i = 0; i < len; ++i) { SVGPathSeg* segment = getItem(i, ec).get(); switch (segment->pathSegType()) { case SVGPathSeg::PATHSEG_MOVETO_ABS: { SVGPathSegMovetoAbs* moveTo = static_cast(segment); pathData.moveTo(FloatPoint(moveTo->x(), moveTo->y())); break; } case SVGPathSeg::PATHSEG_LINETO_ABS: { SVGPathSegLinetoAbs* lineTo = static_cast(segment); pathData.addLineTo(FloatPoint(lineTo->x(), lineTo->y())); break; } case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: { SVGPathSegCurvetoCubicAbs* curveTo = static_cast(segment); pathData.addBezierCurveTo(FloatPoint(curveTo->x1(), curveTo->y1()), FloatPoint(curveTo->x2(), curveTo->y2()), FloatPoint(curveTo->x(), curveTo->y())); break; } case SVGPathSeg::PATHSEG_CLOSEPATH: pathData.closeSubpath(); break; default: ASSERT(false); // FIXME: This only works with normalized/processed path data. break; } } return pathData; } static inline float blendFunc(float from, float to, float progress) { return (to - from) * progress + from; } #define BLENDPATHSEG1(class, attr1) \ class::create(blendFunc(static_cast(from)->attr1(), static_cast(to)->attr1(), progress)) #define BLENDPATHSEG2(class, attr1, attr2) \ class::create(blendFunc(static_cast(from)->attr1(), static_cast(to)->attr1(), progress), \ blendFunc(static_cast(from)->attr2(), static_cast(to)->attr2(), progress)) #define BLENDPATHSEG4(class, attr1, attr2, attr3, attr4) \ class::create(blendFunc(static_cast(from)->attr1(), static_cast(to)->attr1(), progress), \ blendFunc(static_cast(from)->attr2(), static_cast(to)->attr2(), progress), \ blendFunc(static_cast(from)->attr3(), static_cast(to)->attr3(), progress), \ blendFunc(static_cast(from)->attr4(), static_cast(to)->attr4(), progress)) #define BLENDPATHSEG6(class, attr1, attr2, attr3, attr4, attr5, attr6) \ class::create(blendFunc(static_cast(from)->attr1(), static_cast(to)->attr1(), progress), \ blendFunc(static_cast(from)->attr2(), static_cast(to)->attr2(), progress), \ blendFunc(static_cast(from)->attr3(), static_cast(to)->attr3(), progress), \ blendFunc(static_cast(from)->attr4(), static_cast(to)->attr4(), progress), \ blendFunc(static_cast(from)->attr5(), static_cast(to)->attr5(), progress), \ blendFunc(static_cast(from)->attr6(), static_cast(to)->attr6(), progress)) #define BLENDPATHSEG7(class, attr1, attr2, attr3, attr4, attr5, bool1, bool2) \ class::create(blendFunc(static_cast(from)->attr1(), static_cast(to)->attr1(), progress), \ blendFunc(static_cast(from)->attr2(), static_cast(to)->attr2(), progress), \ blendFunc(static_cast(from)->attr3(), static_cast(to)->attr3(), progress), \ blendFunc(static_cast(from)->attr4(), static_cast(to)->attr4(), progress), \ blendFunc(static_cast(from)->attr5(), static_cast(to)->attr5(), progress), \ static_cast(blendFunc(static_cast(from)->bool1(), static_cast(to)->bool1(), progress)), \ static_cast(blendFunc(static_cast(from)->bool2(), static_cast(to)->bool2(), progress))) PassRefPtr SVGPathSegList::createAnimated(const SVGPathSegList* fromList, const SVGPathSegList* toList, float progress) { unsigned itemCount = fromList->numberOfItems(); if (!itemCount || itemCount != toList->numberOfItems()) return 0; RefPtr result = create(fromList->associatedAttributeName()); ExceptionCode ec; for (unsigned n = 0; n < itemCount; ++n) { SVGPathSeg* from = fromList->getItem(n, ec).get(); SVGPathSeg* to = toList->getItem(n, ec).get(); if (from->pathSegType() == SVGPathSeg::PATHSEG_UNKNOWN || from->pathSegType() != to->pathSegType()) return 0; RefPtr segment = 0; switch (static_cast(from->pathSegType())) { case SVGPathSeg::PATHSEG_CLOSEPATH: segment = SVGPathSegClosePath::create(); break; case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS: segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalAbs, x); break; case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL: segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalRel, x); break; case SVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS: segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalAbs, y); break; case SVGPathSeg::PATHSEG_LINETO_VERTICAL_REL: segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalRel, y); break; case SVGPathSeg::PATHSEG_MOVETO_ABS: segment = BLENDPATHSEG2(SVGPathSegMovetoAbs, x, y); break; case SVGPathSeg::PATHSEG_MOVETO_REL: segment = BLENDPATHSEG2(SVGPathSegMovetoRel, x, y); break; case SVGPathSeg::PATHSEG_LINETO_ABS: segment = BLENDPATHSEG2(SVGPathSegLinetoAbs, x, y); break; case SVGPathSeg::PATHSEG_LINETO_REL: segment = BLENDPATHSEG2(SVGPathSegLinetoRel, x, y); break; case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicAbs, x, y, x1, y1, x2, y2); break; case SVGPathSeg::PATHSEG_CURVETO_CUBIC_REL: segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicRel, x, y, x1, y1, x2, y2); break; case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothAbs, x, y, x2, y2); break; case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL: segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothRel, x, y, x2, y2); break; case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS: segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticAbs, x, y, x1, y1); break; case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL: segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticRel, x, y, x1, y1); break; case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothAbs, x, y); break; case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothRel, x, y); break; case SVGPathSeg::PATHSEG_ARC_ABS: segment = BLENDPATHSEG7(SVGPathSegArcAbs, x, y, r1, r2, angle, largeArcFlag, sweepFlag); break; case SVGPathSeg::PATHSEG_ARC_REL: segment = BLENDPATHSEG7(SVGPathSegArcRel, x, y, r1, r2, angle, largeArcFlag, sweepFlag); break; case SVGPathSeg::PATHSEG_UNKNOWN: ASSERT_NOT_REACHED(); } result->appendItem(segment, ec); } return result.release(); } } #endif // ENABLE(SVG)