flutter_flutter/engine/core/animation/CompositorAnimations.cpp
Eric Seidel e0fd75b5ab Make absolute and sort all Sky headers
This caused us to lose our gn check certification. :(

Turns out gn check was just ignoring all the header
paths it didn't understand and so gn check passing
for sky wasn't meaning much.  I tried to straighten
out some of the mess in this CL, but its going to take
several more rounds of massaging before gn check
passes again.  On the bright side (almost) all of
our headers are absolute now.  Turns out my script
(attached to the bug) didn't notice ../ includes
but I'll fix that in the next patch.

R=abarth@chromium.org
BUG=435361

Review URL: https://codereview.chromium.org/746023002
2014-11-20 17:42:05 -08:00

537 lines
23 KiB
C++

/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/config.h"
#include "sky/engine/core/animation/CompositorAnimations.h"
#include "sky/engine/core/animation/AnimationTranslationUtil.h"
#include "sky/engine/core/animation/CompositorAnimationsImpl.h"
#include "sky/engine/core/animation/animatable/AnimatableDouble.h"
#include "sky/engine/core/animation/animatable/AnimatableFilterOperations.h"
#include "sky/engine/core/animation/animatable/AnimatableTransform.h"
#include "sky/engine/core/animation/animatable/AnimatableValue.h"
#include "sky/engine/core/rendering/RenderBoxModelObject.h"
#include "sky/engine/core/rendering/RenderLayer.h"
#include "sky/engine/core/rendering/RenderObject.h"
#include "sky/engine/core/rendering/compositing/CompositedLayerMapping.h"
#include "sky/engine/platform/geometry/FloatBox.h"
#include "sky/engine/public/platform/Platform.h"
#include "sky/engine/public/platform/WebCompositorAnimation.h"
#include "sky/engine/public/platform/WebCompositorSupport.h"
#include "sky/engine/public/platform/WebFilterAnimationCurve.h"
#include "sky/engine/public/platform/WebFilterKeyframe.h"
#include "sky/engine/public/platform/WebFloatAnimationCurve.h"
#include "sky/engine/public/platform/WebFloatKeyframe.h"
#include "sky/engine/public/platform/WebTransformAnimationCurve.h"
#include "sky/engine/public/platform/WebTransformKeyframe.h"
#include <algorithm>
#include <cmath>
namespace blink {
namespace {
void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSPropertyID id, double scale, bool reverse, PropertySpecificKeyframeVector& values)
{
ASSERT(values.isEmpty());
const PropertySpecificKeyframeVector& group = effect->getPropertySpecificKeyframes(id);
if (reverse) {
for (size_t i = group.size(); i--;) {
double offset = (1 - group[i]->offset()) * scale;
values.append(group[i]->cloneWithOffset(offset));
}
} else {
for (size_t i = 0; i < group.size(); ++i) {
double offset = group[i]->offset() * scale;
values.append(group[i]->cloneWithOffset(offset));
}
}
}
}
// -----------------------------------------------------------------------
// TimingFunctionReverser methods
// -----------------------------------------------------------------------
PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const LinearTimingFunction& timefunc)
{
return const_cast<LinearTimingFunction*>(&timefunc);
}
PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const CubicBezierTimingFunction& timefunc)
{
switch (timefunc.subType()) {
case CubicBezierTimingFunction::EaseIn:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
case CubicBezierTimingFunction::EaseOut:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
case CubicBezierTimingFunction::EaseInOut:
return const_cast<CubicBezierTimingFunction*>(&timefunc);
case CubicBezierTimingFunction::Ease: // Ease is not symmetrical
case CubicBezierTimingFunction::Custom:
return CubicBezierTimingFunction::create(1 - timefunc.x2(), 1 - timefunc.y2(), 1 - timefunc.x1(), 1 - timefunc.y1());
default:
ASSERT_NOT_REACHED();
return PassRefPtr<TimingFunction>();
}
}
PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const TimingFunction& timefunc)
{
switch (timefunc.type()) {
case TimingFunction::LinearFunction: {
const LinearTimingFunction& linear = toLinearTimingFunction(timefunc);
return reverse(linear);
}
case TimingFunction::CubicBezierFunction: {
const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(timefunc);
return reverse(cubic);
}
// Steps function can not be reversed.
case TimingFunction::StepsFunction:
default:
ASSERT_NOT_REACHED();
return PassRefPtr<TimingFunction>();
}
}
bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const AnimationEffect& effect, double minValue, double maxValue) const
{
const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
PropertySet properties = keyframeEffect.properties();
if (properties.isEmpty())
return true;
minValue = std::min(minValue, 0.0);
maxValue = std::max(maxValue, 1.0);
for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) {
// TODO: Add the ability to get expanded bounds for filters as well.
if (*it != CSSPropertyTransform && *it != CSSPropertyWebkitTransform)
continue;
const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it);
if (frames.isEmpty() || frames.size() < 2)
continue;
FloatBox originalBox(box);
for (size_t j = 0; j < frames.size() - 1; ++j) {
const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get());
const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get());
// TODO: Add support for inflating modes other than Replace.
if (frames[j]->composite() != AnimationEffect::CompositeReplace)
return false;
const TimingFunction& timing = frames[j]->easing();
double min = 0;
double max = 1;
if (j == 0) {
float frameLength = frames[j+1]->offset();
if (frameLength > 0) {
min = minValue / frameLength;
}
}
if (j == frames.size() - 2) {
float frameLength = frames[j+1]->offset() - frames[j]->offset();
if (frameLength > 0) {
max = 1 + (maxValue - 1) / frameLength;
}
}
FloatBox bounds;
timing.range(&min, &max);
if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds))
return false;
box.expandTo(bounds);
}
}
return true;
}
// -----------------------------------------------------------------------
// CompositorAnimations public API
// -----------------------------------------------------------------------
bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect)
{
const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
PropertySet properties = keyframeEffect.properties();
if (properties.isEmpty())
return false;
for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) {
const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it);
ASSERT(frames.size() >= 2);
for (size_t i = 0; i < frames.size(); ++i) {
const Keyframe::PropertySpecificKeyframe *frame = frames[i].get();
// FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue.
if (frame->composite() != AnimationEffect::CompositeReplace || !frame->getAnimatableValue())
return false;
switch (*it) {
case CSSPropertyOpacity:
break;
case CSSPropertyTransform:
if (toAnimatableTransform(frame->getAnimatableValue().get())->transformOperations().dependsOnBoxSize())
return false;
break;
case CSSPropertyWebkitFilter: {
const FilterOperations& operations = toAnimatableFilterOperations(frame->getAnimatableValue().get())->operations();
if (operations.hasFilterThatMovesPixels())
return false;
break;
}
default:
return false;
}
// FIXME: Remove this check when crbug.com/229405 is resolved
if (i < frames.size() - 1 && frame->easing().type() == TimingFunction::StepsFunction)
return false;
}
}
CompositorAnimationsImpl::CompositorTiming out;
if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out))
return false;
if (timing.timingFunction->type() != TimingFunction::LinearFunction) {
// Checks the of size of KeyframeVector instead of PropertySpecificKeyframeVector.
const KeyframeVector& keyframes = keyframeEffect.getFrames();
if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction && timing.timingFunction->type() != TimingFunction::StepsFunction)
return true;
// FIXME: Support non-linear timing functions in the compositor for
// more than two keyframes and step timing functions in the compositor.
return false;
}
return true;
}
bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
{
return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
}
bool CompositorAnimations::startAnimationOnCompositor(const Element& element, double startTime, double timeOffset, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds)
{
ASSERT(startedAnimationIds.isEmpty());
ASSERT(isCandidateForAnimationOnCompositor(timing, effect));
ASSERT(canStartAnimationOnCompositor(element));
const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);
RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer();
ASSERT(layer);
Vector<OwnPtr<WebCompositorAnimation> > animations;
CompositorAnimationsImpl::getAnimationOnCompositor(timing, startTime, timeOffset, keyframeEffect, animations);
ASSERT(!animations.isEmpty());
for (size_t i = 0; i < animations.size(); ++i) {
int id = animations[i]->id();
if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animations[i].release())) {
// FIXME: We should know ahead of time whether these animations can be started.
for (size_t j = 0; j < startedAnimationIds.size(); ++j)
cancelAnimationOnCompositor(element, startedAnimationIds[j]);
startedAnimationIds.clear();
return false;
}
startedAnimationIds.append(id);
}
ASSERT(!startedAnimationIds.isEmpty());
return true;
}
void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id)
{
if (!canStartAnimationOnCompositor(element)) {
// When an element is being detached, we cancel any associated
// AnimationPlayers for CSS animations. But by the time we get
// here the mapping will have been removed.
// FIXME: Defer remove/pause operations until after the
// compositing update.
return;
}
toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id);
}
void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime)
{
// FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date.
// https://code.google.com/p/chromium/issues/detail?id=339847
DisableCompositingQueryAsserts disabler;
if (!canStartAnimationOnCompositor(element)) {
ASSERT_NOT_REACHED();
return;
}
toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime);
}
// -----------------------------------------------------------------------
// CompositorAnimationsImpl
// -----------------------------------------------------------------------
bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, double timeOffset, CompositorTiming& out)
{
timing.assertValid();
// All fill modes are supported (the calling code handles them).
// FIXME: Support non-zero iteration start.
if (timing.iterationStart)
return false;
if (timing.iterationCount <= 0)
return false;
if (std::isnan(timing.iterationDuration) || !timing.iterationDuration)
return false;
// FIXME: Support other playback rates
if (timing.playbackRate != 1)
return false;
// All directions are supported.
// Now attempt an actual conversion
out.scaledDuration = timing.iterationDuration;
ASSERT(out.scaledDuration > 0);
double scaledStartDelay = timing.startDelay;
if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.iterationCount)
return false;
out.reverse = (timing.direction == Timing::PlaybackDirectionReverse
|| timing.direction == Timing::PlaybackDirectionAlternateReverse);
out.alternate = (timing.direction == Timing::PlaybackDirectionAlternate
|| timing.direction == Timing::PlaybackDirectionAlternateReverse);
if (!std::isfinite(timing.iterationCount)) {
out.adjustedIterationCount = -1;
} else {
out.adjustedIterationCount = timing.iterationCount;
ASSERT(out.adjustedIterationCount > 0);
}
// Compositor's time offset is positive for seeking into the animation.
out.scaledTimeOffset = -scaledStartDelay + timeOffset;
return true;
}
namespace {
template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction)
{
if (!timingFunction) {
curve.add(keyframe);
return;
}
switch (timingFunction->type()) {
case TimingFunction::LinearFunction:
curve.add(keyframe, WebCompositorAnimationCurve::TimingFunctionTypeLinear);
return;
case TimingFunction::CubicBezierFunction: {
const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction);
if (cubic->subType() == CubicBezierTimingFunction::Custom) {
curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2());
} else {
WebCompositorAnimationCurve::TimingFunctionType easeType;
switch (cubic->subType()) {
case CubicBezierTimingFunction::Ease:
easeType = WebCompositorAnimationCurve::TimingFunctionTypeEase;
break;
case CubicBezierTimingFunction::EaseIn:
easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn;
break;
case CubicBezierTimingFunction::EaseOut:
easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseOut;
break;
case CubicBezierTimingFunction::EaseInOut:
easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut;
break;
// Custom Bezier are handled seperately.
case CubicBezierTimingFunction::Custom:
default:
ASSERT_NOT_REACHED();
return;
}
curve.add(keyframe, easeType);
}
return;
}
case TimingFunction::StepsFunction:
default:
ASSERT_NOT_REACHED();
return;
}
}
} // namespace anoymous
void CompositorAnimationsImpl::addKeyframesToCurve(WebCompositorAnimationCurve& curve, const PropertySpecificKeyframeVector& keyframes, const Timing& timing, bool reverse)
{
for (size_t i = 0; i < keyframes.size(); i++) {
RefPtr<TimingFunction> reversedTimingFunction;
const TimingFunction* keyframeTimingFunction = 0;
if (i < keyframes.size() - 1) { // Ignore timing function of last frame.
if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction) {
if (reverse) {
reversedTimingFunction = CompositorAnimationsTimingFunctionReverser::reverse(*timing.timingFunction.get());
keyframeTimingFunction = reversedTimingFunction.get();
} else {
keyframeTimingFunction = timing.timingFunction.get();
}
} else {
if (reverse) {
reversedTimingFunction = CompositorAnimationsTimingFunctionReverser::reverse(keyframes[i + 1]->easing());
keyframeTimingFunction = reversedTimingFunction.get();
} else {
keyframeTimingFunction = &keyframes[i]->easing();
}
}
}
// FIXME: This relies on StringKeyframes being eagerly evaluated, which will
// not happen eventually. Instead we should extract the CSSValue here
// and convert using another set of toAnimatableXXXOperations functions.
const AnimatableValue* value = keyframes[i]->getAnimatableValue().get();
switch (curve.type()) {
case WebCompositorAnimationCurve::AnimationCurveTypeFilter: {
OwnPtr<WebFilterOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
toWebFilterOperations(toAnimatableFilterOperations(value)->operations(), ops.get());
WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release());
WebFilterAnimationCurve* filterCurve = static_cast<WebFilterAnimationCurve*>(&curve);
addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction);
break;
}
case WebCompositorAnimationCurve::AnimationCurveTypeFloat: {
WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value)->toDouble());
WebFloatAnimationCurve* floatCurve = static_cast<WebFloatAnimationCurve*>(&curve);
addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction);
break;
}
case WebCompositorAnimationCurve::AnimationCurveTypeTransform: {
OwnPtr<WebTransformOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
toWebTransformOperations(toAnimatableTransform(value)->transformOperations(), ops.get());
WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release());
WebTransformAnimationCurve* transformCurve = static_cast<WebTransformAnimationCurve*>(&curve);
addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
}
void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, double startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector<OwnPtr<WebCompositorAnimation> >& animations)
{
ASSERT(animations.isEmpty());
CompositorTiming compositorTiming;
bool timingValid = convertTimingForCompositor(timing, timeOffset, compositorTiming);
ASSERT_UNUSED(timingValid, timingValid);
PropertySet properties = effect.properties();
ASSERT(!properties.isEmpty());
for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) {
PropertySpecificKeyframeVector values;
getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, compositorTiming.reverse, values);
WebCompositorAnimation::TargetProperty targetProperty;
OwnPtr<WebCompositorAnimationCurve> curve;
switch (*it) {
case CSSPropertyOpacity: {
targetProperty = WebCompositorAnimation::TargetPropertyOpacity;
WebFloatAnimationCurve* floatCurve = Platform::current()->compositorSupport()->createFloatAnimationCurve();
addKeyframesToCurve(*floatCurve, values, timing, compositorTiming.reverse);
curve = adoptPtr(floatCurve);
break;
}
case CSSPropertyWebkitFilter: {
targetProperty = WebCompositorAnimation::TargetPropertyFilter;
WebFilterAnimationCurve* filterCurve = Platform::current()->compositorSupport()->createFilterAnimationCurve();
addKeyframesToCurve(*filterCurve, values, timing, compositorTiming.reverse);
curve = adoptPtr(filterCurve);
break;
}
case CSSPropertyTransform: {
targetProperty = WebCompositorAnimation::TargetPropertyTransform;
WebTransformAnimationCurve* transformCurve = Platform::current()->compositorSupport()->createTransformAnimationCurve();
addKeyframesToCurve(*transformCurve, values, timing, compositorTiming.reverse);
curve = adoptPtr(transformCurve);
break;
}
default:
ASSERT_NOT_REACHED();
continue;
}
ASSERT(curve.get());
OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty));
if (!std::isnan(startTime))
animation->setStartTime(startTime);
animation->setIterations(compositorTiming.adjustedIterationCount);
animation->setTimeOffset(compositorTiming.scaledTimeOffset);
animation->setAlternatesDirection(compositorTiming.alternate);
animations.append(animation.release());
}
ASSERT(!animations.isEmpty());
}
} // namespace blink