Bug 1764126 - Part 5: Enable `linear(...)` timing function on C++ side, r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D148791
This commit is contained in:
David Shin 2022-06-11 00:36:25 +00:00
Родитель d3f7b3772d
Коммит 562eecc50f
5 изменённых файлов: 175 добавлений и 3 удалений

Просмотреть файл

@ -45,8 +45,9 @@ ComputedTimingFunction::Function ComputedTimingFunction::ConstructFunction(
return AsVariant( return AsVariant(
StepFunc{static_cast<uint32_t>(timing.steps._0), timing.steps._1}); StepFunc{static_cast<uint32_t>(timing.steps._0), timing.steps._1});
case StyleComputedTimingFunction::Tag::LinearFunction: { case StyleComputedTimingFunction::Tag::LinearFunction: {
// TODO(dshin): To be implemented (bug 1764126) StylePiecewiseLinearFunction result;
return ConstructFunction(nsTimingFunction{StyleTimingKeyword::Linear}); Servo_CreatePiecewiseLinearFunction(&timing.linear_function._0, &result);
return AsVariant(result);
} }
} }
MOZ_ASSERT_UNREACHABLE("Unknown timing function."); MOZ_ASSERT_UNREACHABLE("Unknown timing function.");
@ -175,6 +176,10 @@ double ComputedTimingFunction::GetValue(
}, },
[aPortion, aBeforeFlag](const StepFunc& aFunction) { [aPortion, aBeforeFlag](const StepFunc& aFunction) {
return StepTiming(aFunction, aPortion, aBeforeFlag); return StepTiming(aFunction, aPortion, aBeforeFlag);
},
[aPortion](const StylePiecewiseLinearFunction& aFunction) {
return static_cast<double>(Servo_PiecewiseLinearFunctionAt(
&aFunction, static_cast<float>(aPortion)));
}); });
} }
@ -194,6 +199,19 @@ void ComputedTimingFunction::AppendToString(nsACString& aResult) const {
[](const StepFunc& aFunction) { [](const StepFunc& aFunction) {
return StyleComputedTimingFunction::Steps( return StyleComputedTimingFunction::Steps(
static_cast<int>(aFunction.mSteps), aFunction.mPos); static_cast<int>(aFunction.mSteps), aFunction.mPos);
},
[](const StylePiecewiseLinearFunction& aFunction) {
// TODO(dshin, bug 1773493): Having to go back and forth isn't ideal.
Vector<StyleComputedLinearStop> stops;
bool reserved = stops.initCapacity(aFunction.entries.Length());
MOZ_RELEASE_ASSERT(reserved, "Failed to reserve memory");
for (const auto& e : aFunction.entries.AsSpan()) {
stops.infallibleAppend(StyleComputedLinearStop{
e.y, StyleOptional<StylePercentage>::Some(StylePercentage{e.x}),
StyleOptional<StylePercentage>::None()});
}
return StyleComputedTimingFunction::LinearFunction(
StyleOwnedSlice<StyleComputedLinearStop>{std::move(stops)});
})}; })};
Servo_SerializeEasing(&timing, &aResult); Servo_SerializeEasing(&timing, &aResult);
} }
@ -213,6 +231,18 @@ Maybe<ComputedTimingFunction> ComputedTimingFunction::FromLayersTimingFunction(
StyleStepPosition pos = static_cast<StyleStepPosition>(sf.type()); StyleStepPosition pos = static_cast<StyleStepPosition>(sf.type());
return Some(ComputedTimingFunction::Steps(sf.steps(), pos)); return Some(ComputedTimingFunction::Steps(sf.steps(), pos));
} }
case layers::TimingFunction::TLinearFunction: {
auto lf = aTimingFunction.get_LinearFunction();
Vector<StylePiecewiseLinearFunctionEntry> stops;
bool reserved = stops.initCapacity(lf.stops().Length());
MOZ_RELEASE_ASSERT(reserved, "Failed to reserve memory");
for (const auto& e : lf.stops()) {
stops.infallibleAppend(
StylePiecewiseLinearFunctionEntry{e.input(), e.output()});
}
StylePiecewiseLinearFunction result;
return Some(ComputedTimingFunction{result});
}
case layers::TimingFunction::T__None: case layers::TimingFunction::T__None:
break; break;
} }
@ -244,6 +274,13 @@ layers::TimingFunction ComputedTimingFunction::ToLayersTimingFunction(
return layers::TimingFunction{ return layers::TimingFunction{
layers::StepFunction{static_cast<int>(aFunction.mSteps), layers::StepFunction{static_cast<int>(aFunction.mSteps),
static_cast<uint8_t>(aFunction.mPos)}}; static_cast<uint8_t>(aFunction.mPos)}};
},
[](const StylePiecewiseLinearFunction& aFunction) {
nsTArray<layers::LinearStop> stops{aFunction.entries.Length()};
for (const auto& e : aFunction.entries.AsSpan()) {
stops.AppendElement(layers::LinearStop{e.x, e.y});
}
return layers::TimingFunction{layers::LinearFunction{stops}};
}); });
} }

