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