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/CompositorAnimations.h"
33
34#include "core/animation/AnimationNode.h"
35#include "core/animation/AnimationTranslationUtil.h"
36#include "core/animation/CompositorAnimationsImpl.h"
37#include "core/animation/animatable/AnimatableDouble.h"
38#include "core/animation/animatable/AnimatableFilterOperations.h"
39#include "core/animation/animatable/AnimatableTransform.h"
40#include "core/animation/animatable/AnimatableValue.h"
41#include "core/rendering/RenderBoxModelObject.h"
42#include "core/rendering/RenderLayer.h"
43#include "core/rendering/RenderObject.h"
44#include "core/rendering/compositing/CompositedLayerMapping.h"
45#include "platform/geometry/FloatBox.h"
46#include "public/platform/Platform.h"
47#include "public/platform/WebCompositorAnimation.h"
48#include "public/platform/WebCompositorSupport.h"
49#include "public/platform/WebFilterAnimationCurve.h"
50#include "public/platform/WebFilterKeyframe.h"
51#include "public/platform/WebFloatAnimationCurve.h"
52#include "public/platform/WebFloatKeyframe.h"
53#include "public/platform/WebTransformAnimationCurve.h"
54#include "public/platform/WebTransformKeyframe.h"
55
56#include <algorithm>
57#include <cmath>
58
59namespace blink {
60
61namespace {
62
63void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSPropertyID id, double scale, PropertySpecificKeyframeVector& values)
64{
65    ASSERT(values.isEmpty());
66    const PropertySpecificKeyframeVector& group = effect->getPropertySpecificKeyframes(id);
67
68    for (size_t i = 0; i < group.size(); ++i) {
69        double offset = group[i]->offset() * scale;
70        values.append(group[i]->cloneWithOffset(offset));
71    }
72}
73
74}
75
76bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const AnimationEffect& effect, double minValue, double maxValue) const
77{
78    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
79
80    PropertySet properties = keyframeEffect.properties();
81
82    if (properties.isEmpty())
83        return true;
84
85    minValue = std::min(minValue, 0.0);
86    maxValue = std::max(maxValue, 1.0);
87
88    for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) {
89        // TODO: Add the ability to get expanded bounds for filters as well.
90        if (*it != CSSPropertyTransform && *it != CSSPropertyWebkitTransform)
91            continue;
92
93        const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it);
94        if (frames.isEmpty() || frames.size() < 2)
95            continue;
96
97        FloatBox originalBox(box);
98
99        for (size_t j = 0; j < frames.size() - 1; ++j) {
100            const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get());
101            const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get());
102            // TODO: Add support for inflating modes other than Replace.
103            if (frames[j]->composite() != AnimationEffect::CompositeReplace)
104                return false;
105
106            const TimingFunction& timing = frames[j]->easing();
107            double min = 0;
108            double max = 1;
109            if (j == 0) {
110                float frameLength = frames[j+1]->offset();
111                if (frameLength > 0) {
112                    min = minValue / frameLength;
113                }
114            }
115
116            if (j == frames.size() - 2) {
117                float frameLength = frames[j+1]->offset() - frames[j]->offset();
118                if (frameLength > 0) {
119                    max = 1 + (maxValue - 1) / frameLength;
120                }
121            }
122
123            FloatBox bounds;
124            timing.range(&min, &max);
125            if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds))
126                return false;
127            box.expandTo(bounds);
128        }
129    }
130    return true;
131}
132
133// -----------------------------------------------------------------------
134// CompositorAnimations public API
135// -----------------------------------------------------------------------
136
137bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect, double playerPlaybackRate)
138{
139    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
140
141    PropertySet properties = keyframeEffect.properties();
142
143    if (properties.isEmpty())
144        return false;
145
146    for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) {
147        const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it);
148        ASSERT(frames.size() >= 2);
149        for (size_t i = 0; i < frames.size(); ++i) {
150            const Keyframe::PropertySpecificKeyframe *frame = frames[i].get();
151            // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue.
152            if (frame->composite() != AnimationEffect::CompositeReplace || !frame->getAnimatableValue())
153                return false;
154
155            switch (*it) {
156            case CSSPropertyOpacity:
157                break;
158            case CSSPropertyTransform:
159                if (toAnimatableTransform(frame->getAnimatableValue().get())->transformOperations().dependsOnBoxSize())
160                    return false;
161                break;
162            case CSSPropertyWebkitFilter: {
163                const FilterOperations& operations = toAnimatableFilterOperations(frame->getAnimatableValue().get())->operations();
164                if (operations.hasFilterThatMovesPixels())
165                    return false;
166                break;
167            }
168            default:
169                return false;
170            }
171
172            // FIXME: Remove this check when crbug.com/229405 is resolved
173            if (i < frames.size() - 1 && frame->easing().type() == TimingFunction::StepsFunction)
174                return false;
175        }
176    }
177
178    CompositorAnimationsImpl::CompositorTiming out;
179    if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, playerPlaybackRate))
180        return false;
181
182    if (timing.timingFunction->type() != TimingFunction::LinearFunction) {
183        // Checks the of size of KeyframeVector instead of PropertySpecificKeyframeVector.
184        const KeyframeVector& keyframes = keyframeEffect.getFrames();
185        if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction && timing.timingFunction->type() != TimingFunction::StepsFunction)
186            return true;
187
188        // FIXME: Support non-linear timing functions in the compositor for
189        // more than two keyframes and step timing functions in the compositor.
190        return false;
191    }
192
193    return true;
194}
195
196bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
197{
198    return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
199}
200
201bool CompositorAnimations::startAnimationOnCompositor(const Element& element, double startTime, double timeOffset, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate)
202{
203    ASSERT(startedAnimationIds.isEmpty());
204    ASSERT(isCandidateForAnimationOnCompositor(timing, effect, playerPlaybackRate));
205    ASSERT(canStartAnimationOnCompositor(element));
206
207    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
208
209    RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer();
210    ASSERT(layer);
211
212    Vector<OwnPtr<WebCompositorAnimation> > animations;
213    CompositorAnimationsImpl::getAnimationOnCompositor(timing, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate);
214    ASSERT(!animations.isEmpty());
215    for (size_t i = 0; i < animations.size(); ++i) {
216        int id = animations[i]->id();
217        if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animations[i].release())) {
218            // FIXME: We should know ahead of time whether these animations can be started.
219            for (size_t j = 0; j < startedAnimationIds.size(); ++j)
220                cancelAnimationOnCompositor(element, startedAnimationIds[j]);
221            startedAnimationIds.clear();
222            return false;
223        }
224        startedAnimationIds.append(id);
225    }
226    ASSERT(!startedAnimationIds.isEmpty());
227    return true;
228}
229
230void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id)
231{
232    if (!canStartAnimationOnCompositor(element)) {
233        // When an element is being detached, we cancel any associated
234        // AnimationPlayers for CSS animations. But by the time we get
235        // here the mapping will have been removed.
236        // FIXME: Defer remove/pause operations until after the
237        // compositing update.
238        return;
239    }
240    toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id);
241}
242
243void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime)
244{
245    // FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date.
246    // https://code.google.com/p/chromium/issues/detail?id=339847
247    DisableCompositingQueryAsserts disabler;
248
249    if (!canStartAnimationOnCompositor(element)) {
250        ASSERT_NOT_REACHED();
251        return;
252    }
253    toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime);
254}
255
256// -----------------------------------------------------------------------
257// CompositorAnimationsImpl
258// -----------------------------------------------------------------------
259
260bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, double timeOffset, CompositorTiming& out, double playerPlaybackRate)
261{
262    timing.assertValid();
263
264    // All fill modes are supported (the calling code handles them).
265
266    if (timing.iterationCount <= 0)
267        return false;
268
269    if (std::isnan(timing.iterationDuration) || !timing.iterationDuration)
270        return false;
271
272    // All directions are supported.
273
274    // Now attempt an actual conversion
275    out.scaledDuration = timing.iterationDuration;
276    ASSERT(out.scaledDuration > 0);
277
278    double scaledStartDelay = timing.startDelay;
279    if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.iterationCount)
280        return false;
281
282    out.direction = timing.direction;
283
284    if (!std::isfinite(timing.iterationCount)) {
285        out.adjustedIterationCount = -1;
286    } else {
287        out.adjustedIterationCount = timing.iterationCount;
288        ASSERT(out.adjustedIterationCount > 0);
289    }
290
291    // Compositor's time offset is positive for seeking into the animation.
292    out.scaledTimeOffset = -scaledStartDelay + timeOffset;
293    out.playbackRate = timing.playbackRate * playerPlaybackRate;
294    out.fillMode = timing.fillMode == Timing::FillModeAuto ? Timing::FillModeNone : timing.fillMode;
295    out.iterationStart = timing.iterationStart;
296
297    return true;
298}
299
300namespace {
301
302template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
303void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction)
304{
305    if (!timingFunction) {
306        curve.add(keyframe);
307        return;
308    }
309
310    switch (timingFunction->type()) {
311    case TimingFunction::LinearFunction:
312        curve.add(keyframe, WebCompositorAnimationCurve::TimingFunctionTypeLinear);
313        return;
314
315    case TimingFunction::CubicBezierFunction: {
316        const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction);
317
318        if (cubic->subType() == CubicBezierTimingFunction::Custom) {
319            curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2());
320        } else {
321
322            WebCompositorAnimationCurve::TimingFunctionType easeType;
323            switch (cubic->subType()) {
324            case CubicBezierTimingFunction::Ease:
325                easeType = WebCompositorAnimationCurve::TimingFunctionTypeEase;
326                break;
327            case CubicBezierTimingFunction::EaseIn:
328                easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn;
329                break;
330            case CubicBezierTimingFunction::EaseOut:
331                easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseOut;
332                break;
333            case CubicBezierTimingFunction::EaseInOut:
334                easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut;
335                break;
336
337            // Custom Bezier are handled seperately.
338            case CubicBezierTimingFunction::Custom:
339            default:
340                ASSERT_NOT_REACHED();
341                return;
342            }
343
344            curve.add(keyframe, easeType);
345        }
346        return;
347    }
348
349    case TimingFunction::StepsFunction:
350    default:
351        ASSERT_NOT_REACHED();
352        return;
353    }
354}
355
356} // namespace anoymous
357
358void CompositorAnimationsImpl::addKeyframesToCurve(WebCompositorAnimationCurve& curve, const PropertySpecificKeyframeVector& keyframes, const Timing& timing)
359{
360    for (size_t i = 0; i < keyframes.size(); i++) {
361        const TimingFunction* keyframeTimingFunction = 0;
362        if (i < keyframes.size() - 1) { // Ignore timing function of last frame.
363            if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction) {
364                keyframeTimingFunction = timing.timingFunction.get();
365            } else {
366                keyframeTimingFunction = &keyframes[i]->easing();
367            }
368        }
369
370        // FIXME: This relies on StringKeyframes being eagerly evaluated, which will
371        // not happen eventually. Instead we should extract the CSSValue here
372        // and convert using another set of toAnimatableXXXOperations functions.
373        const AnimatableValue* value = keyframes[i]->getAnimatableValue().get();
374
375        switch (curve.type()) {
376        case WebCompositorAnimationCurve::AnimationCurveTypeFilter: {
377            OwnPtr<WebFilterOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
378            toWebFilterOperations(toAnimatableFilterOperations(value)->operations(), ops.get());
379
380            WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release());
381            WebFilterAnimationCurve* filterCurve = static_cast<WebFilterAnimationCurve*>(&curve);
382            addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction);
383            break;
384        }
385        case WebCompositorAnimationCurve::AnimationCurveTypeFloat: {
386            WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value)->toDouble());
387            WebFloatAnimationCurve* floatCurve = static_cast<WebFloatAnimationCurve*>(&curve);
388            addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction);
389            break;
390        }
391        case WebCompositorAnimationCurve::AnimationCurveTypeTransform: {
392            OwnPtr<WebTransformOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
393            toWebTransformOperations(toAnimatableTransform(value)->transformOperations(), ops.get());
394
395            WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release());
396            WebTransformAnimationCurve* transformCurve = static_cast<WebTransformAnimationCurve*>(&curve);
397            addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction);
398            break;
399        }
400        default:
401            ASSERT_NOT_REACHED();
402        }
403    }
404}
405
406void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, double startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector<OwnPtr<WebCompositorAnimation> >& animations, double playerPlaybackRate)
407{
408    ASSERT(animations.isEmpty());
409    CompositorTiming compositorTiming;
410    bool timingValid = convertTimingForCompositor(timing, timeOffset, compositorTiming, playerPlaybackRate);
411    ASSERT_UNUSED(timingValid, timingValid);
412
413    PropertySet properties = effect.properties();
414    ASSERT(!properties.isEmpty());
415    for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) {
416
417        PropertySpecificKeyframeVector values;
418        getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, values);
419
420        WebCompositorAnimation::TargetProperty targetProperty;
421        OwnPtr<WebCompositorAnimationCurve> curve;
422        switch (*it) {
423        case CSSPropertyOpacity: {
424            targetProperty = WebCompositorAnimation::TargetPropertyOpacity;
425
426            WebFloatAnimationCurve* floatCurve = Platform::current()->compositorSupport()->createFloatAnimationCurve();
427            addKeyframesToCurve(*floatCurve, values, timing);
428            curve = adoptPtr(floatCurve);
429            break;
430        }
431        case CSSPropertyWebkitFilter: {
432            targetProperty = WebCompositorAnimation::TargetPropertyFilter;
433            WebFilterAnimationCurve* filterCurve = Platform::current()->compositorSupport()->createFilterAnimationCurve();
434            addKeyframesToCurve(*filterCurve, values, timing);
435            curve = adoptPtr(filterCurve);
436            break;
437        }
438        case CSSPropertyTransform: {
439            targetProperty = WebCompositorAnimation::TargetPropertyTransform;
440            WebTransformAnimationCurve* transformCurve = Platform::current()->compositorSupport()->createTransformAnimationCurve();
441            addKeyframesToCurve(*transformCurve, values, timing);
442            curve = adoptPtr(transformCurve);
443            break;
444        }
445        default:
446            ASSERT_NOT_REACHED();
447            continue;
448        }
449        ASSERT(curve.get());
450
451        OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty));
452
453        if (!std::isnan(startTime))
454            animation->setStartTime(startTime);
455
456        animation->setIterations(compositorTiming.adjustedIterationCount);
457        animation->setIterationStart(compositorTiming.iterationStart);
458        animation->setTimeOffset(compositorTiming.scaledTimeOffset);
459
460        switch (compositorTiming.direction) {
461        case Timing::PlaybackDirectionNormal:
462            animation->setDirection(blink::WebCompositorAnimation::DirectionNormal);
463            break;
464        case Timing::PlaybackDirectionReverse:
465            animation->setDirection(blink::WebCompositorAnimation::DirectionReverse);
466            break;
467        case Timing::PlaybackDirectionAlternate:
468            animation->setDirection(blink::WebCompositorAnimation::DirectionAlternate);
469            break;
470        case Timing::PlaybackDirectionAlternateReverse:
471            animation->setDirection(blink::WebCompositorAnimation::DirectionAlternateReverse);
472            break;
473        default:
474            ASSERT_NOT_REACHED();
475        }
476        animation->setPlaybackRate(compositorTiming.playbackRate);
477
478        switch (compositorTiming.fillMode) {
479        case Timing::FillModeNone:
480            animation->setFillMode(blink::WebCompositorAnimation::FillModeNone);
481            break;
482        case Timing::FillModeForwards:
483            animation->setFillMode(blink::WebCompositorAnimation::FillModeForwards);
484            break;
485        case Timing::FillModeBackwards:
486            animation->setFillMode(blink::WebCompositorAnimation::FillModeBackwards);
487            break;
488        case Timing::FillModeBoth:
489            animation->setFillMode(blink::WebCompositorAnimation::FillModeBoth);
490            break;
491        default:
492            ASSERT_NOT_REACHED();
493        }
494        animations.append(animation.release());
495    }
496    ASSERT(!animations.isEmpty());
497}
498
499} // namespace blink
500