Commit b4b6e3ec authored by Xida Chen's avatar Xida Chen Committed by Chromium LUCI CQ

Keep offset for composite background color animation

At this moment, our implementation only keeps the first and the
last keyframes for composite background color animation. This is
wrong, for example, we can having multiple keyframes like:
0%: { background-color: red }
10%: { background-color: green}
100% { background-color: blue}

This CL fixes the issue. The idea is to keep the offset which
we stored in CompositorKeyframeValues. In the above case, the
offsets would be [0, 0.1, 1].

Once we have that, here is how we do interpolation. Say that
the current progress is 0.4, then we know that it falls in to
the range of [0.1, 1], which means we need to interpolate from
green to blue. We need to adjust the progress (0.4) based on the
offsets. Basically scale [0.1, 1] to [0, 1], then the adjusted
progress should be (0.4-0.1) / (1-0.1). Layout tests are added
to ensure correctness.

Bug: 1153671
Change-Id: I6dffc82b5821fdf418f2b94331f567a239079730
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2597844
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: default avatarKevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841476}
parent 934d0161
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h" #include "third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h"
#include "third_party/blink/renderer/core/animation/animation_effect.h" #include "third_party/blink/renderer/core/animation/animation_effect.h"
#include "third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h"
#include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
#include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/animation/element_animations.h"
#include "third_party/blink/renderer/core/css/css_color_value.h" #include "third_party/blink/renderer/core/css/css_color_value.h"
...@@ -29,16 +30,20 @@ class BackgroundColorPaintWorkletInput : public PaintWorkletInput { ...@@ -29,16 +30,20 @@ class BackgroundColorPaintWorkletInput : public PaintWorkletInput {
const FloatSize& container_size, const FloatSize& container_size,
int worklet_id, int worklet_id,
const Vector<Color>& animated_colors, const Vector<Color>& animated_colors,
const Vector<double>& offsets,
cc::PaintWorkletInput::PropertyKeys property_keys) cc::PaintWorkletInput::PropertyKeys property_keys)
: PaintWorkletInput(container_size, worklet_id, std::move(property_keys)), : PaintWorkletInput(container_size, worklet_id, std::move(property_keys)),
animated_colors_(animated_colors) {} animated_colors_(animated_colors),
offsets_(offsets) {}
~BackgroundColorPaintWorkletInput() override = default; ~BackgroundColorPaintWorkletInput() override = default;
const Vector<Color>& AnimatedColors() const { return animated_colors_; } const Vector<Color>& AnimatedColors() const { return animated_colors_; }
const Vector<double>& Offsets() const { return offsets_; }
private: private:
Vector<Color> animated_colors_; Vector<Color> animated_colors_;
Vector<double> offsets_;
}; };
class BackgroundColorPaintWorkletProxyClient class BackgroundColorPaintWorkletProxyClient
...@@ -64,18 +69,39 @@ class BackgroundColorPaintWorkletProxyClient ...@@ -64,18 +69,39 @@ class BackgroundColorPaintWorkletProxyClient
static_cast<const BackgroundColorPaintWorkletInput*>(compositor_input); static_cast<const BackgroundColorPaintWorkletInput*>(compositor_input);
FloatSize container_size = input->ContainerSize(); FloatSize container_size = input->ContainerSize();
Vector<Color> animated_colors = input->AnimatedColors(); Vector<Color> animated_colors = input->AnimatedColors();
Vector<double> offsets = input->Offsets();
DCHECK_GT(animated_colors.size(), 1u); DCHECK_GT(animated_colors.size(), 1u);
DCHECK_EQ(animated_colors.size(), offsets.size());
DCHECK_EQ(animated_property_values.size(), 1u); DCHECK_EQ(animated_property_values.size(), 1u);
const auto& entry = animated_property_values.begin(); const auto& entry = animated_property_values.begin();
float progress = entry->second.float_value.value(); float progress = entry->second.float_value.value();
// Get the start and end color based on the progress and offsets.
DCHECK_EQ(offsets.front(), 0);
DCHECK_EQ(offsets.back(), 1);
unsigned result_index = -1;
for (unsigned i = 0; i < offsets.size() - 1; i++) {
if (progress <= offsets[i + 1]) {
result_index = i;
break;
}
}
DCHECK_GE(result_index, 0u);
// Because the progress is a global one, we need to adjust it with offsets.
float adjusted_progress =
(progress - offsets[result_index]) /
(offsets[result_index + 1] - offsets[result_index]);
std::unique_ptr<InterpolableValue> from = std::unique_ptr<InterpolableValue> from =
CSSColorInterpolationType::CreateInterpolableColor(animated_colors[0]); CSSColorInterpolationType::CreateInterpolableColor(
animated_colors[result_index]);
std::unique_ptr<InterpolableValue> to = std::unique_ptr<InterpolableValue> to =
CSSColorInterpolationType::CreateInterpolableColor(animated_colors[1]); CSSColorInterpolationType::CreateInterpolableColor(
animated_colors[result_index + 1]);
std::unique_ptr<InterpolableValue> result = std::unique_ptr<InterpolableValue> result =
CSSColorInterpolationType::CreateInterpolableColor(animated_colors[1]); CSSColorInterpolationType::CreateInterpolableColor(
from->Interpolate(*to, progress, *result); animated_colors[result_index + 1]);
from->Interpolate(*to, adjusted_progress, *result);
Color rgba = CSSColorInterpolationType::GetRGBA(*(result.get())); Color rgba = CSSColorInterpolationType::GetRGBA(*(result.get()));
SkColor current_color = static_cast<SkColor>(rgba); SkColor current_color = static_cast<SkColor>(rgba);
...@@ -88,6 +114,7 @@ class BackgroundColorPaintWorkletProxyClient ...@@ -88,6 +114,7 @@ class BackgroundColorPaintWorkletProxyClient
} }
}; };
// TODO(crbug.com/1163949): Support animation keyframes without 0% or 100%.
void GetColorsFromStringKeyframe(const PropertySpecificKeyframe* frame, void GetColorsFromStringKeyframe(const PropertySpecificKeyframe* frame,
Vector<Color>* animated_colors, Vector<Color>* animated_colors,
const Element* element) { const Element* element) {
...@@ -103,6 +130,13 @@ void GetColorsFromStringKeyframe(const PropertySpecificKeyframe* frame, ...@@ -103,6 +130,13 @@ void GetColorsFromStringKeyframe(const PropertySpecificKeyframe* frame,
animated_colors->push_back(color_value->Value()); animated_colors->push_back(color_value->Value());
} }
void GetCompositorKeyframeOffset(const PropertySpecificKeyframe* frame,
Vector<double>* offsets) {
const CompositorKeyframeDouble& value =
To<CompositorKeyframeDouble>(*(frame->GetCompositorKeyframeValue()));
offsets->push_back(value.ToDouble());
}
void GetColorsFromTransitionKeyframe(const PropertySpecificKeyframe* frame, void GetColorsFromTransitionKeyframe(const PropertySpecificKeyframe* frame,
Vector<Color>* animated_colors, Vector<Color>* animated_colors,
const Element* element) { const Element* element) {
...@@ -140,6 +174,7 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint( ...@@ -140,6 +174,7 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint(
const Node* node) { const Node* node) {
DCHECK(node->IsElementNode()); DCHECK(node->IsElementNode());
Vector<Color> animated_colors; Vector<Color> animated_colors;
Vector<double> offsets;
const Element* element = static_cast<const Element*>(node); const Element* element = static_cast<const Element*>(node);
ElementAnimations* element_animations = element->GetElementAnimations(); ElementAnimations* element_animations = element->GetElementAnimations();
// TODO(crbug.com/1153672): implement main-thread fall back logic for // TODO(crbug.com/1153672): implement main-thread fall back logic for
...@@ -154,16 +189,12 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint( ...@@ -154,16 +189,12 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint(
model->GetPropertySpecificKeyframes( model->GetPropertySpecificKeyframes(
PropertyHandle(GetCSSPropertyBackgroundColor())); PropertyHandle(GetCSSPropertyBackgroundColor()));
DCHECK_GE(frames->size(), 2u); DCHECK_GE(frames->size(), 2u);
// TODO(crbug.com/1153671): right now we keep the first and last keyframe for (const auto& frame : *frames) {
// values only, we need to keep all keyframe values. if (model->IsStringKeyframeEffectModel())
if (model->IsStringKeyframeEffectModel()) { GetColorsFromStringKeyframe(frame, &animated_colors, element);
GetColorsFromStringKeyframe(frames->front(), &animated_colors, element); else
GetColorsFromStringKeyframe(frames->back(), &animated_colors, element); GetColorsFromTransitionKeyframe(frame, &animated_colors, element);
} else { GetCompositorKeyframeOffset(frame, &offsets);
GetColorsFromTransitionKeyframe(frames->front(), &animated_colors,
element);
GetColorsFromTransitionKeyframe(frames->back(), &animated_colors,
element);
} }
} }
...@@ -178,7 +209,7 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint( ...@@ -178,7 +209,7 @@ scoped_refptr<Image> BackgroundColorPaintWorklet::Paint(
element_id); element_id);
scoped_refptr<BackgroundColorPaintWorkletInput> input = scoped_refptr<BackgroundColorPaintWorkletInput> input =
base::MakeRefCounted<BackgroundColorPaintWorkletInput>( base::MakeRefCounted<BackgroundColorPaintWorkletInput>(
container_size, worklet_id_, animated_colors, container_size, worklet_id_, animated_colors, offsets,
std::move(input_property_keys)); std::move(input_property_keys));
return PaintWorkletDeferredImage::Create(std::move(input), container_size); return PaintWorkletDeferredImage::Create(std::move(input), container_size);
} }
......
...@@ -64,6 +64,9 @@ crbug.com/1123189 virtual/threaded-composited-iframes/external/wpt/is-input-pend ...@@ -64,6 +64,9 @@ crbug.com/1123189 virtual/threaded-composited-iframes/external/wpt/is-input-pend
crbug.com/1124979 compositing/video/video-controls-layer-creation.html [ Pass Failure ] crbug.com/1124979 compositing/video/video-controls-layer-creation.html [ Pass Failure ]
crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-animation.html [ Crash ] crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-animation.html [ Crash ]
crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html [ Crash ]
crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html [ Crash ]
crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html [ Crash ]
crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-transition.html [ Crash ] crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-transition.html [ Crash ]
crbug.com/1157199 external/wpt/css/css-paint-api/column-count-crash.https.html [ Crash ] crbug.com/1157199 external/wpt/css/css-paint-api/column-count-crash.https.html [ Crash ]
......
<!DOCTYPE html>
<html class="reftest-wait">
<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
<link rel="match" href="one-element-animation-ref.html">
<style>
.container {
width: 100px;
height: 100px;
background-color: green;
/* Use a long animation that start at 5% progress where the slope of the
selected timing function is zero. By setting up the animation in this way,
we accommodate lengthy delays in running the test without a potential drift
in the animated property value. This is important for avoiding flakes,
especially on debug builds. The screenshots are taken as soon as the
animation is ready, thus the long animation duration has no bearing on
the actual duration of the test. */
animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -50000s;
}
@keyframes bgcolor {
0% { background-color: rgb(0, 200, 0); }
10% {
background-color: rgb(200, 0, 0);
animation-timing-function: cubic-bezier(0,1,1,0);
}
100% {
background-color: rgb(0, 0, 200);
animation-timing-function: cubic-bezier(0,1,1,0);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<body>
<div class="container"></div>
<script>
// This test and the "one-element-three-keyframes-animation2.html" ensure that
// we select the correct start and end keyframes for interpolation. In this
// test, the start delay of the animation makes it jump to 5% right away, and in
// the "one-element-three-keyframes-animation2.html" the start delay makes it
// jump to 50%. So for this test, we would choose the keyframes at 0% and 10%
// for interpolation, where for the other test it would be 10% and 100%.
document.getAnimations()[0].ready.then(() => {
takeScreenshot();
});
</script>
</body>
</html>
<!DOCTYPE html>
<body>
<canvas id="canvas" width="100" height="100"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(100, 0, 100)';
ctx.fillRect(0, 0, 100, 100);
</script>
<!DOCTYPE html>
<html class="reftest-wait">
<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
<link rel="match" href="one-element-three-keyframes-animation2-ref.html">
<style>
.container {
width: 100px;
height: 100px;
background-color: green;
/* Use a long animation that start at 50% progress where the slope of the
selected timing function is zero. By setting up the animation in this way,
we accommodate lengthy delays in running the test without a potential drift
in the animated property value. This is important for avoiding flakes,
especially on debug builds. The screenshots are taken as soon as the
animation is ready, thus the long animation duration has no bearing on
the actual duration of the test. */
animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -500000s;
}
@keyframes bgcolor {
0% { background-color: rgb(0, 200, 0); }
10% {
background-color: rgb(200, 0, 0);
animation-timing-function: cubic-bezier(0,1,1,0);
}
100% {
background-color: rgb(0, 0, 200);
animation-timing-function: cubic-bezier(0,1,1,0);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<body>
<div class="container"></div>
<script>
// The start delay of the animation makes it jump 50% of the animation, which
// means we would select the keyframes at 10% and 100% for animation. The
// progress would be (0.5-0.1) / (1-0.1) = 0.44. So a timing function input of
// 0.44 results in an output of 0.5.
document.getAnimations()[0].ready.then(() => {
takeScreenshot();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html class="reftest-wait">
<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
<link rel="match" href="one-element-animation-ref.html">
<style>
.container {
width: 100px;
height: 100px;
background-color: green;
/* Use a long animation that start at 5% progress where the slope of the
selected timing function is zero. By setting up the animation in this way,
we accommodate lengthy delays in running the test without a potential drift
in the animated property value. This is important for avoiding flakes,
especially on debug builds. The screenshots are taken as soon as the
animation is ready, thus the long animation duration has no bearing on
the actual duration of the test. */
animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -50000s;
}
@keyframes bgcolor {
10% {
background-color: rgb(200, 0, 0);
animation-timing-function: cubic-bezier(0,1,1,0);
}
0% { background-color: rgb(0, 200, 0); }
100% {
background-color: rgb(0, 0, 200);
animation-timing-function: cubic-bezier(0,1,1,0);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<body>
<div class="container"></div>
<script>
document.getAnimations()[0].ready.then(() => {
takeScreenshot();
});
</script>
</body>
</html>
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