• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
59 namespace blink {
60 
61 namespace {
62 
getKeyframeValuesForProperty(const KeyframeEffectModelBase * effect,CSSPropertyID id,double scale,PropertySpecificKeyframeVector & values)63 void 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 
getAnimatedBoundingBox(FloatBox & box,const AnimationEffect & effect,double minValue,double maxValue) const76 bool 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 
isCandidateForAnimationOnCompositor(const Timing & timing,const AnimationEffect & effect,double playerPlaybackRate)137 bool 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 
canStartAnimationOnCompositor(const Element & element)196 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
197 {
198     return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
199 }
200 
startAnimationOnCompositor(const Element & element,double startTime,double timeOffset,const Timing & timing,const AnimationEffect & effect,Vector<int> & startedAnimationIds,double playerPlaybackRate)201 bool 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 
cancelAnimationOnCompositor(const Element & element,int id)230 void 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 
pauseAnimationForTestingOnCompositor(const Element & element,int id,double pauseTime)243 void 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 
convertTimingForCompositor(const Timing & timing,double timeOffset,CompositorTiming & out,double playerPlaybackRate)260 bool 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 
300 namespace {
301 
302 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
addKeyframeWithTimingFunction(PlatformAnimationCurveType & curve,const PlatformAnimationKeyframeType & keyframe,const TimingFunction * timingFunction)303 void 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 
addKeyframesToCurve(WebCompositorAnimationCurve & curve,const PropertySpecificKeyframeVector & keyframes,const Timing & timing)358 void 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 
getAnimationOnCompositor(const Timing & timing,double startTime,double timeOffset,const KeyframeEffectModelBase & effect,Vector<OwnPtr<WebCompositorAnimation>> & animations,double playerPlaybackRate)406 void 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