706158d8229019df8342d95436b2227a3ae791ef
[WebKit-https.git] / Source / WebCore / platform / graphics / GraphicsLayerAnimation.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
22 #if USE(ACCELERATED_COMPOSITING)
23 #include "GraphicsLayerAnimation.h"
24
25 #include "LayoutSize.h"
26 #include "UnitBezier.h"
27 #include <wtf/CurrentTime.h>
28
29 namespace WebCore {
30
31 #if ENABLE(CSS_FILTERS)
32 static inline PassRefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation* toOp, double progress, const IntSize& size, bool blendToPassthrough = false)
33 {
34     ASSERT(toOp);
35     if (toOp->blendingNeedsRendererSize())
36         return toOp->blend(fromOp, progress, LayoutSize(size.width(), size.height()), blendToPassthrough);
37
38     return toOp->blend(fromOp, progress, blendToPassthrough);
39 }
40
41
42 static FilterOperations applyFilterAnimation(const FilterOperations* from, const FilterOperations* to, double progress, const IntSize& boxSize)
43 {
44     // First frame of an animation.
45     if (!progress)
46         return *from;
47
48     // Last frame of an animation.
49     if (progress == 1)
50         return *to;
51
52     if (!from->operationsMatch(*to))
53         return *to;
54
55     FilterOperations result;
56
57     size_t fromSize = from->operations().size();
58     size_t toSize = to->operations().size();
59     size_t size = std::max(fromSize, toSize);
60     for (size_t i = 0; i < size; i++) {
61         RefPtr<FilterOperation> fromOp = (i < fromSize) ? from->operations()[i].get() : 0;
62         RefPtr<FilterOperation> toOp = (i < toSize) ? to->operations()[i].get() : 0;
63         RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), toOp.get(), progress, boxSize) : (fromOp ? blendFunc(0, fromOp.get(), progress, boxSize, true) : 0);
64         if (blendedOp)
65             result.operations().append(blendedOp);
66         else {
67             RefPtr<FilterOperation> identityOp = PassthroughFilterOperation::create();
68             if (progress > 0.5)
69                 result.operations().append(toOp ? toOp : identityOp);
70             else
71                 result.operations().append(fromOp ? fromOp : identityOp);
72         }
73     }
74
75     return result;
76 }
77 #endif
78
79 static bool shouldReverseAnimationValue(Animation::AnimationDirection direction, int loopCount)
80 {
81     if (((direction == Animation::AnimationDirectionAlternate) && (loopCount & 1))
82         || ((direction == Animation::AnimationDirectionAlternateReverse) && !(loopCount & 1))
83         || direction == Animation::AnimationDirectionReverse)
84         return true;
85     return false;
86 }
87
88 static double normalizedAnimationValue(double runningTime, double duration, Animation::AnimationDirection direction, double iterationCount)
89 {
90     if (!duration)
91         return 0;
92
93     const int loopCount = runningTime / duration;
94     const double lastFullLoop = duration * double(loopCount);
95     const double remainder = runningTime - lastFullLoop;
96     // Ignore remainder when we've reached the end of animation.
97     const double normalized = (loopCount == iterationCount) ? 1.0 : (remainder / duration);
98
99     return shouldReverseAnimationValue(direction, loopCount) ? 1 - normalized : normalized;
100 }
101
102 static double normalizedAnimationValueForFillsForwards(double iterationCount, Animation::AnimationDirection direction)
103 {
104     if (direction == Animation::AnimationDirectionNormal)
105         return 1;
106     if (direction == Animation::AnimationDirectionReverse)
107         return 0;
108     return shouldReverseAnimationValue(direction, iterationCount) ? 1 : 0;
109 }
110
111 static float applyOpacityAnimation(float fromOpacity, float toOpacity, double progress)
112 {
113     // Optimization: special case the edge values (0 and 1).
114     if (progress == 1.0)
115         return toOpacity;
116
117     if (!progress)
118         return fromOpacity;
119
120     return fromOpacity + progress * (toOpacity - fromOpacity);
121 }
122
123 static inline double solveEpsilon(double duration)
124 {
125     return 1.0 / (200.0 * duration);
126 }
127
128 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
129 {
130     return UnitBezier(p1x, p1y, p2x, p2y).solve(t, solveEpsilon(duration));
131 }
132
133 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
134 {
135     if (stepAtStart)
136         return std::min(1.0, (floor(numSteps * t) + 1) / numSteps);
137     return floor(numSteps * t) / numSteps;
138 }
139
140 static inline float applyTimingFunction(const TimingFunction* timingFunction, float progress, double duration)
141 {
142     if (!timingFunction)
143         return progress;
144
145     if (timingFunction->isCubicBezierTimingFunction()) {
146         const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
147         return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2(), progress, duration);
148     }
149
150     if (timingFunction->isStepsTimingFunction()) {
151         const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
152         return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
153     }
154
155     return progress;
156 }
157
158 static TransformationMatrix applyTransformAnimation(const TransformOperations* from, const TransformOperations* to, double progress, const IntSize& boxSize, bool listsMatch)
159 {
160     TransformationMatrix matrix;
161
162     // First frame of an animation.
163     if (!progress) {
164         from->apply(boxSize, matrix);
165         return matrix;
166     }
167
168     // Last frame of an animation.
169     if (progress == 1) {
170         to->apply(boxSize, matrix);
171         return matrix;
172     }
173
174     // If we have incompatible operation lists, we blend the resulting matrices.
175     if (!listsMatch) {
176         TransformationMatrix fromMatrix;
177         to->apply(boxSize, matrix);
178         from->apply(boxSize, fromMatrix);
179         matrix.blend(fromMatrix, progress);
180         return matrix;
181     }
182
183     // Animation to "-webkit-transform: none".
184     if (!to->size()) {
185         TransformOperations blended(*from);
186         for (size_t i = 0; i < blended.operations().size(); ++i)
187             blended.operations()[i]->blend(0, progress, true)->apply(matrix, boxSize);
188         return matrix;
189     }
190
191     // Animation from "-webkit-transform: none".
192     if (!from->size()) {
193         TransformOperations blended(*to);
194         for (size_t i = 0; i < blended.operations().size(); ++i)
195             blended.operations()[i]->blend(0, 1. - progress, true)->apply(matrix, boxSize);
196         return matrix;
197     }
198
199     // Normal animation with a matching operation list.
200     TransformOperations blended(*to);
201     for (size_t i = 0; i < blended.operations().size(); ++i)
202         blended.operations()[i]->blend(from->at(i), progress, !from->at(i))->apply(matrix, boxSize);
203     return matrix;
204 }
205
206 static const TimingFunction* timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim)
207 {
208     if (animValue->timingFunction())
209         return animValue->timingFunction();
210     if (anim->timingFunction())
211         return anim->timingFunction().get();
212
213     return CubicBezierTimingFunction::defaultTimingFunction();
214 }
215
216 GraphicsLayerAnimation::GraphicsLayerAnimation(const String& name, const KeyframeValueList& keyframes, const IntSize& boxSize, const Animation* animation, double startTime, bool listsMatch)
217     : m_keyframes(keyframes)
218     , m_boxSize(boxSize)
219     , m_animation(Animation::create(animation))
220     , m_name(name)
221     , m_listsMatch(listsMatch)
222     , m_startTime(startTime)
223     , m_pauseTime(0)
224     , m_totalRunningTime(0)
225     , m_lastRefreshedTime(m_startTime)
226     , m_state(PlayingState)
227 {
228 }
229
230 void GraphicsLayerAnimation::applyInternal(Client* client, const AnimationValue* from, const AnimationValue* to, float progress)
231 {
232     switch (m_keyframes.property()) {
233     case AnimatedPropertyOpacity:
234         client->setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue*>(from)->value()), (static_cast<const FloatAnimationValue*>(to)->value()), progress));
235         return;
236     case AnimatedPropertyWebkitTransform:
237         client->setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue*>(from)->value(), static_cast<const TransformAnimationValue*>(to)->value(), progress, m_boxSize, m_listsMatch));
238         return;
239 #if ENABLE(CSS_FILTERS)
240     case AnimatedPropertyWebkitFilter:
241         client->setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue*>(from)->value(), static_cast<const FilterAnimationValue*>(to)->value(), progress, m_boxSize));
242         return;
243 #endif
244     default:
245         ASSERT_NOT_REACHED();
246     }
247 }
248
249 bool GraphicsLayerAnimation::isActive() const
250 {
251     if (state() != StoppedState)
252         return true;
253
254     return m_animation->fillsForwards();
255 }
256
257 bool GraphicsLayerAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const
258 {
259     for (size_t i = 0; i < m_animations.size(); ++i) {
260         if (m_animations[i].isActive() && m_animations[i].property() == type)
261             return true;
262     }
263     return false;
264 }
265
266 bool GraphicsLayerAnimations::hasRunningAnimations() const
267 {
268     for (size_t i = 0; i < m_animations.size(); ++i) {
269         if (m_animations[i].state() == GraphicsLayerAnimation::PlayingState)
270             return true;
271     }
272
273     return false;
274 }
275
276 void GraphicsLayerAnimation::apply(Client* client)
277 {
278     if (!isActive())
279         return;
280
281     double totalRunningTime = computeTotalRunningTime();
282     double normalizedValue = normalizedAnimationValue(totalRunningTime, m_animation->duration(), m_animation->direction(), m_animation->iterationCount());
283
284     if (m_animation->iterationCount() != Animation::IterationCountInfinite && totalRunningTime >= m_animation->duration() * m_animation->iterationCount()) {
285         setState(StoppedState);
286         if (m_animation->fillsForwards())
287             normalizedValue = normalizedAnimationValueForFillsForwards(m_animation->iterationCount(), m_animation->direction());
288     }
289
290     if (!normalizedValue) {
291         applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), 0);
292         return;
293     }
294
295     if (normalizedValue == 1.0) {
296         applyInternal(client, m_keyframes.at(m_keyframes.size() - 2), m_keyframes.at(m_keyframes.size() - 1), 1);
297         return;
298     }
299     if (m_keyframes.size() == 2) {
300         const TimingFunction* timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), m_animation.get());
301         normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration());
302         applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), normalizedValue);
303         return;
304     }
305
306     for (size_t i = 0; i < m_keyframes.size() - 1; ++i) {
307         const AnimationValue* from = m_keyframes.at(i);
308         const AnimationValue* to = m_keyframes.at(i + 1);
309         if (from->keyTime() > normalizedValue || to->keyTime() < normalizedValue)
310             continue;
311
312         normalizedValue = (normalizedValue - from->keyTime()) / (to->keyTime() - from->keyTime());
313         const TimingFunction* timingFunction = timingFunctionForAnimationValue(from, m_animation.get());
314         normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration());
315         applyInternal(client, from, to, normalizedValue);
316         break;
317     }
318 }
319
320 double GraphicsLayerAnimation::computeTotalRunningTime()
321 {
322     if (state() == PausedState)
323         return m_pauseTime;
324
325     double oldLastRefreshedTime = m_lastRefreshedTime;
326     m_lastRefreshedTime = WTF::currentTime();
327     m_totalRunningTime += m_lastRefreshedTime - oldLastRefreshedTime;
328     return m_totalRunningTime;
329 }
330
331 void GraphicsLayerAnimation::pause(double time)
332 {
333     setState(PausedState);
334     m_pauseTime = time;
335 }
336
337 void GraphicsLayerAnimation::resume()
338 {
339     setState(PlayingState);
340     m_totalRunningTime = m_pauseTime;
341     m_lastRefreshedTime = WTF::currentTime();
342 }
343
344 void GraphicsLayerAnimations::add(const GraphicsLayerAnimation& animation)
345 {
346     // Remove the old state if we are resuming a paused animation.
347     remove(animation.name());
348
349     m_animations.append(animation);
350 }
351
352 void GraphicsLayerAnimations::pause(const String& name, double offset)
353 {
354     for (size_t i = 0; i < m_animations.size(); ++i) {
355         if (m_animations[i].name() == name)
356             m_animations[i].pause(offset);
357     }
358 }
359
360 void GraphicsLayerAnimations::suspend(double offset)
361 {
362     for (size_t i = 0; i < m_animations.size(); ++i)
363         m_animations[i].pause(offset);
364 }
365
366 void GraphicsLayerAnimations::resume()
367 {
368     for (size_t i = 0; i < m_animations.size(); ++i)
369         m_animations[i].resume();
370 }
371
372 void GraphicsLayerAnimations::remove(const String& name)
373 {
374     for (int i = m_animations.size() - 1; i >= 0; --i) {
375         if (m_animations[i].name() == name)
376             m_animations.remove(i);
377     }
378 }
379
380 void GraphicsLayerAnimations::apply(GraphicsLayerAnimation::Client* client)
381 {
382     for (size_t i = 0; i < m_animations.size(); ++i)
383         m_animations[i].apply(client);
384 }
385
386 GraphicsLayerAnimations GraphicsLayerAnimations::getActiveAnimations() const
387 {
388     GraphicsLayerAnimations active;
389     for (size_t i = 0; i < m_animations.size(); ++i) {
390         if (m_animations[i].isActive())
391             active.add(m_animations[i]);
392     }
393     return active;
394 }
395 }
396 #endif
397