2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/animation/KeyframeEffectModel.h"
34 #include "core/animation/TimedItem.h"
35 #include "wtf/text/StringHash.h"
39 using namespace WebCore;
41 class AddCompositableValue FINAL : public AnimationEffect::CompositableValue {
43 static PassRefPtr<AddCompositableValue> create(const AnimatableValue* value)
45 return adoptRef(new AddCompositableValue(value));
47 virtual bool dependsOnUnderlyingValue() const OVERRIDE
51 virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const OVERRIDE
53 return AnimatableValue::add(underlyingValue, m_value.get());
56 AddCompositableValue(const AnimatableValue* value)
57 : m_value(const_cast<AnimatableValue*>(value))
60 RefPtr<AnimatableValue> m_value;
63 class BlendedCompositableValue FINAL : public AnimationEffect::CompositableValue {
65 static PassRefPtr<BlendedCompositableValue> create(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction)
67 return adoptRef(new BlendedCompositableValue(before, after, fraction));
69 virtual bool dependsOnUnderlyingValue() const OVERRIDE
71 return m_dependsOnUnderlyingValue;
73 virtual PassRefPtr<AnimatableValue> compositeOnto(const AnimatableValue* underlyingValue) const OVERRIDE
75 return AnimatableValue::interpolate(m_before->compositeOnto(underlyingValue).get(), m_after->compositeOnto(underlyingValue).get(), m_fraction);
78 BlendedCompositableValue(const AnimationEffect::CompositableValue* before, const AnimationEffect::CompositableValue* after, double fraction)
79 : m_before(const_cast<AnimationEffect::CompositableValue*>(before))
80 , m_after(const_cast<AnimationEffect::CompositableValue*>(after))
81 , m_fraction(fraction)
82 , m_dependsOnUnderlyingValue(before->dependsOnUnderlyingValue() || after->dependsOnUnderlyingValue())
84 RefPtr<AnimationEffect::CompositableValue> m_before;
85 RefPtr<AnimationEffect::CompositableValue> m_after;
87 bool m_dependsOnUnderlyingValue;
90 const double accuracyForKeyframeEasing = 0.0000001;
98 : m_offset(nullValue())
99 , m_composite(AnimationEffect::CompositeReplace)
102 Keyframe::Keyframe(const Keyframe& copyFrom)
103 : m_offset(copyFrom.m_offset)
104 , m_composite(copyFrom.m_composite)
105 , m_easing(copyFrom.m_easing)
107 for (PropertyValueMap::const_iterator iter = copyFrom.m_propertyValues.begin(); iter != copyFrom.m_propertyValues.end(); ++iter)
108 setPropertyValue(iter->key, iter->value.get());
111 void Keyframe::setPropertyValue(CSSPropertyID property, const AnimatableValue* value)
113 m_propertyValues.add(property, const_cast<AnimatableValue*>(value));
116 void Keyframe::clearPropertyValue(CSSPropertyID property)
118 m_propertyValues.remove(property);
121 const AnimatableValue* Keyframe::propertyValue(CSSPropertyID property) const
123 ASSERT(m_propertyValues.contains(property));
124 return m_propertyValues.get(property);
127 PropertySet Keyframe::properties() const
129 // This is not used in time-critical code, so we probably don't need to
130 // worry about caching this result.
131 PropertySet properties;
132 for (PropertyValueMap::const_iterator iter = m_propertyValues.begin(); iter != m_propertyValues.end(); ++iter)
133 properties.add(*iter.keys());
137 PassRefPtr<Keyframe> Keyframe::cloneWithOffset(double offset) const
139 RefPtr<Keyframe> theClone = clone();
140 theClone->setOffset(offset);
141 return theClone.release();
144 KeyframeEffectModel::KeyframeEffectModel(const KeyframeVector& keyframes)
145 : m_keyframes(keyframes)
149 PropertySet KeyframeEffectModel::properties() const
152 if (!m_keyframes.size()) {
155 result = m_keyframes[0]->properties();
156 for (size_t i = 1; i < m_keyframes.size(); i++) {
157 PropertySet extras = m_keyframes[i]->properties();
158 for (PropertySet::const_iterator it = extras.begin(); it != extras.end(); ++it) {
165 PassOwnPtr<AnimationEffect::CompositableValueList> KeyframeEffectModel::sample(int iteration, double fraction) const
167 ASSERT(iteration >= 0);
168 ASSERT(!isNull(fraction));
169 const_cast<KeyframeEffectModel*>(this)->ensureKeyframeGroups();
170 OwnPtr<CompositableValueList> map = adoptPtr(new CompositableValueList());
171 for (KeyframeGroupMap::const_iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter)
172 map->append(std::make_pair(iter->key, iter->value->sample(iteration, fraction)));
173 return map.release();
176 KeyframeEffectModel::KeyframeVector KeyframeEffectModel::normalizedKeyframes(const KeyframeVector& keyframes)
178 // keyframes [beginIndex, endIndex) will remain after removing all keyframes if they are not
179 // loosely sorted by offset, and after removing keyframes with positional offset outide [0, 1].
180 size_t beginIndex = 0;
181 size_t endIndex = keyframes.size();
183 // Becomes the most recent keyframe with an explicit offset.
184 size_t lastIndex = endIndex;
185 double lastOffset = std::numeric_limits<double>::quiet_NaN();
187 for (size_t i = 0; i < keyframes.size(); ++i) {
188 double offset = keyframes[i]->offset();
189 if (!isNull(offset)) {
190 if (lastIndex < i && offset < lastOffset) {
191 // The keyframes are not loosely sorted by offset. Exclude all.
192 endIndex = beginIndex;
197 // Remove all keyframes up to and including this keyframe.
199 } else if (offset > 1) {
200 // Remove all keyframes from this keyframe onwards. Note we must complete our checking
201 // that the keyframes are loosely sorted by offset, so we can't exit the loop early.
202 endIndex = std::min(i, endIndex);
210 KeyframeVector result;
211 if (beginIndex != endIndex) {
212 result.reserveCapacity(endIndex - beginIndex);
213 for (size_t i = beginIndex; i < endIndex; ++i) {
214 result.append(keyframes[i]->clone());
217 if (isNull(result[result.size() - 1]->offset()))
218 result[result.size() - 1]->setOffset(1);
220 if (result.size() > 1 && isNull(result[0]->offset()))
221 result[0]->setOffset(0);
224 lastOffset = result[0]->offset();
225 for (size_t i = 1; i < result.size(); ++i) {
226 double offset = result[i]->offset();
227 if (!isNull(offset)) {
228 if (lastIndex + 1 < i) {
229 for (size_t j = 1; j < i - lastIndex; ++j)
230 result[lastIndex + j]->setOffset(lastOffset + (offset - lastOffset) * j / (i - lastIndex));
240 void KeyframeEffectModel::ensureKeyframeGroups() const
242 if (m_keyframeGroups)
245 m_keyframeGroups = adoptPtr(new KeyframeGroupMap);
246 const KeyframeVector keyframes = normalizedKeyframes(getFrames());
247 for (KeyframeVector::const_iterator keyframeIter = keyframes.begin(); keyframeIter != keyframes.end(); ++keyframeIter) {
248 const Keyframe* keyframe = keyframeIter->get();
249 PropertySet keyframeProperties = keyframe->properties();
250 for (PropertySet::const_iterator propertyIter = keyframeProperties.begin(); propertyIter != keyframeProperties.end(); ++propertyIter) {
251 CSSPropertyID property = *propertyIter;
252 KeyframeGroupMap::iterator groupIter = m_keyframeGroups->find(property);
253 PropertySpecificKeyframeGroup* group;
254 if (groupIter == m_keyframeGroups->end())
255 group = m_keyframeGroups->add(property, adoptPtr(new PropertySpecificKeyframeGroup)).storedValue->value.get();
257 group = groupIter->value.get();
259 group->appendKeyframe(adoptPtr(
260 new PropertySpecificKeyframe(keyframe->offset(), keyframe->easing(), keyframe->propertyValue(property), keyframe->composite())));
264 // Add synthetic keyframes.
265 for (KeyframeGroupMap::iterator iter = m_keyframeGroups->begin(); iter != m_keyframeGroups->end(); ++iter) {
266 iter->value->addSyntheticKeyframeIfRequired();
267 iter->value->removeRedundantKeyframes();
272 KeyframeEffectModel::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, const AnimatableValue* value, CompositeOperation composite)
275 , m_value(composite == AnimationEffect::CompositeReplace ?
276 AnimatableValue::takeConstRef(value) :
277 static_cast<PassRefPtr<CompositableValue> >(AddCompositableValue::create(value)))
281 KeyframeEffectModel::PropertySpecificKeyframe::PropertySpecificKeyframe(double offset, PassRefPtr<TimingFunction> easing, PassRefPtr<CompositableValue> value)
286 ASSERT(!isNull(m_offset));
289 PassOwnPtr<KeyframeEffectModel::PropertySpecificKeyframe> KeyframeEffectModel::PropertySpecificKeyframe::cloneWithOffset(double offset) const
291 return adoptPtr(new PropertySpecificKeyframe(offset, m_easing, PassRefPtr<CompositableValue>(m_value)));
295 void KeyframeEffectModel::PropertySpecificKeyframeGroup::appendKeyframe(PassOwnPtr<PropertySpecificKeyframe> keyframe)
297 ASSERT(m_keyframes.isEmpty() || m_keyframes.last()->offset() <= keyframe->offset());
298 m_keyframes.append(keyframe);
301 void KeyframeEffectModel::PropertySpecificKeyframeGroup::removeRedundantKeyframes()
303 // As an optimization, removes keyframes in the following categories, as
304 // they will never be used by sample().
305 // - End keyframes with the same offset as their neighbor
306 // - Interior keyframes with the same offset as both their neighbors
307 // Note that synthetic keyframes must be added before this method is
309 ASSERT(m_keyframes.size() >= 2);
310 for (int i = m_keyframes.size() - 1; i >= 0; --i) {
311 double offset = m_keyframes[i]->offset();
312 bool hasSameOffsetAsPreviousNeighbor = !i || m_keyframes[i - 1]->offset() == offset;
313 bool hasSameOffsetAsNextNeighbor = i == static_cast<int>(m_keyframes.size() - 1) || m_keyframes[i + 1]->offset() == offset;
314 if (hasSameOffsetAsPreviousNeighbor && hasSameOffsetAsNextNeighbor)
315 m_keyframes.remove(i);
317 ASSERT(m_keyframes.size() >= 2);
320 void KeyframeEffectModel::PropertySpecificKeyframeGroup::addSyntheticKeyframeIfRequired()
322 ASSERT(!m_keyframes.isEmpty());
323 double offset = m_keyframes.first()->offset();
324 bool allOffsetsEqual = true;
325 for (PropertySpecificKeyframeVector::const_iterator iter = m_keyframes.begin() + 1; iter != m_keyframes.end(); ++iter) {
326 if ((*iter)->offset() != offset) {
327 allOffsetsEqual = false;
331 if (!allOffsetsEqual)
335 appendKeyframe(m_keyframes.first()->cloneWithOffset(1.0));
337 m_keyframes.insert(0, adoptPtr(new PropertySpecificKeyframe(0.0, 0, AnimatableValue::neutralValue(), CompositeAdd)));
340 PassRefPtr<AnimationEffect::CompositableValue> KeyframeEffectModel::PropertySpecificKeyframeGroup::sample(int iteration, double offset) const
342 // FIXME: Implement accumulation.
343 ASSERT_UNUSED(iteration, iteration >= 0);
344 ASSERT(!isNull(offset));
346 // Bail if offset is null, as this can lead to buffer overflow below.
348 return const_cast<CompositableValue*>(m_keyframes.first()->value());
350 double minimumOffset = m_keyframes.first()->offset();
351 double maximumOffset = m_keyframes.last()->offset();
352 ASSERT(minimumOffset != maximumOffset);
354 PropertySpecificKeyframeVector::const_iterator before;
355 PropertySpecificKeyframeVector::const_iterator after;
357 // Note that this algorithm is simpler than that in the spec because we
358 // have removed keyframes with equal offsets in
359 // removeRedundantKeyframes().
360 if (offset < minimumOffset) {
361 before = m_keyframes.begin();
363 ASSERT((*before)->offset() > offset);
364 ASSERT((*after)->offset() > offset);
365 } else if (offset >= maximumOffset) {
366 after = m_keyframes.end() - 1;
368 ASSERT((*before)->offset() < offset);
369 ASSERT((*after)->offset() <= offset);
371 // FIXME: This is inefficient for large numbers of keyframes. Consider
372 // using binary search.
373 after = m_keyframes.begin();
374 while ((*after)->offset() <= offset)
377 ASSERT((*before)->offset() <= offset);
378 ASSERT((*after)->offset() > offset);
381 if ((*before)->offset() == offset)
382 return const_cast<CompositableValue*>((*before)->value());
383 if ((*after)->offset() == offset)
384 return const_cast<CompositableValue*>((*after)->value());
386 double fraction = (offset - (*before)->offset()) / ((*after)->offset() - (*before)->offset());
387 if (const TimingFunction* timingFunction = (*before)->easing())
388 fraction = timingFunction->evaluate(fraction, accuracyForKeyframeEasing);
389 return BlendedCompositableValue::create((*before)->value(), (*after)->value(), fraction);