Home | History | Annotate | Download | only in svg
      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 WebCore {
     39 
     40 SVGPathSegList::SVGPathSegList(SVGPathElement* contextElement, SVGPathSegRole role)
     41     : m_contextElement(contextElement)
     42     , m_role(role)
     43     , m_listSyncedToByteStream(true)
     44 {
     45     ASSERT(contextElement);
     46 }
     47 
     48 SVGPathSegList::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 
     57 SVGPathSegList::~SVGPathSegList()
     58 {
     59 }
     60 
     61 PassRefPtr<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 
     68 PassRefPtr<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 
     75 const 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 
     96 void 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 
    120 void SVGPathSegList::invalidateList()
    121 {
    122     m_listSyncedToByteStream = false;
    123     Base::clear();
    124 }
    125 
    126 PassRefPtr<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 
    146 String SVGPathSegList::valueAsString() const
    147 {
    148     String string;
    149     buildStringFromByteStream(byteStream(), string, UnalteredParsing);
    150     return string;
    151 }
    152 
    153 void 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 
    162 void 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 
    173 void 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 
    233 float SVGPathSegList::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement*)
    234 {
    235     // FIXME: Support paced animations.
    236     return -1;
    237 }
    238 
    239 }
    240