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