2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/animation/CompositorAnimations.h"
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"
63 void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSPropertyID id, double scale, PropertySpecificKeyframeVector& values)
65 ASSERT(values.isEmpty());
67 for (const auto& keyframe : effect->getPropertySpecificKeyframes(id)) {
68 double offset = keyframe->offset() * scale;
69 values.append(keyframe->cloneWithOffset(offset));
75 bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const AnimationEffect& effect, double minValue, double maxValue) const
77 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
79 PropertySet properties = keyframeEffect.properties();
81 if (properties.isEmpty())
84 minValue = std::min(minValue, 0.0);
85 maxValue = std::max(maxValue, 1.0);
87 for (const auto& property : properties) {
88 // TODO: Add the ability to get expanded bounds for filters as well.
89 if (property != CSSPropertyTransform && property != CSSPropertyWebkitTransform)
92 const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(property);
93 if (frames.isEmpty() || frames.size() < 2)
96 FloatBox originalBox(box);
98 for (size_t j = 0; j < frames.size() - 1; ++j) {
99 const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get());
100 const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get());
101 // TODO: Add support for inflating modes other than Replace.
102 if (frames[j]->composite() != AnimationEffect::CompositeReplace)
105 const TimingFunction& timing = frames[j]->easing();
109 float frameLength = frames[j+1]->offset();
110 if (frameLength > 0) {
111 min = minValue / frameLength;
115 if (j == frames.size() - 2) {
116 float frameLength = frames[j+1]->offset() - frames[j]->offset();
117 if (frameLength > 0) {
118 max = 1 + (maxValue - 1) / frameLength;
123 timing.range(&min, &max);
124 if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds))
126 box.expandTo(bounds);
132 // -----------------------------------------------------------------------
133 // CompositorAnimations public API
134 // -----------------------------------------------------------------------
136 bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect, double playerPlaybackRate)
138 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
140 PropertySet properties = keyframeEffect.properties();
142 if (properties.isEmpty())
145 for (const auto& property : properties) {
146 const PropertySpecificKeyframeVector& keyframes = keyframeEffect.getPropertySpecificKeyframes(property);
147 ASSERT(keyframes.size() >= 2);
148 auto* lastKeyframe = keyframes.last().get();
149 for (const auto& keyframe : keyframes) {
150 // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue.
151 if (keyframe->composite() != AnimationEffect::CompositeReplace || !keyframe->getAnimatableValue())
155 case CSSPropertyOpacity:
157 case CSSPropertyTransform:
158 if (toAnimatableTransform(keyframe->getAnimatableValue().get())->transformOperations().dependsOnBoxSize())
161 case CSSPropertyWebkitFilter: {
162 const FilterOperations& operations = toAnimatableFilterOperations(keyframe->getAnimatableValue().get())->operations();
163 if (operations.hasFilterThatMovesPixels())
171 // FIXME: Remove this check when crbug.com/229405 is resolved
172 if (keyframe != lastKeyframe && keyframe->easing().type() == TimingFunction::StepsFunction)
177 CompositorAnimationsImpl::CompositorTiming out;
178 if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, playerPlaybackRate))
181 if (timing.timingFunction->type() != TimingFunction::LinearFunction) {
182 // Checks the of size of KeyframeVector instead of PropertySpecificKeyframeVector.
183 const KeyframeVector& keyframes = keyframeEffect.getFrames();
184 if (keyframes.size() == 2 && keyframes.first()->easing().type() == TimingFunction::LinearFunction && timing.timingFunction->type() != TimingFunction::StepsFunction)
187 // FIXME: Support non-linear timing functions in the compositor for
188 // more than two keyframes and step timing functions in the compositor.
195 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
197 return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
200 bool CompositorAnimations::startAnimationOnCompositor(const Element& element, double startTime, double timeOffset, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate)
202 ASSERT(startedAnimationIds.isEmpty());
203 ASSERT(isCandidateForAnimationOnCompositor(timing, effect, playerPlaybackRate));
204 ASSERT(canStartAnimationOnCompositor(element));
206 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
208 RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer();
211 Vector<OwnPtr<WebCompositorAnimation>> animations;
212 CompositorAnimationsImpl::getAnimationOnCompositor(timing, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate);
213 ASSERT(!animations.isEmpty());
214 for (auto& animation : animations) {
215 int id = animation->id();
216 if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animation.release())) {
217 // FIXME: We should know ahead of time whether these animations can be started.
218 for (int startedAnimationId : startedAnimationIds)
219 cancelAnimationOnCompositor(element, startedAnimationId);
220 startedAnimationIds.clear();
223 startedAnimationIds.append(id);
225 ASSERT(!startedAnimationIds.isEmpty());
229 void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id)
231 if (!canStartAnimationOnCompositor(element)) {
232 // When an element is being detached, we cancel any associated
233 // AnimationPlayers for CSS animations. But by the time we get
234 // here the mapping will have been removed.
235 // FIXME: Defer remove/pause operations until after the
236 // compositing update.
239 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id);
242 void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime)
244 // FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date.
245 // https://code.google.com/p/chromium/issues/detail?id=339847
246 DisableCompositingQueryAsserts disabler;
248 if (!canStartAnimationOnCompositor(element)) {
249 ASSERT_NOT_REACHED();
252 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime);
255 // -----------------------------------------------------------------------
256 // CompositorAnimationsImpl
257 // -----------------------------------------------------------------------
259 bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, double timeOffset, CompositorTiming& out, double playerPlaybackRate)
261 timing.assertValid();
263 if (!timing.iterationCount || !timing.iterationDuration)
266 if (!std::isfinite(timing.iterationCount)) {
267 out.adjustedIterationCount = -1;
269 out.adjustedIterationCount = timing.iterationCount;
272 out.scaledDuration = timing.iterationDuration;
273 out.direction = timing.direction;
274 // Compositor's time offset is positive for seeking into the animation.
275 out.scaledTimeOffset = -timing.startDelay + timeOffset;
276 out.playbackRate = timing.playbackRate * playerPlaybackRate;
277 out.fillMode = timing.fillMode == Timing::FillModeAuto ? Timing::FillModeNone : timing.fillMode;
278 out.iterationStart = timing.iterationStart;
285 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
286 void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction)
288 if (!timingFunction) {
293 switch (timingFunction->type()) {
294 case TimingFunction::LinearFunction:
295 curve.add(keyframe, WebCompositorAnimationCurve::TimingFunctionTypeLinear);
298 case TimingFunction::CubicBezierFunction: {
299 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction);
301 if (cubic->subType() == CubicBezierTimingFunction::Custom) {
302 curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2());
305 WebCompositorAnimationCurve::TimingFunctionType easeType;
306 switch (cubic->subType()) {
307 case CubicBezierTimingFunction::Ease:
308 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEase;
310 case CubicBezierTimingFunction::EaseIn:
311 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn;
313 case CubicBezierTimingFunction::EaseOut:
314 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseOut;
316 case CubicBezierTimingFunction::EaseInOut:
317 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut;
320 // Custom Bezier are handled seperately.
321 case CubicBezierTimingFunction::Custom:
323 ASSERT_NOT_REACHED();
327 curve.add(keyframe, easeType);
332 case TimingFunction::StepsFunction:
334 ASSERT_NOT_REACHED();
339 } // namespace anoymous
341 void CompositorAnimationsImpl::addKeyframesToCurve(WebCompositorAnimationCurve& curve, const PropertySpecificKeyframeVector& keyframes, const Timing& timing)
343 auto* lastKeyframe = keyframes.last().get();
344 for (const auto& keyframe : keyframes) {
345 const TimingFunction* keyframeTimingFunction = 0;
346 if (keyframe != lastKeyframe) { // Ignore timing function of last frame.
347 if (keyframes.size() == 2 && keyframes.first()->easing().type() == TimingFunction::LinearFunction)
348 keyframeTimingFunction = timing.timingFunction.get();
350 keyframeTimingFunction = &keyframe->easing();
353 // FIXME: This relies on StringKeyframes being eagerly evaluated, which will
354 // not happen eventually. Instead we should extract the CSSValue here
355 // and convert using another set of toAnimatableXXXOperations functions.
356 const AnimatableValue* value = keyframe->getAnimatableValue().get();
358 switch (curve.type()) {
359 case WebCompositorAnimationCurve::AnimationCurveTypeFilter: {
360 OwnPtr<WebFilterOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
361 toWebFilterOperations(toAnimatableFilterOperations(value)->operations(), ops.get());
363 WebFilterKeyframe filterKeyframe(keyframe->offset(), ops.release());
364 WebFilterAnimationCurve* filterCurve = static_cast<WebFilterAnimationCurve*>(&curve);
365 addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction);
368 case WebCompositorAnimationCurve::AnimationCurveTypeFloat: {
369 WebFloatKeyframe floatKeyframe(keyframe->offset(), toAnimatableDouble(value)->toDouble());
370 WebFloatAnimationCurve* floatCurve = static_cast<WebFloatAnimationCurve*>(&curve);
371 addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction);
374 case WebCompositorAnimationCurve::AnimationCurveTypeTransform: {
375 OwnPtr<WebTransformOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
376 toWebTransformOperations(toAnimatableTransform(value)->transformOperations(), ops.get());
378 WebTransformKeyframe transformKeyframe(keyframe->offset(), ops.release());
379 WebTransformAnimationCurve* transformCurve = static_cast<WebTransformAnimationCurve*>(&curve);
380 addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction);
384 ASSERT_NOT_REACHED();
389 void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, double startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector<OwnPtr<WebCompositorAnimation> >& animations, double playerPlaybackRate)
391 ASSERT(animations.isEmpty());
392 CompositorTiming compositorTiming;
393 bool timingValid = convertTimingForCompositor(timing, timeOffset, compositorTiming, playerPlaybackRate);
394 ASSERT_UNUSED(timingValid, timingValid);
396 PropertySet properties = effect.properties();
397 ASSERT(!properties.isEmpty());
398 for (const auto& property : properties) {
399 PropertySpecificKeyframeVector values;
400 getKeyframeValuesForProperty(&effect, property, compositorTiming.scaledDuration, values);
402 WebCompositorAnimation::TargetProperty targetProperty;
403 OwnPtr<WebCompositorAnimationCurve> curve;
405 case CSSPropertyOpacity: {
406 targetProperty = WebCompositorAnimation::TargetPropertyOpacity;
408 WebFloatAnimationCurve* floatCurve = Platform::current()->compositorSupport()->createFloatAnimationCurve();
409 addKeyframesToCurve(*floatCurve, values, timing);
410 curve = adoptPtr(floatCurve);
413 case CSSPropertyWebkitFilter: {
414 targetProperty = WebCompositorAnimation::TargetPropertyFilter;
415 WebFilterAnimationCurve* filterCurve = Platform::current()->compositorSupport()->createFilterAnimationCurve();
416 addKeyframesToCurve(*filterCurve, values, timing);
417 curve = adoptPtr(filterCurve);
420 case CSSPropertyTransform: {
421 targetProperty = WebCompositorAnimation::TargetPropertyTransform;
422 WebTransformAnimationCurve* transformCurve = Platform::current()->compositorSupport()->createTransformAnimationCurve();
423 addKeyframesToCurve(*transformCurve, values, timing);
424 curve = adoptPtr(transformCurve);
428 ASSERT_NOT_REACHED();
433 OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty));
435 if (!std::isnan(startTime))
436 animation->setStartTime(startTime);
438 animation->setIterations(compositorTiming.adjustedIterationCount);
439 animation->setIterationStart(compositorTiming.iterationStart);
440 animation->setTimeOffset(compositorTiming.scaledTimeOffset);
442 switch (compositorTiming.direction) {
443 case Timing::PlaybackDirectionNormal:
444 animation->setDirection(blink::WebCompositorAnimation::DirectionNormal);
446 case Timing::PlaybackDirectionReverse:
447 animation->setDirection(blink::WebCompositorAnimation::DirectionReverse);
449 case Timing::PlaybackDirectionAlternate:
450 animation->setDirection(blink::WebCompositorAnimation::DirectionAlternate);
452 case Timing::PlaybackDirectionAlternateReverse:
453 animation->setDirection(blink::WebCompositorAnimation::DirectionAlternateReverse);
456 ASSERT_NOT_REACHED();
458 animation->setPlaybackRate(compositorTiming.playbackRate);
460 switch (compositorTiming.fillMode) {
461 case Timing::FillModeNone:
462 animation->setFillMode(blink::WebCompositorAnimation::FillModeNone);
464 case Timing::FillModeForwards:
465 animation->setFillMode(blink::WebCompositorAnimation::FillModeForwards);
467 case Timing::FillModeBackwards:
468 animation->setFillMode(blink::WebCompositorAnimation::FillModeBackwards);
470 case Timing::FillModeBoth:
471 animation->setFillMode(blink::WebCompositorAnimation::FillModeBoth);
474 ASSERT_NOT_REACHED();
476 animations.append(animation.release());
478 ASSERT(!animations.isEmpty());