Commit cfa08298 authored by Stephen McGruer's avatar Stephen McGruer Committed by Commit Bot

Handle non-interpolable transform animations on cc/

Previously TransformOperations::Blend would silently fail if either of the
input matrices was non-decomposible, returning an empty TransformOperations
list. This would end up being converted to the identity matrix for animations
purposes, which would result in incorrect animations on the compositor.

This CL fixes the logic to treat the non-decomposible cases as discrete
animations; that is, if the progress is <= 0.5 we return the 'from' input,
else we return the 'to' input.

Bug: 821351
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel
Change-Id: I82d3400ac183798cc71a1871c1f08e792e653c7a
Reviewed-on: https://chromium-review.googlesource.com/966599
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#544117}
parent 34d1df4e
......@@ -5,6 +5,7 @@
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/animation/transform_operations.h"
#include "cc/test/geometry_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/animation/tween.h"
......@@ -304,6 +305,102 @@ TEST(KeyframedAnimationCurveTest, RepeatedTransformKeyTimes) {
ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f)));
}
// Tests that a discrete transform animation (e.g. where one or more keyframes
// is a non-invertible matrix) works as expected.
TEST(KeyframedAnimationCurveTest, DiscreteLinearTransformAnimation) {
gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0);
gfx::Transform identity_matrix;
std::unique_ptr<KeyframedTransformAnimationCurve> curve(
KeyframedTransformAnimationCurve::Create());
TransformOperations operations1;
operations1.AppendMatrix(non_invertible_matrix);
TransformOperations operations2;
operations2.AppendMatrix(identity_matrix);
TransformOperations operations3;
operations3.AppendMatrix(non_invertible_matrix);
curve->AddKeyframe(
TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr));
curve->AddKeyframe(TransformKeyframe::Create(
base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
curve->AddKeyframe(TransformKeyframe::Create(
base::TimeDelta::FromSecondsD(2.0), operations3, nullptr));
TransformOperations result;
// Between 0 and 0.5 seconds, the first keyframe should be returned.
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.49f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
// Between 0.5 and 1.5 seconds, the middle keyframe should be returned.
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.5f));
EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(1.49f));
EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply());
// Between 1.5 and 2.0 seconds, the last keyframe should be returned.
result = curve->GetValue(base::TimeDelta::FromSecondsD(1.5f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
}
TEST(KeyframedAnimationCurveTest, DiscreteCubicBezierTransformAnimation) {
gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0);
gfx::Transform identity_matrix;
std::unique_ptr<KeyframedTransformAnimationCurve> curve(
KeyframedTransformAnimationCurve::Create());
TransformOperations operations1;
operations1.AppendMatrix(non_invertible_matrix);
TransformOperations operations2;
operations2.AppendMatrix(identity_matrix);
TransformOperations operations3;
operations3.AppendMatrix(non_invertible_matrix);
// The cubic-bezier here is a nice fairly strong ease-in curve, where 50%
// progression is at approximately 85% of the time.
curve->AddKeyframe(TransformKeyframe::Create(
base::TimeDelta(), operations1,
CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f)));
curve->AddKeyframe(TransformKeyframe::Create(
base::TimeDelta::FromSecondsD(1.0), operations2,
CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f)));
curve->AddKeyframe(TransformKeyframe::Create(
base::TimeDelta::FromSecondsD(2.0), operations3,
CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f)));
TransformOperations result;
// Due to the cubic-bezier, the first keyframe is returned almost all the way
// to 1 second.
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.8f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
// Between ~0.85 and ~1.85 seconds, the middle keyframe should be returned.
result = curve->GetValue(base::TimeDelta::FromSecondsD(0.85f));
EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(1.8f));
EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply());
// Finally the last keyframe only takes effect after ~1.85 seconds.
result = curve->GetValue(base::TimeDelta::FromSecondsD(1.85f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f));
EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply());
}
// Tests that a filter animation with one keyframe works as expected.
TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) {
std::unique_ptr<KeyframedFilterAnimationCurve> curve(
......
......@@ -53,7 +53,11 @@ gfx::Transform TransformOperations::Apply() const {
TransformOperations TransformOperations::Blend(const TransformOperations& from,
SkMScalar progress) const {
TransformOperations to_return;
BlendInternal(from, progress, &to_return);
if (!BlendInternal(from, progress, &to_return)) {
// If the matrices cannot be blended, fallback to discrete animation logic.
// See https://drafts.csswg.org/css-transforms/#matrix-interpolation
to_return = progress < 0.5 ? from : *this;
}
return to_return;
}
......
......@@ -48,6 +48,9 @@ class CC_ANIMATION_EXPORT TransformOperations {
// transforms are baked to matrices (using apply), and the matrices are
// then decomposed and interpolated. For more information, see
// http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition.
//
// If either of the matrices are non-decomposable for the blend, Blend applies
// discrete interpolation between them based on the progress value.
TransformOperations Blend(const TransformOperations& from,
SkMScalar progress) const;
......
......@@ -857,6 +857,32 @@ TEST(TransformOperationTest, ExtrapolateMatrixBlending) {
operations1.Blend(operations2, -0.5).Apply());
}
TEST(TransformOperationTest, NonDecomposableBlend) {
TransformOperations non_decomposible_transform;
gfx::Transform non_decomposible_matrix(0, 0, 0, 0, 0, 0);
non_decomposible_transform.AppendMatrix(non_decomposible_matrix);
TransformOperations identity_transform;
gfx::Transform identity_matrix;
identity_transform.AppendMatrix(identity_matrix);
// Before the half-way point, we should return the 'from' matrix.
EXPECT_TRANSFORMATION_MATRIX_EQ(
non_decomposible_matrix,
identity_transform.Blend(non_decomposible_transform, 0.0f).Apply());
EXPECT_TRANSFORMATION_MATRIX_EQ(
non_decomposible_matrix,
identity_transform.Blend(non_decomposible_transform, 0.49f).Apply());
// After the half-way point, we should return the 'to' matrix.
EXPECT_TRANSFORMATION_MATRIX_EQ(
identity_matrix,
identity_transform.Blend(non_decomposible_transform, 0.5f).Apply());
EXPECT_TRANSFORMATION_MATRIX_EQ(
identity_matrix,
identity_transform.Blend(non_decomposible_transform, 1.0f).Apply());
}
TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) {
TransformOperations operations_from;
operations_from.AppendScale(2.0, 4.0, 8.0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment