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