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/KeyframeEffectModel.h" 33 34#include "core/StylePropertyShorthand.h" 35#include "core/animation/AnimationNode.h" 36#include "platform/geometry/FloatBox.h" 37#include "platform/transforms/TransformationMatrix.h" 38#include "wtf/text/StringHash.h" 39 40namespace blink { 41 42PropertySet KeyframeEffectModelBase::properties() const 43{ 44 PropertySet result; 45 if (!m_keyframes.size()) { 46 return result; 47 } 48 result = m_keyframes[0]->properties(); 49 for (size_t i = 1; i < m_keyframes.size(); i++) { 50 PropertySet extras = m_keyframes[i]->properties(); 51 for (PropertySet::const_iterator it = extras.begin(); it != extras.end(); ++it) { 52 result.add(*it); 53 } 54 } 55 return result; 56} 57 58PassOwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > KeyframeEffectModelBase::sample(int iteration, double fraction, double iterationDuration) const 59{ 60 ASSERT(iteration >= 0); 61 ASSERT(!isNull(fraction)); 62 ensureKeyframeGroups(); 63 ensureInterpolationEffect(); 64 65 return m_interpolationEffect->getActiveInterpolations(fraction, iterationDuration); 66} 67 68KeyframeEffectModelBase::KeyframeVector KeyframeEffectModelBase::normalizedKeyframes(const KeyframeVector& keyframes) 69{ 70 double lastOffset = 0; 71 KeyframeVector result; 72 result.reserveCapacity(keyframes.size()); 73 74 for (size_t i = 0; i < keyframes.size(); ++i) { 75 double offset = keyframes[i]->offset(); 76 if (!isNull(offset)) { 77 ASSERT(offset >= 0); 78 ASSERT(offset <= 1); 79 ASSERT(offset >= lastOffset); 80 lastOffset = offset; 81 } 82 result.append(keyframes[i]->clone()); 83 } 84 85 if (result.isEmpty()) { 86 return result; 87 } 88 89 if (isNull(result.last()->offset())) 90 result.last()->setOffset(1); 91 92 if (result.size() > 1 && isNull(result[0]->offset())) 93 result[0]->setOffset(0); 94 95 size_t lastIndex = 0; 96 lastOffset = result[0]->offset(); 97 for (size_t i = 1; i < result.size(); ++i) { 98 double offset = result[i]->offset(); 99 if (!isNull(offset)) { 100 for (size_t j = 1; j < i - lastIndex; ++j) 101 result[lastIndex + j]->setOffset(lastOffset + (offset - lastOffset) * j / (i - lastIndex)); 102 lastIndex = i; 103 lastOffset = offset; 104 } 105 } 106 107 return result; 108} 109 110 111void KeyframeEffectModelBase::ensureKeyframeGroups() const 112{ 113 if (m_keyframeGroups) 114 return; 115 116 m_keyframeGroups = adoptPtrWillBeNoop(new KeyframeGroupMap); 117 const KeyframeVector keyframes = normalizedKeyframes(getFrames()); 118 for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) { 119 const Keyframe* keyframe = keyframeIter->get(); 120 PropertySet keyframeProperties = keyframe->properties(); 121 for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) { 122 CSSPropertyID property = *propertyIter; 123 ASSERT_WITH_MESSAGE(!isExpandedShorthand(property), "Web Animations: Encountered shorthand CSS property (%d) in normalized keyframes.", property); 124 KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property); 125 PropertySpecificKeyframeGroup* group; 126 if (groupIter == m_keyframeGroups->end()) 127 group = m_keyframeGroups->add(property, adoptPtrWillBeNoop(new PropertySpecificKeyframeGroup)).storedValue->value.get(); 128 else 129 group = groupIter->value.get(); 130 131 group->appendKeyframe(keyframe->createPropertySpecificKeyframe(property)); 132 } 133 } 134 135 // Add synthetic keyframes. 136 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { 137 iter->value->addSyntheticKeyframeIfRequired(this); 138 iter->value->removeRedundantKeyframes(); 139 } 140} 141 142void KeyframeEffectModelBase::ensureInterpolationEffect(Element* element) const 143{ 144 if (m_interpolationEffect) 145 return; 146 m_interpolationEffect = InterpolationEffect::create(); 147 148 for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { 149 const PropertySpecificKeyframeVector& keyframes = iter->value->keyframes(); 150 ASSERT(keyframes[0]->composite() == AnimationEffect::CompositeReplace); 151 for (size_t i = 0; i < keyframes.size() - 1; i++) { 152 ASSERT(keyframes[i + 1]->composite() == AnimationEffect::CompositeReplace); 153 double applyFrom = i ? keyframes[i]->offset() : (-std::numeric_limits<double>::infinity()); 154 double applyTo = i == keyframes.size() - 2 ? std::numeric_limits<double>::infinity() : keyframes[i + 1]->offset(); 155 if (applyTo == 1) 156 applyTo = std::numeric_limits<double>::infinity(); 157 158 m_interpolationEffect->addInterpolation(keyframes[i]->createInterpolation(iter->key, keyframes[i + 1].get(), element), 159 &keyframes[i]->easing(), keyframes[i]->offset(), keyframes[i + 1]->offset(), applyFrom, applyTo); 160 } 161 } 162} 163 164bool KeyframeEffectModelBase::isReplaceOnly() 165{ 166 ensureKeyframeGroups(); 167 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) { 168 const PropertySpecificKeyframeVector& keyframeVector = iter->value->keyframes(); 169 for (size_t i = 0; i < keyframeVector.size(); ++i) { 170 if (keyframeVector[i]->composite() != AnimationEffect::CompositeReplace) 171 return false; 172 } 173 } 174 return true; 175} 176 177void KeyframeEffectModelBase::trace(Visitor* visitor) 178{ 179 visitor->trace(m_keyframes); 180 visitor->trace(m_interpolationEffect); 181#if ENABLE_OILPAN 182 visitor->trace(m_keyframeGroups); 183#endif 184 AnimationEffect::trace(visitor); 185} 186 187Keyframe::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, AnimationEffect::CompositeOperation composite) 188 : m_offset(offset) 189 , m_easing(easing) 190 , m_composite(composite) 191{ 192} 193 194void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtrWillBeRawPtr<Keyframe::PropertySpecificKeyframe> keyframe) 195{ 196 ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset()); 197 m_keyframes.append(keyframe); 198} 199 200void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::removeRedundantKeyframes() 201{ 202 // As an optimization, removes keyframes in the following categories, as 203 // they will never be used by sample(). 204 // - End keyframes with the same offset as their neighbor 205 // - Interior keyframes with the same offset as both their neighbors 206 // Note that synthetic keyframes must be added before this method is 207 // called. 208 ASSERT(m_keyframes.size() >= 2); 209 for (int i = m_keyframes.size() - 1; i >= 0; --i) { 210 double offset = m_keyframes[i]->offset(); 211 bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset; 212 bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.size() - 1) || m_keyframes[i + 1]->offset() == offset; 213 if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor) 214 m_keyframes.remove(i); 215 } 216 ASSERT(m_keyframes.size() >= 2); 217} 218 219void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired(const KeyframeEffectModelBase* context) 220{ 221 ASSERT(!m_keyframes.isEmpty()); 222 if (m_keyframes.first()->offset() != 0.0) 223 m_keyframes.insert(0, m_keyframes.first()->neutralKeyframe(0, nullptr)); 224 if (m_keyframes.last()->offset() != 1.0) 225 appendKeyframe(m_keyframes.last()->neutralKeyframe(1, nullptr)); 226} 227 228void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::trace(Visitor* visitor) 229{ 230#if ENABLE(OILPAN) 231 visitor->trace(m_keyframes); 232#endif 233} 234 235} // namespace 236