blob: defed36c115ead5648bad96f2cc6f355e33e16ba [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
* 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 "config.h"
#include "core/svg/SVGPathSegList.h"
#include "SVGNames.h"
#include "core/svg/SVGAnimationElement.h"
#include "core/svg/SVGPathBlender.h"
#include "core/svg/SVGPathByteStreamBuilder.h"
#include "core/svg/SVGPathByteStreamSource.h"
#include "core/svg/SVGPathElement.h"
#include "core/svg/SVGPathParser.h"
#include "core/svg/SVGPathSegListBuilder.h"
#include "core/svg/SVGPathSegListSource.h"
#include "core/svg/SVGPathUtilities.h"
namespace WebCore {
SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, SVGPathSegRole role)
: m_contextElement(contextElement)
, m_role(role)
, m_listSyncedToByteStream(true)
{
ASSERT(contextElement);
}
SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, SVGPathSegRole role, PassOwnPtr<SVGPathByteStream> byteStream)
: m_contextElement(contextElement)
, m_role(role)
, m_byteStream(byteStream)
, m_listSyncedToByteStream(true)
{
ASSERT(contextElement);
}
SVGPathSegList::~SVGPathSegList()
{
}
PassRefPtr<SVGPathSegList> SVGPathSegList::clone()
{
RefPtr<SVGPathSegList> svgPathSegList = adoptRef(new SVGPathSegList(m_contextElement, m_role, byteStream()->copy()));
svgPathSegList->invalidateList();
return svgPathSegList.release();
}
PassRefPtr<NewSVGPropertyBase> SVGPathSegList::cloneForAnimation(const String& value) const
{
RefPtr<SVGPathSegList> svgPathSegList = SVGPathSegList::create(m_contextElement);
svgPathSegList->setValueAsString(value, IGNORE_EXCEPTION);
return svgPathSegList;
}
const SVGPathByteStream* SVGPathSegList::byteStream() const
{
if (!m_byteStream) {
m_byteStream = SVGPathByteStream::create();
if (!Base::isEmpty()) {
SVGPathByteStreamBuilder builder;
builder.setCurrentByteStream(m_byteStream.get());
SVGPathSegListSource source(begin(), end());
SVGPathParser parser;
parser.setCurrentConsumer(&builder);
parser.setCurrentSource(&source);
parser.parsePathDataFromSource(UnalteredParsing);
}
}
return m_byteStream.get();
}
void SVGPathSegList::updateListFromByteStream()
{
if (m_listSyncedToByteStream)
return;
Base::clear();
if (m_byteStream && !m_byteStream->isEmpty()) {
SVGPathSegListBuilder builder;
builder.setCurrentSVGPathElement(m_contextElement);
builder.setCurrentSVGPathSegList(this);
builder.setCurrentSVGPathSegRole(PathSegUnalteredRole);
SVGPathByteStreamSource source(m_byteStream.get());
SVGPathParser parser;
parser.setCurrentConsumer(&builder);
parser.setCurrentSource(&source);
parser.parsePathDataFromSource(UnalteredParsing);
}
m_listSyncedToByteStream = true;
}
void SVGPathSegList::invalidateList()
{
m_listSyncedToByteStream = false;
Base::clear();
}
PassRefPtr<SVGPathSeg> SVGPathSegList::appendItem(PassRefPtr<SVGPathSeg> passItem)
{
updateListFromByteStream();
RefPtr<SVGPathSeg> item = Base::appendItem(passItem);
if (m_byteStream) {
SVGPathByteStreamBuilder builder;
builder.setCurrentByteStream(m_byteStream.get());
SVGPathSegListSource source(lastAppended(), end());
SVGPathParser parser;
parser.setCurrentConsumer(&builder);
parser.setCurrentSource(&source);
parser.parsePathDataFromSource(UnalteredParsing, false);
}
return item.release();
}
String SVGPathSegList::valueAsString() const
{
String string;
buildStringFromByteStream(byteStream(), string, UnalteredParsing);
return string;
}
void SVGPathSegList::setValueAsString(const String& string, ExceptionState& exceptionState)
{
invalidateList();
if (!m_byteStream)
m_byteStream = SVGPathByteStream::create();
if (!buildSVGPathByteStreamFromString(string, m_byteStream.get(), UnalteredParsing))
exceptionState.throwDOMException(SyntaxError, "Problem parsing path \"" + string + "\"");
}
void SVGPathSegList::add(PassRefPtr<NewSVGPropertyBase> other, SVGElement*)
{
RefPtr<SVGPathSegList> otherList = toSVGPathSegList(other);
if (length() != otherList->length())
return;
byteStream(); // create |m_byteStream| if not exist.
addToSVGPathByteStream(m_byteStream.get(), otherList->byteStream());
invalidateList();
}
void SVGPathSegList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<NewSVGPropertyBase> fromValue, PassRefPtr<NewSVGPropertyBase> toValue, PassRefPtr<NewSVGPropertyBase> toAtEndOfDurationValue, SVGElement*)
{
invalidateList();
ASSERT(animationElement);
bool isToAnimation = animationElement->animationMode() == ToAnimation;
const RefPtr<SVGPathSegList> from = toSVGPathSegList(fromValue);
const RefPtr<SVGPathSegList> to = toSVGPathSegList(toValue);
const RefPtr<SVGPathSegList> toAtEndOfDuration = toSVGPathSegList(toAtEndOfDurationValue);
const SVGPathByteStream* toStream = to->byteStream();
const SVGPathByteStream* fromStream = from->byteStream();
OwnPtr<SVGPathByteStream> copy;
// If no 'to' value is given, nothing to animate.
if (!toStream->size())
return;
if (isToAnimation) {
copy = byteStream()->copy();
fromStream = copy.get();
}
// If the 'from' value is given and it's length doesn't match the 'to' value list length, fallback to a discrete animation.
if (fromStream->size() != toStream->size() && fromStream->size()) {
if (percentage < 0.5) {
if (!isToAnimation) {
m_byteStream = fromStream->copy();
return;
}
} else {
m_byteStream = toStream->copy();
return;
}
}
OwnPtr<SVGPathByteStream> lastAnimatedStream = m_byteStream.release();
m_byteStream = SVGPathByteStream::create();
SVGPathByteStreamBuilder builder;
builder.setCurrentByteStream(m_byteStream.get());
SVGPathByteStreamSource fromSource(fromStream);
SVGPathByteStreamSource toSource(toStream);
SVGPathBlender blender;
blender.blendAnimatedPath(percentage, &fromSource, &toSource, &builder);
// Handle additive='sum'.
if (!fromStream->size() || (animationElement->isAdditive() && !isToAnimation))
addToSVGPathByteStream(m_byteStream.get(), lastAnimatedStream.get());
// Handle accumulate='sum'.
if (animationElement->isAccumulated() && repeatCount) {
const SVGPathByteStream* toAtEndOfDurationStream = toAtEndOfDuration->byteStream();
addToSVGPathByteStream(m_byteStream.get(), toAtEndOfDurationStream, repeatCount);
}
}
float SVGPathSegList::calculateDistance(PassRefPtr<NewSVGPropertyBase> to, SVGElement*)
{
// FIXME: Support paced animations.
return -1;
}
}