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/AnimatableDouble.h"
35 #include "core/animation/AnimatableFilterOperations.h"
36 #include "core/animation/AnimatableTransform.h"
37 #include "core/animation/AnimatableValue.h"
38 #include "core/animation/AnimationTranslationUtil.h"
39 #include "core/animation/CompositorAnimationsImpl.h"
40 #include "core/rendering/CompositedLayerMapping.h"
41 #include "core/rendering/RenderBoxModelObject.h"
42 #include "core/rendering/RenderLayer.h"
43 #include "core/rendering/RenderObject.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebAnimation.h"
46 #include "public/platform/WebCompositorSupport.h"
47 #include "public/platform/WebFilterAnimationCurve.h"
48 #include "public/platform/WebFilterKeyframe.h"
49 #include "public/platform/WebFloatAnimationCurve.h"
50 #include "public/platform/WebFloatKeyframe.h"
51 #include "public/platform/WebTransformAnimationCurve.h"
52 #include "public/platform/WebTransformKeyframe.h"
61 void getKeyframeValuesForProperty(const KeyframeEffectModel* effect, CSSPropertyID id, double scale, bool reverse, KeyframeVector& values)
63 ASSERT(values.isEmpty());
64 const KeyframeVector& group = effect->getPropertySpecificKeyframes(id);
67 for (size_t i = group.size(); i--;) {
68 double offset = (1 - group[i]->offset()) * scale;
69 values.append(group[i]->cloneWithOffset(offset));
72 for (size_t i = 0; i < group.size(); ++i) {
73 double offset = group[i]->offset() * scale;
74 values.append(group[i]->cloneWithOffset(offset));
81 // -----------------------------------------------------------------------
82 // TimingFunctionReverser methods
83 // -----------------------------------------------------------------------
85 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const LinearTimingFunction* timefunc)
87 return const_cast<LinearTimingFunction*>(timefunc);
90 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const CubicBezierTimingFunction* timefunc)
92 switch (timefunc->subType()) {
93 case CubicBezierTimingFunction::EaseIn:
94 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
95 case CubicBezierTimingFunction::EaseOut:
96 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
97 case CubicBezierTimingFunction::EaseInOut:
98 return const_cast<CubicBezierTimingFunction*>(timefunc);
99 case CubicBezierTimingFunction::Ease: // Ease is not symmetrical
100 case CubicBezierTimingFunction::Custom:
101 return CubicBezierTimingFunction::create(1 - timefunc->x2(), 1 - timefunc->y2(), 1 - timefunc->x1(), 1 - timefunc->y1());
103 ASSERT_NOT_REACHED();
104 return PassRefPtr<TimingFunction>();
108 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const ChainedTimingFunction* timefunc)
110 RefPtr<ChainedTimingFunction> reversed = ChainedTimingFunction::create();
111 for (size_t i = 0; i < timefunc->m_segments.size(); i++) {
112 size_t index = timefunc->m_segments.size() - i - 1;
114 RefPtr<TimingFunction> rtf = reverse(timefunc->m_segments[index].m_timingFunction.get());
115 reversed->appendSegment(1 - timefunc->m_segments[index].m_min, rtf.get());
120 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const TimingFunction* timefunc)
122 switch (timefunc->type()) {
123 case TimingFunction::LinearFunction: {
124 const LinearTimingFunction* linear = toLinearTimingFunction(timefunc);
125 return reverse(linear);
127 case TimingFunction::CubicBezierFunction: {
128 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timefunc);
129 return reverse(cubic);
131 case TimingFunction::ChainedFunction: {
132 const ChainedTimingFunction* chained = toChainedTimingFunction(timefunc);
133 return reverse(chained);
136 // Steps function can not be reversed.
137 case TimingFunction::StepsFunction:
139 ASSERT_NOT_REACHED();
140 return PassRefPtr<TimingFunction>();
144 // -----------------------------------------------------------------------
145 // CompositorAnimations public API
146 // -----------------------------------------------------------------------
148 bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect)
150 const KeyframeEffectModel& keyframeEffect = *toKeyframeEffectModel(&effect);
152 // Are the keyframes convertible?
153 const KeyframeEffectModel::KeyframeVector frames = keyframeEffect.getFrames();
154 for (size_t i = 0; i < frames.size(); ++i) {
155 // Only replace mode can be accelerated
156 if (frames[i]->composite() != AnimationEffect::CompositeReplace)
159 // Check all the properties can be accelerated
160 const PropertySet properties = frames[i]->properties(); // FIXME: properties creates a whole new PropertySet!
162 if (properties.isEmpty())
165 for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) {
167 case CSSPropertyOpacity:
169 case CSSPropertyWebkitTransform:
170 if (toAnimatableTransform(frames[i]->propertyValue(CSSPropertyWebkitTransform))->transformOperations().dependsOnBoxSize())
173 case CSSPropertyWebkitFilter: {
174 const FilterOperations& operations = toAnimatableFilterOperations(frames[i]->propertyValue(CSSPropertyWebkitFilter))->operations();
175 if (operations.hasFilterThatMovesPixels())
177 for (size_t i = 0; i < operations.size(); i++) {
178 const FilterOperation& op = *operations.at(i);
179 if (op.type() == FilterOperation::VALIDATED_CUSTOM || op.type() == FilterOperation::CUSTOM)
190 // Is the timing object convertible?
191 CompositorAnimationsImpl::CompositorTiming out;
192 if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, out))
195 // Is the timing function convertible?
196 switch (timing.timingFunction->type()) {
197 case TimingFunction::LinearFunction:
200 case TimingFunction::CubicBezierFunction:
201 // Can have a cubic if we don't have to split it (IE only have two frames).
202 if (frames.size() != 2)
205 ASSERT(frames[0]->offset() == 0.0 && frames[1]->offset() == 1.0);
208 case TimingFunction::StepsFunction:
211 case TimingFunction::ChainedFunction: {
212 // Currently we only support chained segments in the form the CSS code
213 // generates. These chained segments are only one level deep and have
214 // one timing function per frame.
215 const ChainedTimingFunction* chained = static_cast<const ChainedTimingFunction*>(timing.timingFunction.get());
216 if (!chained->m_segments.size())
219 if (frames.size() != chained->m_segments.size() + 1)
222 for (size_t timeIndex = 0; timeIndex < chained->m_segments.size(); timeIndex++) {
223 const ChainedTimingFunction::Segment& segment = chained->m_segments[timeIndex];
225 if (frames[timeIndex]->offset() != segment.m_min || frames[timeIndex + 1]->offset() != segment.m_max)
228 switch (segment.m_timingFunction->type()) {
229 case TimingFunction::LinearFunction:
230 case TimingFunction::CubicBezierFunction:
233 case TimingFunction::StepsFunction:
234 case TimingFunction::ChainedFunction:
243 ASSERT_NOT_REACHED();
250 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
252 return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
255 bool CompositorAnimations::startAnimationOnCompositor(const Element& element, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds)
257 ASSERT(startedAnimationIds.isEmpty());
258 ASSERT(isCandidateForAnimationOnCompositor(timing, effect));
259 ASSERT(canStartAnimationOnCompositor(element));
261 const KeyframeEffectModel& keyframeEffect = *toKeyframeEffectModel(&effect);
263 RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer();
266 Vector<OwnPtr<blink::WebAnimation> > animations;
267 CompositorAnimationsImpl::getAnimationOnCompositor(timing, keyframeEffect, animations);
268 ASSERT(!animations.isEmpty());
269 for (size_t i = 0; i < animations.size(); ++i) {
270 int id = animations[i]->id();
271 if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animations[i].release())) {
272 // FIXME: We should know ahead of time whether these animations can be started.
273 for (size_t j = 0; j < startedAnimationIds.size(); ++j)
274 cancelAnimationOnCompositor(element, startedAnimationIds[j]);
275 startedAnimationIds.clear();
278 startedAnimationIds.append(id);
280 ASSERT(!startedAnimationIds.isEmpty());
284 void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id)
286 if (!canStartAnimationOnCompositor(element)) {
287 ASSERT_NOT_REACHED();
290 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id);
293 void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime)
295 if (!canStartAnimationOnCompositor(element)) {
296 ASSERT_NOT_REACHED();
299 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime);
302 // -----------------------------------------------------------------------
303 // CompositorAnimationsImpl
304 // -----------------------------------------------------------------------
306 bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, CompositorTiming& out)
308 timing.assertValid();
310 // All fill modes are supported (the calling code handles them).
312 // FIXME: Support non-zero iteration start.
313 if (timing.iterationStart)
316 // FIXME: Compositor only supports positive, integer iteration counts.
317 // Zero iterations could be converted, but silly.
318 if ((std::floor(timing.iterationCount) != timing.iterationCount) || timing.iterationCount <= 0)
321 if (!timing.iterationDuration)
324 // FIXME: Support other playback rates
325 if (timing.playbackRate != 1)
328 // All directions are supported.
330 // Now attempt an actual conversion
331 out.scaledDuration = timing.iterationDuration;
332 ASSERT(out.scaledDuration > 0);
334 double scaledStartDelay = timing.startDelay;
335 if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.iterationCount)
338 out.reverse = (timing.direction == Timing::PlaybackDirectionReverse
339 || timing.direction == Timing::PlaybackDirectionAlternateReverse);
340 out.alternate = (timing.direction == Timing::PlaybackDirectionAlternate
341 || timing.direction == Timing::PlaybackDirectionAlternateReverse);
343 if (!std::isfinite(timing.iterationCount)) {
344 out.adjustedIterationCount = -1;
346 out.adjustedIterationCount = std::floor(timing.iterationCount);
347 ASSERT(out.adjustedIterationCount > 0);
350 // Compositor's time offset is positive for seeking into the animation.
351 out.scaledTimeOffset = -scaledStartDelay;
357 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
358 void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction)
360 if (!timingFunction) {
365 switch (timingFunction->type()) {
366 case TimingFunction::LinearFunction:
367 curve.add(keyframe, blink::WebAnimationCurve::TimingFunctionTypeLinear);
370 case TimingFunction::CubicBezierFunction: {
371 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction);
373 if (cubic->subType() == CubicBezierTimingFunction::Custom) {
374 curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2());
377 blink::WebAnimationCurve::TimingFunctionType easeType;
378 switch (cubic->subType()) {
379 case CubicBezierTimingFunction::Ease:
380 easeType = blink::WebAnimationCurve::TimingFunctionTypeEase;
382 case CubicBezierTimingFunction::EaseIn:
383 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseIn;
385 case CubicBezierTimingFunction::EaseOut:
386 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseOut;
388 case CubicBezierTimingFunction::EaseInOut:
389 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseInOut;
392 // Custom Bezier are handled seperately.
393 case CubicBezierTimingFunction::Custom:
395 ASSERT_NOT_REACHED();
399 curve.add(keyframe, easeType);
404 case TimingFunction::StepsFunction:
405 case TimingFunction::ChainedFunction:
407 ASSERT_NOT_REACHED();
412 } // namespace anoymous
414 void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& curve, const KeyframeVector& keyframes, const TimingFunction& timingFunction)
416 for (size_t i = 0; i < keyframes.size(); i++) {
417 const TimingFunction* keyframeTimingFunction = 0;
418 if (i + 1 < keyframes.size()) { // Last keyframe has no timing function
419 switch (timingFunction.type()) {
420 case TimingFunction::LinearFunction:
421 case TimingFunction::CubicBezierFunction:
422 keyframeTimingFunction = &timingFunction;
425 case TimingFunction::ChainedFunction: {
426 const ChainedTimingFunction& chained = toChainedTimingFunction(timingFunction);
427 // ChainedTimingFunction criteria was checked in isCandidate,
428 // assert it is valid.
429 ASSERT(keyframes.size() == chained.m_segments.size() + 1);
431 keyframeTimingFunction = chained.m_segments[i].m_timingFunction.get();
434 case TimingFunction::StepsFunction:
436 ASSERT_NOT_REACHED();
440 ASSERT(!keyframes[i]->value()->dependsOnUnderlyingValue());
441 RefPtr<AnimatableValue> value = keyframes[i]->value()->compositeOnto(0);
443 switch (curve.type()) {
444 case blink::WebAnimationCurve::AnimationCurveTypeFilter: {
445 OwnPtr<blink::WebFilterOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createFilterOperations());
446 bool converted = toWebFilterOperations(toAnimatableFilterOperations(value.get())->operations(), ops.get());
447 ASSERT_UNUSED(converted, converted);
449 blink::WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release());
450 blink::WebFilterAnimationCurve* filterCurve = static_cast<blink::WebFilterAnimationCurve*>(&curve);
451 addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction);
454 case blink::WebAnimationCurve::AnimationCurveTypeFloat: {
455 blink::WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value.get())->toDouble());
456 blink::WebFloatAnimationCurve* floatCurve = static_cast<blink::WebFloatAnimationCurve*>(&curve);
457 addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction);
460 case blink::WebAnimationCurve::AnimationCurveTypeTransform: {
461 OwnPtr<blink::WebTransformOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createTransformOperations());
462 toWebTransformOperations(toAnimatableTransform(value.get())->transformOperations(), FloatSize(), ops.get());
464 blink::WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release());
465 blink::WebTransformAnimationCurve* transformCurve = static_cast<blink::WebTransformAnimationCurve*>(&curve);
466 addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction);
470 ASSERT_NOT_REACHED();
475 void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, const KeyframeEffectModel& effect, Vector<OwnPtr<blink::WebAnimation> >& animations)
477 ASSERT(animations.isEmpty());
478 CompositorTiming compositorTiming;
479 bool timingValid = convertTimingForCompositor(timing, compositorTiming);
480 ASSERT_UNUSED(timingValid, timingValid);
482 RefPtr<TimingFunction> timingFunction = timing.timingFunction;
483 if (compositorTiming.reverse)
484 timingFunction = CompositorAnimationsTimingFunctionReverser::reverse(timingFunction.get());
486 PropertySet properties = effect.properties();
487 ASSERT(!properties.isEmpty());
488 for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) {
490 KeyframeVector values;
491 getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, compositorTiming.reverse, values);
493 blink::WebAnimation::TargetProperty targetProperty;
494 OwnPtr<blink::WebAnimationCurve> curve;
496 case CSSPropertyOpacity: {
497 targetProperty = blink::WebAnimation::TargetPropertyOpacity;
499 blink::WebFloatAnimationCurve* floatCurve = blink::Platform::current()->compositorSupport()->createFloatAnimationCurve();
500 addKeyframesToCurve(*floatCurve, values, *timingFunction.get());
501 curve = adoptPtr(floatCurve);
504 case CSSPropertyWebkitFilter: {
505 targetProperty = blink::WebAnimation::TargetPropertyFilter;
506 blink::WebFilterAnimationCurve* filterCurve = blink::Platform::current()->compositorSupport()->createFilterAnimationCurve();
507 addKeyframesToCurve(*filterCurve, values, *timingFunction);
508 curve = adoptPtr(filterCurve);
511 case CSSPropertyWebkitTransform: {
512 targetProperty = blink::WebAnimation::TargetPropertyTransform;
513 blink::WebTransformAnimationCurve* transformCurve = blink::Platform::current()->compositorSupport()->createTransformAnimationCurve();
514 addKeyframesToCurve(*transformCurve, values, *timingFunction.get());
515 curve = adoptPtr(transformCurve);
519 ASSERT_NOT_REACHED();
524 OwnPtr<blink::WebAnimation> animation = adoptPtr(blink::Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty));
526 animation->setIterations(compositorTiming.adjustedIterationCount);
527 animation->setTimeOffset(compositorTiming.scaledTimeOffset);
528 animation->setAlternatesDirection(compositorTiming.alternate);
530 animations.append(animation.release());
532 ASSERT(!animations.isEmpty());
535 } // namespace WebCore