Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/animation/KeyframeAnimationEffect.h"
     33 
     34 #include "core/animation/TimedItem.h"
     35 #include "wtf/text/StringHash.h"
     36 
     37 namespace {
     38 
     39 using namespace WebCore;
     40 
     41 class ReplaceCompositableValue : public AnimationEffect::CompositableValue {
     42 public:
     43     static PassRefPtr<ReplaceCompositableValue> create(const AnimatableValue* value)
     44     {
     45         return adoptRef(new ReplaceCompositableValue(value));
     46     }
     47     virtual bool dependsOnUnderlyingValue() const
     48     {
     49         return false;
     50     }
     51     virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const
     52     {
     53         return PassRefPtr<AnimatableValue>(m_value);
     54     }
     55 private:
     56     ReplaceCompositableValue(const AnimatableValue* value)
     57         : m_value(const_cast<AnimatableValue*>(value))
     58     {
     59     }
     60     RefPtr<AnimatableValue> m_value;
     61 };
     62 
     63 class AddCompositableValue : public AnimationEffect::CompositableValue {
     64 public:
     65     static PassRefPtr<AddCompositableValue> create(const AnimatableValue* value)
     66     {
     67         return adoptRef(new AddCompositableValue(value));
     68     }
     69     virtual bool dependsOnUnderlyingValue() const
     70     {
     71         return true;
     72     }
     73     virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const
     74     {
     75         return AnimatableValue::add(underlyingValue, m_value.get());
     76     }
     77 private:
     78     AddCompositableValue(const AnimatableValue* value)
     79         : m_value(const_cast<AnimatableValue*>(value))
     80     {
     81     }
     82     RefPtr<AnimatableValue> m_value;
     83 };
     84 
     85 class BlendedCompositableValue : public AnimationEffect::CompositableValue {
     86 public:
     87     static PassRefPtr<BlendedCompositableValue> create(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction)
     88     {
     89         return adoptRef(new BlendedCompositableValue(before, after, fraction));
     90     }
     91     virtual bool dependsOnUnderlyingValue() const
     92     {
     93         return m_dependsOnUnderlyingValue;
     94     }
     95     virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const
     96     {
     97         return AnimatableValue::interpolate(m_before->compositeOnto(underlyingValue).get(), m_after->compositeOnto(underlyingValue).get(), m_fraction);
     98     }
     99 private:
    100     BlendedCompositableValue(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction)
    101         : m_before(const_cast<AnimationEffect::CompositableValue*>(before))
    102         , m_after(const_cast<AnimationEffect::CompositableValue*>(after))
    103         , m_fraction(fraction)
    104         , m_dependsOnUnderlyingValue(before->dependsOnUnderlyingValue() || after->dependsOnUnderlyingValue())
    105     { }
    106     RefPtr<AnimationEffect::CompositableValue> m_before;
    107     RefPtr<AnimationEffect::CompositableValue> m_after;
    108     double m_fraction;
    109     bool m_dependsOnUnderlyingValue;
    110 };
    111 
    112 } // namespace
    113 
    114 
    115 namespace WebCore {
    116 
    117 Keyframe::Keyframe()
    118     : m_offset(nullValue())
    119     , m_composite(AnimationEffect::CompositeReplace)
    120 { }
    121 
    122 Keyframe::Keyframe(const Keyframe& copyFrom)
    123     : m_offset(copyFrom.m_offset)
    124     , m_composite(copyFrom.m_composite)
    125 {
    126     for (PropertyValueMap::const_iterator iter = copyFrom.m_propertyValues.begin(); iter != copyFrom.m_propertyValues.end(); ++iter)
    127         setPropertyValue(iter->key, iter->value.get());
    128 }
    129 
    130 void Keyframe::setPropertyValue(CSSPropertyID property, const AnimatableValue* value)
    131 {
    132     m_propertyValues.add(property, const_cast<AnimatableValue*>(value));
    133 }
    134 
    135 void Keyframe::clearPropertyValue(CSSPropertyID property)
    136 {
    137     m_propertyValues.remove(property);
    138 }
    139 
    140 const AnimatableValue* Keyframe::propertyValue(CSSPropertyID property) const
    141 {
    142     ASSERT(m_propertyValues.contains(property));
    143     return m_propertyValues.get(property);
    144 }
    145 
    146 PropertySet Keyframe::properties() const
    147 {
    148     // This is not used in time-critical code, so we probably don't need to
    149     // worry about caching this result.
    150     PropertySet properties;
    151     for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter != m_propertyValues.end(); ++iter)
    152         properties.add(*iter.keys());
    153     return properties;
    154 }
    155 
    156 PassRefPtr<Keyframe> Keyframe::cloneWithOffset(double offset) const
    157 {
    158     RefPtr<Keyframe> theClone = clone();
    159     theClone->setOffset(offset);
    160     return theClone.release();
    161 }
    162 
    163 KeyframeAnimationEffect::KeyframeAnimationEffect(const KeyframeVector& keyframes)
    164     : m_keyframes(keyframes)
    165 {
    166 }
    167 
    168 PropertySet KeyframeAnimationEffect::properties() const
    169 {
    170     PropertySet result;
    171     const KeyframeVector& frames = getFrames();
    172     if (!frames.size()) {
    173         return result;
    174     }
    175     result = frames[0]->properties();
    176     for (size_t i = 1; i < frames.size(); i++) {
    177         PropertySet extras = frames[i]->properties();
    178         for (PropertySet::const_iterator it = extras.begin(); it != extras.end(); ++it) {
    179             result.add(*it);
    180         }
    181     }
    182     return result;
    183 }
    184 
    185 PassOwnPtr<AnimationEffect::CompositableValueList> KeyframeAnimationEffect::sample(int iteration, double fraction) const
    186 {
    187     ASSERT(iteration >= 0);
    188     ASSERT(!isNull(fraction));
    189     const_cast<KeyframeAnimationEffect*>(this)->ensureKeyframeGroups();
    190     OwnPtr<CompositableValueList> map = adoptPtr(new CompositableValueList());
    191     for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter)
    192         map->append(std::make_pair(iter->key, iter->value->sample(iteration, fraction)));
    193     return map.release();
    194 }
    195 
    196 KeyframeAnimationEffect::KeyframeVector KeyframeAnimationEffect::normalizedKeyframes() const
    197 {
    198     KeyframeVector keyframes = m_keyframes;
    199 
    200     // Set offsets at 0.0 and 1.0 at ends if unset.
    201     if (keyframes.size() >= 2) {
    202         Keyframe* firstKeyframe = keyframes.first().get();
    203         if (isNull(firstKeyframe->offset()))
    204             firstKeyframe->setOffset(0.0);
    205     }
    206     if (keyframes.size() >= 1) {
    207         Keyframe* lastKeyframe = keyframes.last().get();
    208         if (lastKeyframe && isNull(lastKeyframe->offset()))
    209             lastKeyframe->setOffset(1.0);
    210     }
    211 
    212     // FIXME: Distribute offsets where missing.
    213     for (KeyframeVector::iterator iter = keyframes.begin(); iter != keyframes.end(); ++iter)
    214         ASSERT(!isNull((*iter)->offset()));
    215 
    216     // Sort by offset.
    217     std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
    218     return keyframes;
    219 }
    220 
    221 void KeyframeAnimationEffect::ensureKeyframeGroups() const
    222 {
    223     if (m_keyframeGroups)
    224         return;
    225 
    226     m_keyframeGroups = adoptPtr(new KeyframeGroupMap);
    227     const KeyframeVector& keyframes = normalizedKeyframes();
    228     for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) {
    229         const Keyframe* keyframe = keyframeIter->get();
    230         PropertySet keyframeProperties = keyframe->properties();
    231         for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) {
    232             CSSPropertyID property = *propertyIter;
    233             KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property);
    234             if (groupIter == m_keyframeGroups->end()) {
    235                 KeyframeGroupMap::AddResult result = m_keyframeGroups->add(property, adoptPtr(new PropertySpecificKeyframeGroup));
    236                 ASSERT(result.isNewEntry);
    237                 groupIter = result.iterator;
    238             }
    239             groupIter->value->appendKeyframe(adoptPtr(
    240                 new PropertySpecificKeyframe(keyframe->offset(), keyframe->propertyValue(property), keyframe->composite())));
    241         }
    242     }
    243 
    244     // Add synthetic keyframes.
    245     for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) {
    246         iter->value->addSyntheticKeyframeIfRequired();
    247         iter->value->removeRedundantKeyframes();
    248     }
    249 }
    250 
    251 
    252 KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, const AnimatableValue* value, CompositeOperation composite)
    253     : m_offset(offset)
    254     , m_value(composite == AnimationEffect::CompositeReplace ?
    255         static_cast<PassRefPtr<CompositableValue> >(ReplaceCompositableValue::create(value)) :
    256         static_cast<PassRefPtr<CompositableValue> >(AddCompositableValue::create(value)))
    257 {
    258 }
    259 
    260 KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<CompositableValue> value)
    261     : m_offset(offset)
    262     , m_value(value)
    263 {
    264     ASSERT(!isNull(m_offset));
    265 }
    266 
    267 PassOwnPtr<KeyframeAnimationEffect::PropertySpecificKeyframe> KeyframeAnimationEffect::PropertySpecificKeyframe::cloneWithOffset(double offset) const
    268 {
    269     return adoptPtr(new PropertySpecificKeyframe(offset, PassRefPtr<CompositableValue>(m_value)));
    270 }
    271 
    272 
    273 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtr<PropertySpecificKeyframe> keyframe)
    274 {
    275     ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset());
    276     m_keyframes.append(keyframe);
    277 }
    278 
    279 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::removeRedundantKeyframes()
    280 {
    281     // As an optimization, removes keyframes in the following categories, as
    282     // they will never be used by sample().
    283     // - End keyframes with the same offset as their neighbor
    284     // - Interior keyframes with the same offset as both their neighbors
    285     // Note that synthetic keyframes must be added before this method is
    286     // called.
    287     ASSERT(m_keyframes.size() >= 2);
    288     for (int i = m_keyframes.size() - 1; i >= 0; --i) {
    289         double offset = m_keyframes[i]->offset();
    290         bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset;
    291         bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.size() - 1) || m_keyframes[i + 1]->offset() == offset;
    292         if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor)
    293             m_keyframes.remove(i);
    294     }
    295     ASSERT(m_keyframes.size() >= 2);
    296 }
    297 
    298 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired()
    299 {
    300     ASSERT(!m_keyframes.isEmpty());
    301     double offset = m_keyframes.first()->offset();
    302     bool allOffsetsEqual = true;
    303     for (PropertySpecificKeyframeVector::const_iterator iter = m_keyframes.begin() + 1; iter != m_keyframes.end(); ++iter) {
    304         if ((*iter)->offset() != offset) {
    305             allOffsetsEqual = false;
    306             break;
    307         }
    308     }
    309     if (!allOffsetsEqual)
    310         return;
    311 
    312     if (!offset)
    313         appendKeyframe(m_keyframes.first()->cloneWithOffset(1.0));
    314     else
    315         m_keyframes.insert(0, adoptPtr(new PropertySpecificKeyframe(0.0, AnimatableValue::neutralValue(), CompositeAdd)));
    316 }
    317 
    318 PassRefPtr<AnimationEffect::CompositableValue> KeyframeAnimationEffect::PropertySpecificKeyframeGroup::sample(int iteration, double offset) const
    319 {
    320     // FIXME: Implement accumulation.
    321     ASSERT_UNUSED(iteration, iteration >= 0);
    322     ASSERT(!isNull(offset));
    323 
    324     // Bail if offset is null, as this can lead to buffer overflow below.
    325     if (isNull(offset))
    326         return const_cast<CompositableValue*>(m_keyframes.first()->value());
    327 
    328     double minimumOffset = m_keyframes.first()->offset();
    329     double maximumOffset = m_keyframes.last()->offset();
    330     ASSERT(minimumOffset != maximumOffset);
    331 
    332     PropertySpecificKeyframeVector::const_iterator before;
    333     PropertySpecificKeyframeVector::const_iterator after;
    334 
    335     // Note that this algorithm is simpler than that in the spec because we
    336     // have removed keyframes with equal offsets in
    337     // removeRedundantKeyframes().
    338     if (offset < minimumOffset) {
    339         before = m_keyframes.begin();
    340         after = before + 1;
    341         ASSERT((*before)->offset() > offset);
    342         ASSERT((*after)->offset() > offset);
    343     } else if (offset >= maximumOffset) {
    344         after = m_keyframes.end() - 1;
    345         before = after - 1;
    346         ASSERT((*before)->offset() < offset);
    347         ASSERT((*after)->offset() <= offset);
    348     } else {
    349         // FIXME: This is inefficient for large numbers of keyframes. Consider
    350         // using binary search.
    351         after = m_keyframes.begin();
    352         while ((*after)->offset() <= offset)
    353             ++after;
    354         before = after - 1;
    355         ASSERT((*before)->offset() <= offset);
    356         ASSERT((*after)->offset() > offset);
    357     }
    358 
    359     if ((*before)->offset() == offset)
    360         return const_cast<CompositableValue*>((*before)->value());
    361     if ((*after)->offset() == offset)
    362         return const_cast<CompositableValue*>((*after)->value());
    363     return BlendedCompositableValue::create((*before)->value(), (*after)->value(),
    364         (offset - (*before)->offset()) / ((*after)->offset() - (*before)->offset()));
    365 }
    366 
    367 } // namespace
    368