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