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 "wtf/MathExtras.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(std::numeric_limits<double>::quiet_NaN())
    119     , m_composite(AnimationEffect::CompositeReplace)
    120 { }
    121 
    122 void Keyframe::setPropertyValue(CSSPropertyID property, const AnimatableValue* value)
    123 {
    124     m_propertyValues.add(property, const_cast<AnimatableValue*>(value));
    125 }
    126 
    127 const AnimatableValue* Keyframe::propertyValue(CSSPropertyID property) const
    128 {
    129     ASSERT(m_propertyValues.contains(property));
    130     return m_propertyValues.get(property);
    131 }
    132 
    133 PropertySet Keyframe::properties() const
    134 {
    135     // This is only used when setting up the keyframe groups, so there's no
    136     // need to cache the result.
    137     PropertySet properties;
    138     for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter != m_propertyValues.end(); ++iter)
    139         properties.add(*iter.keys());
    140     return properties;
    141 }
    142 
    143 
    144 KeyframeAnimationEffect::KeyframeAnimationEffect(const KeyframeVector& keyframes)
    145     : m_keyframes(keyframes)
    146 {
    147 }
    148 
    149 PassOwnPtr<AnimationEffect::CompositableValueMap> KeyframeAnimationEffect::sample(int iteration, double fraction) const
    150 {
    151     const_cast<KeyframeAnimationEffect*>(this)->ensureKeyframeGroups();
    152     OwnPtr<CompositableValueMap> map = adoptPtr(new CompositableValueMap());
    153     for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter)
    154         map->add(iter->key, iter->value->sample(iteration, fraction));
    155     return map.release();
    156 }
    157 
    158 KeyframeAnimationEffect::KeyframeVector KeyframeAnimationEffect::normalizedKeyframes() const
    159 {
    160     KeyframeVector keyframes = m_keyframes;
    161 
    162     // Set offsets at 0.0 and 1.0 at ends if unset.
    163     if (keyframes.size() >= 2) {
    164         Keyframe* firstKeyframe = keyframes.first().get();
    165         if (std::isnan(firstKeyframe->offset()))
    166             firstKeyframe->setOffset(0.0);
    167     }
    168     if (keyframes.size() >= 1) {
    169         Keyframe* lastKeyframe = keyframes.last().get();
    170         if (lastKeyframe && std::isnan(lastKeyframe->offset()))
    171             lastKeyframe->setOffset(1.0);
    172     }
    173 
    174     // FIXME: Distribute offsets where missing.
    175     for (KeyframeVector::iterator iter = keyframes.begin(); iter != keyframes.end(); ++iter)
    176         ASSERT(!std::isnan((*iter)->offset()));
    177 
    178     // Sort by offset.
    179     std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
    180     return keyframes;
    181 }
    182 
    183 void KeyframeAnimationEffect::ensureKeyframeGroups()
    184 {
    185     if (m_keyframeGroups)
    186         return;
    187 
    188     m_keyframeGroups = adoptPtr(new KeyframeGroupMap);
    189     const KeyframeVector& keyframes = normalizedKeyframes();
    190     for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) {
    191         const Keyframe* keyframe = keyframeIter->get();
    192         PropertySet keyframeProperties = keyframe->properties();
    193         for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) {
    194             CSSPropertyID property = *propertyIter;
    195             KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property);
    196             if (groupIter == m_keyframeGroups->end()) {
    197                 KeyframeGroupMap::AddResult result = m_keyframeGroups->add(property, adoptPtr(new PropertySpecificKeyframeGroup));
    198                 ASSERT(result.isNewEntry);
    199                 groupIter = result.iterator;
    200             }
    201             groupIter->value->appendKeyframe(adoptPtr(
    202                 new PropertySpecificKeyframe(keyframe->offset(), keyframe->propertyValue(property), keyframe->composite())));
    203         }
    204     }
    205 
    206     // Add synthetic keyframes.
    207     for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) {
    208         iter->value->addSyntheticKeyframeIfRequired();
    209         iter->value->removeRedundantKeyframes();
    210     }
    211 }
    212 
    213 
    214 KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, const AnimatableValue* value, CompositeOperation composite)
    215     : m_offset(offset)
    216     , m_value(composite == AnimationEffect::CompositeReplace ?
    217         static_cast<PassRefPtr<CompositableValue> >(ReplaceCompositableValue::create(value)) :
    218         static_cast<PassRefPtr<CompositableValue> >(AddCompositableValue::create(value)))
    219 {
    220 }
    221 
    222 KeyframeAnimationEffect::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<CompositableValue> value)
    223     : m_offset(offset)
    224     , m_value(value)
    225 {
    226     ASSERT(!std::isnan(m_offset));
    227 }
    228 
    229 PassOwnPtr<KeyframeAnimationEffect::PropertySpecificKeyframe> KeyframeAnimationEffect::PropertySpecificKeyframe::cloneWithOffset(double offset) const
    230 {
    231     return adoptPtr(new PropertySpecificKeyframe(offset, PassRefPtr<CompositableValue>(m_value)));
    232 }
    233 
    234 
    235 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtr<PropertySpecificKeyframe> keyframe)
    236 {
    237     ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset());
    238     m_keyframes.append(keyframe);
    239 }
    240 
    241 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::removeRedundantKeyframes()
    242 {
    243     // As an optimization, removes keyframes in the following categories, as
    244     // they will never be used by sample().
    245     // - End keyframes with the same offset as their neighbor
    246     // - Interior keyframes with the same offset as both their neighbors
    247     // Note that synthetic keyframes must be added before this method is
    248     // called.
    249     ASSERT(m_keyframes.size() >= 2);
    250     for (int i = m_keyframes.size() - 1; i >= 0; --i) {
    251         double offset = m_keyframes[i]->offset();
    252         bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset;
    253         bool hasSameOffsetAsNextNeighbor = i == m_keyframes.size() - 1 || m_keyframes[i + 1]->offset() == offset;
    254         if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor)
    255             m_keyframes.remove(i);
    256     }
    257     ASSERT(m_keyframes.size() >= 2);
    258 }
    259 
    260 void KeyframeAnimationEffect::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired()
    261 {
    262     ASSERT(!m_keyframes.isEmpty());
    263     double offset = m_keyframes.first()->offset();
    264     bool allOffsetsEqual = true;
    265     for (PropertySpecificKeyframeVector::const_iterator iter = m_keyframes.begin() + 1; iter != m_keyframes.end(); ++iter) {
    266         if ((*iter)->offset() != offset) {
    267             allOffsetsEqual = false;
    268             break;
    269         }
    270     }
    271     if (!allOffsetsEqual)
    272         return;
    273 
    274     if (!offset)
    275         appendKeyframe(m_keyframes.first()->cloneWithOffset(1.0));
    276     else
    277         m_keyframes.insert(0, adoptPtr(new PropertySpecificKeyframe(0.0, AnimatableValue::neutralValue(), CompositeAdd)));
    278 }
    279 
    280 PassRefPtr<AnimationEffect::CompositableValue> KeyframeAnimationEffect::PropertySpecificKeyframeGroup::sample(int iteration, double offset) const
    281 {
    282     // FIXME: Implement accumulation.
    283     ASSERT_UNUSED(iteration, iteration >= 0);
    284 
    285     double minimumOffset = m_keyframes.first()->offset();
    286     double maximumOffset = m_keyframes.last()->offset();
    287     ASSERT(minimumOffset != maximumOffset);
    288 
    289     PropertySpecificKeyframeVector::const_iterator before;
    290     PropertySpecificKeyframeVector::const_iterator after;
    291 
    292     // Note that this algorithm is simpler than that in the spec because we
    293     // have removed keyframes with equal offsets in
    294     // removeRedundantKeyframes().
    295     if (offset < minimumOffset) {
    296         before = m_keyframes.begin();
    297         after = before + 1;
    298         ASSERT((*before)->offset() > offset);
    299         ASSERT((*after)->offset() > offset);
    300     } else if (offset >= maximumOffset) {
    301         after = m_keyframes.end() - 1;
    302         before = after - 1;
    303         ASSERT((*before)->offset() < offset);
    304         ASSERT((*after)->offset() <= offset);
    305     } else {
    306         // FIXME: This is inefficient for large numbers of keyframes. Consider
    307         // using binary search.
    308         after = m_keyframes.begin();
    309         while ((*after)->offset() <= offset)
    310             ++after;
    311         before = after - 1;
    312         ASSERT((*before)->offset() <= offset);
    313         ASSERT((*after)->offset() > offset);
    314     }
    315 
    316     if ((*before)->offset() == offset)
    317         return const_cast<CompositableValue*>((*before)->value());
    318     if ((*after)->offset() == offset)
    319         return const_cast<CompositableValue*>((*after)->value());
    320     return BlendedCompositableValue::create((*before)->value(), (*after)->value(),
    321         (offset - (*before)->offset()) / ((*after)->offset() - (*before)->offset()));
    322 }
    323 
    324 } // namespace
    325