Просмотреть файл

@ -81,6 +81,17 @@ class ComputedTimingFunction {
StyleComputedTimingFunction::Tag::Steps && StyleComputedTimingFunction::Tag::Steps &&
aFunction.mSteps == uint32_t(aOther.mTiming.steps._0) && aFunction.mSteps == uint32_t(aOther.mTiming.steps._0) &&
aFunction.mPos == aOther.mTiming.steps._1; aFunction.mPos == aOther.mTiming.steps._1;
},
[&aOther](const StylePiecewiseLinearFunction& aFunction) {
if (aOther.mTiming.tag !=
StyleComputedTimingFunction::Tag::LinearFunction) {
return false;
}
StylePiecewiseLinearFunction other;
// TODO(dshin, bug 1773493): Having to go back and forth isn't ideal.
Servo_CreatePiecewiseLinearFunction(
&aOther.mTiming.linear_function._0, &other);
return aFunction.entries == other.entries;
}); });
} }
bool operator!=(const nsTimingFunction& aOther) const { bool operator!=(const nsTimingFunction& aOther) const {
@ -105,13 +116,17 @@ class ComputedTimingFunction {
mozilla::StyleTimingKeyword mKeyword; mozilla::StyleTimingKeyword mKeyword;
SMILKeySpline mFunction; SMILKeySpline mFunction;
}; };
using Function = mozilla::Variant<KeywordFunction, SMILKeySpline, StepFunc>;
using Function = mozilla::Variant<KeywordFunction, SMILKeySpline, StepFunc,
StylePiecewiseLinearFunction>;
static Function ConstructFunction(const nsTimingFunction& aFunction); static Function ConstructFunction(const nsTimingFunction& aFunction);
ComputedTimingFunction(double x1, double y1, double x2, double y2) ComputedTimingFunction(double x1, double y1, double x2, double y2)
: mFunction{AsVariant(SMILKeySpline{x1, y1, x2, y2})} {} : mFunction{AsVariant(SMILKeySpline{x1, y1, x2, y2})} {}
ComputedTimingFunction(uint32_t aSteps, StyleStepPosition aPos) ComputedTimingFunction(uint32_t aSteps, StyleStepPosition aPos)
: mFunction{AsVariant(StepFunc{aSteps, aPos})} {} : mFunction{AsVariant(StepFunc{aSteps, aPos})} {}
explicit ComputedTimingFunction(StylePiecewiseLinearFunction aFunction)
: mFunction{AsVariant(std::move(aFunction))} {}
Function mFunction; Function mFunction;
}; };

Просмотреть файл

@ -101,10 +101,20 @@ struct StepFunction {
uint8_t type; // Converted from StyleStepPosition. uint8_t type; // Converted from StyleStepPosition.
}; };
struct LinearStop {
float input;
float output;
};
struct LinearFunction {
LinearStop[] stops;
};
union TimingFunction { union TimingFunction {
null_t; null_t;
CubicBezierFunction; CubicBezierFunction;
StepFunction; StepFunction;
LinearFunction;
}; };
struct LayerColor { DeviceColor value; }; struct LayerColor { DeviceColor value; };

