38d8dd6bf25bb7594f960f38bad29e978a9a7ba9
[WebKit-https.git] / Source / WebCore / platform / graphics / texmap / TextureMapperAnimation.cpp
1 /*
2  Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  Library General Public License for more details.
13
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB.  If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "TextureMapperAnimation.h"
22
23 #include "LayoutSize.h"
24 #include <wtf/CurrentTime.h>
25
26 namespace WebCore {
27
28 static RefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation& toOp, double progress, const FloatSize&, bool blendToPassthrough = false)
29 {
30     return toOp.blend(fromOp, progress, blendToPassthrough);
31 }
32
33 static FilterOperations applyFilterAnimation(const FilterOperations& from, const FilterOperations& to, double progress, const FloatSize& boxSize)
34 {
35     // First frame of an animation.
36     if (!progress)
37         return from;
38
39     // Last frame of an animation.
40     if (progress == 1)
41         return to;
42
43     if (!from.isEmpty() && !to.isEmpty() && !from.operationsMatch(to))
44         return to;
45
46     FilterOperations result;
47
48     size_t fromSize = from.operations().size();
49     size_t toSize = to.operations().size();
50     size_t size = std::max(fromSize, toSize);
51     for (size_t i = 0; i < size; i++) {
52         RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : nullptr;
53         RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : nullptr;
54         RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), *toOp, progress, boxSize) : (fromOp ? blendFunc(nullptr, *fromOp, progress, boxSize, true) : nullptr);
55         if (blendedOp)
56             result.operations().append(blendedOp);
57         else {
58             RefPtr<FilterOperation> identityOp = PassthroughFilterOperation::create();
59             if (progress > 0.5)
60                 result.operations().append(toOp ? toOp : identityOp);
61             else
62                 result.operations().append(fromOp ? fromOp : identityOp);
63         }
64     }
65
66     return result;
67 }
68
69 static bool shouldReverseAnimationValue(Animation::AnimationDirection direction, int loopCount)
70 {
71     return (direction == Animation::AnimationDirectionAlternate && loopCount & 1)
72         || (direction == Animation::AnimationDirectionAlternateReverse && !(loopCount & 1))
73         || direction == Animation::AnimationDirectionReverse;
74 }
75
76 static double normalizedAnimationValue(double runningTime, double duration, Animation::AnimationDirection direction, double iterationCount)
77 {
78     if (!duration)
79         return 0;
80
81     const int loopCount = runningTime / duration;
82     const double lastFullLoop = duration * double(loopCount);
83     const double remainder = runningTime - lastFullLoop;
84     // Ignore remainder when we've reached the end of animation.
85     const double normalized = (loopCount == iterationCount) ? 1.0 : (remainder / duration);
86
87     return shouldReverseAnimationValue(direction, loopCount) ? 1 - normalized : normalized;
88 }
89
90 static double normalizedAnimationValueForFillsForwards(double iterationCount, Animation::AnimationDirection direction)
91 {
92     if (direction == Animation::AnimationDirectionNormal)
93         return 1;
94     if (direction == Animation::AnimationDirectionReverse)
95         return 0;
96     return shouldReverseAnimationValue(direction, iterationCount) ? 1 : 0;
97 }
98
99 static float applyOpacityAnimation(float fromOpacity, float toOpacity, double progress)
100 {
101     // Optimization: special case the edge values (0 and 1).
102     if (progress == 1.0)
103         return toOpacity;
104
105     if (!progress)
106         return fromOpacity;
107
108     return fromOpacity + progress * (toOpacity - fromOpacity);
109 }
110
111 static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const FloatSize& boxSize, bool listsMatch)
112 {
113     TransformationMatrix matrix;
114
115     // First frame of an animation.
116     if (!progress) {
117         from.apply(boxSize, matrix);
118         return matrix;
119     }
120
121     // Last frame of an animation.
122     if (progress == 1) {
123         to.apply(boxSize, matrix);
124         return matrix;
125     }
126
127     // If we have incompatible operation lists, we blend the resulting matrices.
128     if (!listsMatch) {
129         TransformationMatrix fromMatrix;
130         to.apply(boxSize, matrix);
131         from.apply(boxSize, fromMatrix);
132         matrix.blend(fromMatrix, progress);
133         return matrix;
134     }
135
136     // Animation to "-webkit-transform: none".
137     if (!to.size()) {
138         TransformOperations blended(from);
139         for (auto& operation : blended.operations())
140             operation->blend(nullptr, progress, true)->apply(matrix, boxSize);
141         return matrix;
142     }
143
144     // Animation from "-webkit-transform: none".
145     if (!from.size()) {
146         TransformOperations blended(to);
147         for (auto& operation : blended.operations())
148             operation->blend(nullptr, 1 - progress, true)->apply(matrix, boxSize);
149         return matrix;
150     }
151
152     // Normal animation with a matching operation list.
153     TransformOperations blended(to);
154     for (size_t i = 0; i < blended.operations().size(); ++i)
155         blended.operations()[i]->blend(from.at(i), progress, !from.at(i))->apply(matrix, boxSize);
156     return matrix;
157 }
158
159 static const TimingFunction& timingFunctionForAnimationValue(const AnimationValue& animationValue, const Animation& animation)
160 {
161     if (animationValue.timingFunction())
162         return *animationValue.timingFunction();
163     if (animation.timingFunction())
164         return *animation.timingFunction();
165     return CubicBezierTimingFunction::defaultTimingFunction();
166 }
167
168 TextureMapperAnimation::TextureMapperAnimation(const String& name, const KeyframeValueList& keyframes, const FloatSize& boxSize, const Animation& animation, bool listsMatch, MonotonicTime startTime, Seconds pauseTime, AnimationState state)
169     : m_name(name.isSafeToSendToAnotherThread() ? name : name.isolatedCopy())
170     , m_keyframes(keyframes)
171     , m_boxSize(boxSize)
172     , m_animation(Animation::create(animation))
173     , m_listsMatch(listsMatch)
174     , m_startTime(startTime)
175     , m_pauseTime(pauseTime)
176     , m_totalRunningTime(0_s)
177     , m_lastRefreshedTime(m_startTime)
178     , m_state(state)
179 {
180 }
181
182 TextureMapperAnimation::TextureMapperAnimation(const TextureMapperAnimation& other)
183     : m_name(other.m_name.isSafeToSendToAnotherThread() ? other.m_name : other.m_name.isolatedCopy())
184     , m_keyframes(other.m_keyframes)
185     , m_boxSize(other.m_boxSize)
186     , m_animation(Animation::create(*other.m_animation))
187     , m_listsMatch(other.m_listsMatch)
188     , m_startTime(other.m_startTime)
189     , m_pauseTime(other.m_pauseTime)
190     , m_totalRunningTime(other.m_totalRunningTime)
191     , m_lastRefreshedTime(other.m_lastRefreshedTime)
192     , m_state(other.m_state)
193 {
194 }
195
196 void TextureMapperAnimation::apply(Client& client)
197 {
198     if (!isActive())
199         return;
200
201     Seconds totalRunningTime = computeTotalRunningTime();
202     double normalizedValue = normalizedAnimationValue(totalRunningTime.seconds(), m_animation->duration(), m_animation->direction(), m_animation->iterationCount());
203
204     if (m_animation->iterationCount() != Animation::IterationCountInfinite && totalRunningTime.seconds() >= m_animation->duration() * m_animation->iterationCount()) {
205         m_state = AnimationState::Stopped;
206         m_pauseTime = 0_s;
207         if (m_animation->fillsForwards())
208             normalizedValue = normalizedAnimationValueForFillsForwards(m_animation->iterationCount(), m_animation->direction());
209     }
210
211     if (!normalizedValue) {
212         applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), 0);
213         return;
214     }
215
216     if (normalizedValue == 1.0) {
217         applyInternal(client, m_keyframes.at(m_keyframes.size() - 2), m_keyframes.at(m_keyframes.size() - 1), 1);
218         return;
219     }
220     if (m_keyframes.size() == 2) {
221         auto& timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), *m_animation);
222         normalizedValue = timingFunction.transformTime(normalizedValue, m_animation->duration());
223         applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), normalizedValue);
224         return;
225     }
226
227     for (size_t i = 0; i < m_keyframes.size() - 1; ++i) {
228         const AnimationValue& from = m_keyframes.at(i);
229         const AnimationValue& to = m_keyframes.at(i + 1);
230         if (from.keyTime() > normalizedValue || to.keyTime() < normalizedValue)
231             continue;
232
233         normalizedValue = (normalizedValue - from.keyTime()) / (to.keyTime() - from.keyTime());
234         auto& timingFunction = timingFunctionForAnimationValue(from, *m_animation);
235         normalizedValue = timingFunction.transformTime(normalizedValue, m_animation->duration());
236         applyInternal(client, from, to, normalizedValue);
237         break;
238     }
239 }
240
241 void TextureMapperAnimation::pause(Seconds time)
242 {
243     m_state = AnimationState::Paused;
244     m_pauseTime = time;
245 }
246
247 void TextureMapperAnimation::resume()
248 {
249     m_state = AnimationState::Playing;
250     // FIXME: This seems wrong. m_totalRunningTime is cleared.
251     // https://bugs.webkit.org/show_bug.cgi?id=183113
252     m_pauseTime = 0_s;
253     m_totalRunningTime = m_pauseTime;
254     m_lastRefreshedTime = MonotonicTime::now();
255 }
256
257 Seconds TextureMapperAnimation::computeTotalRunningTime()
258 {
259     if (m_state == AnimationState::Paused)
260         return m_pauseTime;
261
262     MonotonicTime oldLastRefreshedTime = m_lastRefreshedTime;
263     m_lastRefreshedTime = MonotonicTime::now();
264     m_totalRunningTime += m_lastRefreshedTime - oldLastRefreshedTime;
265     return m_totalRunningTime;
266 }
267
268 bool TextureMapperAnimation::isActive() const
269 {
270     return m_state != AnimationState::Stopped || m_animation->fillsForwards();
271 }
272
273 void TextureMapperAnimation::applyInternal(Client& client, const AnimationValue& from, const AnimationValue& to, float progress)
274 {
275     switch (m_keyframes.property()) {
276     case AnimatedPropertyOpacity:
277         client.setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue&>(from).value()), (static_cast<const FloatAnimationValue&>(to).value()), progress));
278         return;
279     case AnimatedPropertyTransform:
280         client.setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue&>(from).value(), static_cast<const TransformAnimationValue&>(to).value(), progress, m_boxSize, m_listsMatch));
281         return;
282     case AnimatedPropertyFilter:
283         client.setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue&>(from).value(), static_cast<const FilterAnimationValue&>(to).value(), progress, m_boxSize));
284         return;
285     default:
286         ASSERT_NOT_REACHED();
287     }
288 }
289
290 void TextureMapperAnimations::add(const TextureMapperAnimation& animation)
291 {
292     // Remove the old state if we are resuming a paused animation.
293     remove(animation.name(), animation.keyframes().property());
294
295     m_animations.append(animation);
296 }
297
298 void TextureMapperAnimations::remove(const String& name)
299 {
300     m_animations.removeAllMatching([&name] (const TextureMapperAnimation& animation) {
301         return animation.name() == name;
302     });
303 }
304
305 void TextureMapperAnimations::remove(const String& name, AnimatedPropertyID property)
306 {
307     m_animations.removeAllMatching([&name, property] (const TextureMapperAnimation& animation) {
308         return animation.name() == name && animation.keyframes().property() == property;
309     });
310 }
311
312 void TextureMapperAnimations::pause(const String& name, Seconds offset)
313 {
314     for (auto& animation : m_animations) {
315         if (animation.name() == name)
316             animation.pause(offset);
317     }
318 }
319
320 void TextureMapperAnimations::suspend(MonotonicTime time)
321 {
322     // FIXME: This seems wrong. `pause` takes time offset (Seconds), not MonotonicTime.
323     // https://bugs.webkit.org/show_bug.cgi?id=183112
324     for (auto& animation : m_animations)
325         animation.pause(time.secondsSinceEpoch());
326 }
327
328 void TextureMapperAnimations::resume()
329 {
330     for (auto& animation : m_animations)
331         animation.resume();
332 }
333
334 void TextureMapperAnimations::apply(TextureMapperAnimation::Client& client)
335 {
336     for (auto& animation : m_animations)
337         animation.apply(client);
338 }
339
340 bool TextureMapperAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const
341 {
342     return std::any_of(m_animations.begin(), m_animations.end(),
343         [&type](const TextureMapperAnimation& animation) { return animation.isActive() && animation.keyframes().property() == type; });
344 }
345
346 bool TextureMapperAnimations::hasRunningAnimations() const
347 {
348     return std::any_of(m_animations.begin(), m_animations.end(),
349         [](const TextureMapperAnimation& animation) { return animation.state() == TextureMapperAnimation::AnimationState::Playing; });
350 }
351
352 TextureMapperAnimations TextureMapperAnimations::getActiveAnimations() const
353 {
354     TextureMapperAnimations active;
355     for (auto& animation : m_animations) {
356         if (animation.isActive())
357             active.add(animation);
358     }
359     return active;
360 }
361
362 } // namespace WebCore