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