1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23#include "config.h" 24 25#include "core/svg/SVGPathSegList.h" 26 27#include "core/SVGNames.h" 28#include "core/svg/SVGAnimationElement.h" 29#include "core/svg/SVGPathBlender.h" 30#include "core/svg/SVGPathByteStreamBuilder.h" 31#include "core/svg/SVGPathByteStreamSource.h" 32#include "core/svg/SVGPathElement.h" 33#include "core/svg/SVGPathParser.h" 34#include "core/svg/SVGPathSegListBuilder.h" 35#include "core/svg/SVGPathSegListSource.h" 36#include "core/svg/SVGPathUtilities.h" 37 38namespace WebCore { 39 40SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, SVGPathSegRole role) 41 : m_contextElement(contextElement) 42 , m_role(role) 43 , m_listSyncedToByteStream(true) 44{ 45 ASSERT(contextElement); 46} 47 48SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, SVGPathSegRole role, PassOwnPtr<SVGPathByteStream> byteStream) 49 : m_contextElement(contextElement) 50 , m_role(role) 51 , m_byteStream(byteStream) 52 , m_listSyncedToByteStream(true) 53{ 54 ASSERT(contextElement); 55} 56 57SVGPathSegList::~SVGPathSegList() 58{ 59} 60 61PassRefPtr<SVGPathSegList> SVGPathSegList::clone() 62{ 63 RefPtr<SVGPathSegList> svgPathSegList = adoptRef(new SVGPathSegList(m_contextElement, m_role, byteStream()->copy())); 64 svgPathSegList->invalidateList(); 65 return svgPathSegList.release(); 66} 67 68PassRefPtr<SVGPropertyBase> SVGPathSegList::cloneForAnimation(const String& value) const 69{ 70 RefPtr<SVGPathSegList> svgPathSegList = SVGPathSegList::create(m_contextElement); 71 svgPathSegList->setValueAsString(value, IGNORE_EXCEPTION); 72 return svgPathSegList; 73} 74 75const SVGPathByteStream* SVGPathSegList::byteStream() const 76{ 77 if (!m_byteStream) { 78 m_byteStream = SVGPathByteStream::create(); 79 80 if (!Base::isEmpty()) { 81 SVGPathByteStreamBuilder builder; 82 builder.setCurrentByteStream(m_byteStream.get()); 83 84 SVGPathSegListSource source(begin(), end()); 85 86 SVGPathParser parser; 87 parser.setCurrentConsumer(&builder); 88 parser.setCurrentSource(&source); 89 parser.parsePathDataFromSource(UnalteredParsing); 90 } 91 } 92 93 return m_byteStream.get(); 94} 95 96void SVGPathSegList::updateListFromByteStream() 97{ 98 if (m_listSyncedToByteStream) 99 return; 100 101 Base::clear(); 102 103 if (m_byteStream && !m_byteStream->isEmpty()) { 104 SVGPathSegListBuilder builder; 105 builder.setCurrentSVGPathElement(m_contextElement); 106 builder.setCurrentSVGPathSegList(this); 107 builder.setCurrentSVGPathSegRole(PathSegUnalteredRole); 108 109 SVGPathByteStreamSource source(m_byteStream.get()); 110 111 SVGPathParser parser; 112 parser.setCurrentConsumer(&builder); 113 parser.setCurrentSource(&source); 114 parser.parsePathDataFromSource(UnalteredParsing); 115 } 116 117 m_listSyncedToByteStream = true; 118} 119 120void SVGPathSegList::invalidateList() 121{ 122 m_listSyncedToByteStream = false; 123 Base::clear(); 124} 125 126PassRefPtr<SVGPathSeg> SVGPathSegList::appendItem(PassRefPtr<SVGPathSeg> passItem) 127{ 128 updateListFromByteStream(); 129 RefPtr<SVGPathSeg> item = Base::appendItem(passItem); 130 131 if (m_byteStream) { 132 SVGPathByteStreamBuilder builder; 133 builder.setCurrentByteStream(m_byteStream.get()); 134 135 SVGPathSegListSource source(lastAppended(), end()); 136 137 SVGPathParser parser; 138 parser.setCurrentConsumer(&builder); 139 parser.setCurrentSource(&source); 140 parser.parsePathDataFromSource(UnalteredParsing, false); 141 } 142 143 return item.release(); 144} 145 146String SVGPathSegList::valueAsString() const 147{ 148 String string; 149 buildStringFromByteStream(byteStream(), string, UnalteredParsing); 150 return string; 151} 152 153void SVGPathSegList::setValueAsString(const String& string, ExceptionState& exceptionState) 154{ 155 invalidateList(); 156 if (!m_byteStream) 157 m_byteStream = SVGPathByteStream::create(); 158 if (!buildSVGPathByteStreamFromString(string, m_byteStream.get(), UnalteredParsing)) 159 exceptionState.throwDOMException(SyntaxError, "Problem parsing path \"" + string + "\""); 160} 161 162void SVGPathSegList::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*) 163{ 164 RefPtr<SVGPathSegList> otherList = toSVGPathSegList(other); 165 if (length() != otherList->length()) 166 return; 167 168 byteStream(); // create |m_byteStream| if not exist. 169 addToSVGPathByteStream(m_byteStream.get(), otherList->byteStream()); 170 invalidateList(); 171} 172 173void SVGPathSegList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement*) 174{ 175 invalidateList(); 176 177 ASSERT(animationElement); 178 bool isToAnimation = animationElement->animationMode() == ToAnimation; 179 180 const RefPtr<SVGPathSegList> from = toSVGPathSegList(fromValue); 181 const RefPtr<SVGPathSegList> to = toSVGPathSegList(toValue); 182 const RefPtr<SVGPathSegList> toAtEndOfDuration = toSVGPathSegList(toAtEndOfDurationValue); 183 184 const SVGPathByteStream* toStream = to->byteStream(); 185 const SVGPathByteStream* fromStream = from->byteStream(); 186 OwnPtr<SVGPathByteStream> copy; 187 188 // If no 'to' value is given, nothing to animate. 189 if (!toStream->size()) 190 return; 191 192 if (isToAnimation) { 193 copy = byteStream()->copy(); 194 fromStream = copy.get(); 195 } 196 197 // If the 'from' value is given and it's length doesn't match the 'to' value list length, fallback to a discrete animation. 198 if (fromStream->size() != toStream->size() && fromStream->size()) { 199 if (percentage < 0.5) { 200 if (!isToAnimation) { 201 m_byteStream = fromStream->copy(); 202 return; 203 } 204 } else { 205 m_byteStream = toStream->copy(); 206 return; 207 } 208 } 209 210 OwnPtr<SVGPathByteStream> lastAnimatedStream = m_byteStream.release(); 211 212 m_byteStream = SVGPathByteStream::create(); 213 SVGPathByteStreamBuilder builder; 214 builder.setCurrentByteStream(m_byteStream.get()); 215 216 SVGPathByteStreamSource fromSource(fromStream); 217 SVGPathByteStreamSource toSource(toStream); 218 219 SVGPathBlender blender; 220 blender.blendAnimatedPath(percentage, &fromSource, &toSource, &builder); 221 222 // Handle additive='sum'. 223 if (!fromStream->size() || (animationElement->isAdditive() && !isToAnimation)) 224 addToSVGPathByteStream(m_byteStream.get(), lastAnimatedStream.get()); 225 226 // Handle accumulate='sum'. 227 if (animationElement->isAccumulated() && repeatCount) { 228 const SVGPathByteStream* toAtEndOfDurationStream = toAtEndOfDuration->byteStream(); 229 addToSVGPathByteStream(m_byteStream.get(), toAtEndOfDurationStream, repeatCount); 230 } 231} 232 233float SVGPathSegList::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement*) 234{ 235 // FIXME: Support paced animations. 236 return -1; 237} 238 239} 240