Просмотреть файл

@ -0,0 +1 @@
prefs: [layout.css.linear-easing-function.enabled:true]

Просмотреть файл

@ -0,0 +1,109 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="assert" content="This test checks the output of linear timing functions" />
<title>Tests for the output of linear timing functions</title>
<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/6533">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/util.js"></script>
<script src="testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
function assert_style_left_at(animation, time, expected_y) {
animation.currentTime = time;
assert_approx_equals(pxToNum(getComputedStyle(animation.effect.target).left),
expected_y * 100,
0.01,
'The left of the animation should be approximately ' +
expected_y * 100 + ' at ' + time + 'ms');
}
function assert_animations_equal_at(actual_animation, expected_animation, time) {
actual_animation.currentTime = time;
var actual_left = pxToNum(getComputedStyle(actual_animation.effect.target).left);
expected_animation.currentTime = time;
var expected_left = pxToNum(getComputedStyle(expected_animation.effect.target).left);
assert_approx_equals(actual_left,
expected_left,
0.01,
'The left of the animation should be approximately ' +
expected_left + ' at ' + time + 'ms');
}
function create_animated_div(t, easing_function) {
var target = createDiv(t);
target.style.position = 'absolute';
return target.animate(
[ { left: '0px' },
{ left: '100px' } ],
{ duration: 1000,
fill: 'forwards',
easing: easing_function });
}
test(function(t) {
var anim = create_animated_div(t, 'linear(0, 1.5, 1)');
assert_style_left_at(anim, 0, 0.0);
assert_style_left_at(anim, 250, 0.75);
assert_style_left_at(anim, 500, 1.5);
assert_style_left_at(anim, 750, 1.25);
assert_style_left_at(anim, 1000, 1.00);
}, 'linear function easing with output greater than 1');
test(function(t) {
var anim = create_animated_div(t, 'linear(1, -0.5, 0)');
assert_style_left_at(anim, 0, 1.0);
assert_style_left_at(anim, 250, 0.25);
assert_style_left_at(anim, 500, -0.5);
assert_style_left_at(anim, 750, -0.25);
assert_style_left_at(anim, 1000, 0.00);
}, 'linear function easing with output less than 1');
test(function(t) {
var anim = create_animated_div(t, 'linear()');
var equiv = create_animated_div(t, 'linear');
assert_animations_equal_at(anim, equiv, 0);
assert_animations_equal_at(anim, equiv, 250);
assert_animations_equal_at(anim, equiv, 750);
assert_animations_equal_at(anim, equiv, 1000);
}, 'linear function easing, linear equivalent');
test(function(t) {
var anim = create_animated_div(t, 'linear(0.5)');
assert_style_left_at(anim, 0, 0.5);
assert_style_left_at(anim, 250, 0.5);
assert_style_left_at(anim, 500, 0.5);
assert_style_left_at(anim, 750, 0.5);
assert_style_left_at(anim, 1000, 0.5);
}, 'linear function easing, constant');
test(function(t) {
var anim = create_animated_div(t, 'linear(0.2 0% 20%, 0.4 20% 40%, 0.6 40% 60%, 0.8 60% 80%, 1.0 80% 100%)');
var equiv = create_animated_div(t, 'steps(5, jump-start)');
assert_animations_equal_at(anim, equiv, 0);
// Spec doesn't specify which way these steps should be continuous,
// so just check on each side.
assert_animations_equal_at(anim, equiv, 199);
assert_animations_equal_at(anim, equiv, 201);
assert_animations_equal_at(anim, equiv, 399);
assert_animations_equal_at(anim, equiv, 401);
assert_animations_equal_at(anim, equiv, 599);
assert_animations_equal_at(anim, equiv, 601);
assert_animations_equal_at(anim, equiv, 799);
assert_animations_equal_at(anim, equiv, 801);
assert_animations_equal_at(anim, equiv, 1000);
}, 'linear function easing, steps equivalent');
</script>
</body>