1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric (at) 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 38 namespace blink { 39 40 SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement) 41 : m_contextElement(contextElement) 42 , m_listSyncedToByteStream(true) 43 { 44 ASSERT(contextElement); 45 } 46 47 SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, PassOwnPtr<SVGPathByteStream> byteStream) 48 : m_contextElement(contextElement) 49 , m_byteStream(byteStream) 50 , m_listSyncedToByteStream(true) 51 { 52 ASSERT(contextElement); 53 } 54 55 SVGPathSegList::~SVGPathSegList() 56 { 57 } 58 59 PassRefPtr<SVGPathSegList> SVGPathSegList::clone() 60 { 61 RefPtr<SVGPathSegList> svgPathSegList = adoptRef(new SVGPathSegList(m_contextElement, byteStream()->copy())); 62 svgPathSegList->invalidateList(); 63 return svgPathSegList.release(); 64 } 65 66 PassRefPtr<SVGPropertyBase> SVGPathSegList::cloneForAnimation(const String& value) const 67 { 68 RefPtr<SVGPathSegList> svgPathSegList = SVGPathSegList::create(m_contextElement); 69 svgPathSegList->setValueAsString(value, IGNORE_EXCEPTION); 70 return svgPathSegList; 71 } 72 73 const SVGPathByteStream* SVGPathSegList::byteStream() const 74 { 75 if (!m_byteStream) { 76 m_byteStream = SVGPathByteStream::create(); 77 78 if (!Base::isEmpty()) { 79 SVGPathByteStreamBuilder builder; 80 builder.setCurrentByteStream(m_byteStream.get()); 81 82 SVGPathSegListSource source(begin(), end()); 83 84 SVGPathParser parser; 85 parser.setCurrentConsumer(&builder); 86 parser.setCurrentSource(&source); 87 parser.parsePathDataFromSource(UnalteredParsing); 88 } 89 } 90 91 return m_byteStream.get(); 92 } 93 94 void SVGPathSegList::updateListFromByteStream() 95 { 96 if (m_listSyncedToByteStream) 97 return; 98 99 Base::clear(); 100 101 if (m_byteStream && !m_byteStream->isEmpty()) { 102 SVGPathSegListBuilder builder; 103 builder.setCurrentSVGPathElement(m_contextElement); 104 builder.setCurrentSVGPathSegList(this); 105 106 SVGPathByteStreamSource source(m_byteStream.get()); 107 108 SVGPathParser parser; 109 parser.setCurrentConsumer(&builder); 110 parser.setCurrentSource(&source); 111 parser.parsePathDataFromSource(UnalteredParsing); 112 } 113 114 m_listSyncedToByteStream = true; 115 } 116 117 void SVGPathSegList::invalidateList() 118 { 119 m_listSyncedToByteStream = false; 120 Base::clear(); 121 } 122 123 PassRefPtr<SVGPathSeg> SVGPathSegList::appendItem(PassRefPtr<SVGPathSeg> passItem) 124 { 125 updateListFromByteStream(); 126 RefPtr<SVGPathSeg> item = Base::appendItem(passItem); 127 128 if (m_byteStream) { 129 SVGPathByteStreamBuilder builder; 130 builder.setCurrentByteStream(m_byteStream.get()); 131 132 SVGPathSegListSource source(lastAppended(), end()); 133 134 SVGPathParser parser; 135 parser.setCurrentConsumer(&builder); 136 parser.setCurrentSource(&source); 137 parser.parsePathDataFromSource(UnalteredParsing, false); 138 } 139 140 return item.release(); 141 } 142 143 String SVGPathSegList::valueAsString() const 144 { 145 String string; 146 buildStringFromByteStream(byteStream(), string, UnalteredParsing); 147 return string; 148 } 149 150 void SVGPathSegList::setValueAsString(const String& string, ExceptionState& exceptionState) 151 { 152 invalidateList(); 153 if (!m_byteStream) 154 m_byteStream = SVGPathByteStream::create(); 155 if (!buildSVGPathByteStreamFromString(string, m_byteStream.get(), UnalteredParsing)) 156 exceptionState.throwDOMException(SyntaxError, "Problem parsing path \"" + string + "\""); 157 } 158 159 void SVGPathSegList::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*) 160 { 161 RefPtr<SVGPathSegList> otherList = toSVGPathSegList(other); 162 if (length() != otherList->length()) 163 return; 164 165 byteStream(); // create |m_byteStream| if not exist. 166 addToSVGPathByteStream(m_byteStream.get(), otherList->byteStream()); 167 invalidateList(); 168 } 169 170 void SVGPathSegList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement*) 171 { 172 invalidateList(); 173 174 ASSERT(animationElement); 175 bool isToAnimation = animationElement->animationMode() == ToAnimation; 176 177 const RefPtr<SVGPathSegList> from = toSVGPathSegList(fromValue); 178 const RefPtr<SVGPathSegList> to = toSVGPathSegList(toValue); 179 const RefPtr<SVGPathSegList> toAtEndOfDuration = toSVGPathSegList(toAtEndOfDurationValue); 180 181 const SVGPathByteStream* toStream = to->byteStream(); 182 const SVGPathByteStream* fromStream = from->byteStream(); 183 OwnPtr<SVGPathByteStream> copy; 184 185 // If no 'to' value is given, nothing to animate. 186 if (!toStream->size()) 187 return; 188 189 if (isToAnimation) { 190 copy = byteStream()->copy(); 191 fromStream = copy.get(); 192 } 193 194 // If the 'from' value is given and it's length doesn't match the 'to' value list length, fallback to a discrete animation. 195 if (fromStream->size() != toStream->size() && fromStream->size()) { 196 if (percentage < 0.5) { 197 if (!isToAnimation) { 198 m_byteStream = fromStream->copy(); 199 return; 200 } 201 } else { 202 m_byteStream = toStream->copy(); 203 return; 204 } 205 } 206 207 OwnPtr<SVGPathByteStream> lastAnimatedStream = m_byteStream.release(); 208 209 m_byteStream = SVGPathByteStream::create(); 210 SVGPathByteStreamBuilder builder; 211 builder.setCurrentByteStream(m_byteStream.get()); 212 213 SVGPathByteStreamSource fromSource(fromStream); 214 SVGPathByteStreamSource toSource(toStream); 215 216 SVGPathBlender blender; 217 blender.blendAnimatedPath(percentage, &fromSource, &toSource, &builder); 218 219 // Handle additive='sum'. 220 if (!fromStream->size() || (animationElement->isAdditive() && !isToAnimation)) 221 addToSVGPathByteStream(m_byteStream.get(), lastAnimatedStream.get()); 222 223 // Handle accumulate='sum'. 224 if (animationElement->isAccumulated() && repeatCount) { 225 const SVGPathByteStream* toAtEndOfDurationStream = toAtEndOfDuration->byteStream(); 226 addToSVGPathByteStream(m_byteStream.get(), toAtEndOfDurationStream, repeatCount); 227 } 228 } 229 230 float SVGPathSegList::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement*) 231 { 232 // FIXME: Support paced animations. 233 return -1; 234 } 235 236 } 237