mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb: Don't canonicalize linear easing function points until use time
Canonicalization can require information that is only known after compute time (i.e. resolved relative lengths within calcs). This also allows us to get rid of the `had_explicit_input` flag and just rely on whether Optional has a value
This commit is contained in:
parent
95e26819d9
commit
91925db9ca
|
|
@ -210,6 +210,61 @@ double StepsEasingFunction::evaluate_at(double input_progress, bool before_flag)
|
|||
return current_step / jumps;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#linear-canonicalization
|
||||
static Vector<LinearEasingFunction::ControlPoint> canonicalize_linear_easing_function_control_points(Vector<LinearEasingFunction::ControlPoint> control_points)
|
||||
{
|
||||
// To canonicalize a linear() function’s control points, perform the following:
|
||||
Vector<LinearEasingFunction::ControlPoint> canonicalized_control_points = control_points;
|
||||
|
||||
// 1. If the first control point lacks an input progress value, set its input progress value to 0.
|
||||
if (!canonicalized_control_points.first().input.has_value())
|
||||
canonicalized_control_points.first().input = 0;
|
||||
|
||||
// 2. If the last control point lacks an input progress value, set its input progress value to 1.
|
||||
if (!canonicalized_control_points.last().input.has_value())
|
||||
canonicalized_control_points.last().input = 1;
|
||||
|
||||
// 3. If any control point has an input progress value that is less than
|
||||
// the input progress value of any preceding control point,
|
||||
// set its input progress value to the largest input progress value of any preceding control point.
|
||||
double largest_input = 0;
|
||||
for (auto& control_point : canonicalized_control_points) {
|
||||
if (control_point.input.has_value()) {
|
||||
if (control_point.input.value() < largest_input) {
|
||||
control_point.input = largest_input;
|
||||
} else {
|
||||
largest_input = control_point.input.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If any control point still lacks an input progress value,
|
||||
// then for each contiguous run of such control points,
|
||||
// set their input progress values so that they are evenly spaced
|
||||
// between the preceding and following control points with input progress values.
|
||||
Optional<size_t> run_start_idx;
|
||||
for (size_t idx = 0; idx < canonicalized_control_points.size(); idx++) {
|
||||
auto& control_point = canonicalized_control_points[idx];
|
||||
if (control_point.input.has_value() && run_start_idx.has_value()) {
|
||||
// Note: this stop is immediately after a run
|
||||
// set inputs of [start, idx-1] stops to be evenly spaced between start-1 and idx
|
||||
auto start_input = canonicalized_control_points[run_start_idx.value() - 1].input.value();
|
||||
auto end_input = canonicalized_control_points[idx].input.value();
|
||||
auto run_stop_count = idx - run_start_idx.value() + 1;
|
||||
auto delta = (end_input - start_input) / run_stop_count;
|
||||
for (size_t run_idx = 0; run_idx < run_stop_count; run_idx++) {
|
||||
canonicalized_control_points[run_idx + run_start_idx.value() - 1].input = start_input + delta * run_idx;
|
||||
}
|
||||
run_start_idx = {};
|
||||
} else if (!control_point.input.has_value() && !run_start_idx.has_value()) {
|
||||
// Note: this stop is the start of a run
|
||||
run_start_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
return canonicalized_control_points;
|
||||
}
|
||||
|
||||
EasingFunction EasingFunction::from_style_value(StyleValue const& style_value)
|
||||
{
|
||||
if (style_value.is_easing()) {
|
||||
|
|
@ -220,6 +275,11 @@ EasingFunction EasingFunction::from_style_value(StyleValue const& style_value)
|
|||
for (auto const& control_point : linear.stops)
|
||||
resolved_control_points.append({ control_point.input, control_point.output });
|
||||
|
||||
// https://drafts.csswg.org/css-easing-2/#funcdef-linear
|
||||
// If an argument lacks a <percentage>, its input progress value is initially empty. This is corrected
|
||||
// at used value time by linear() canonicalization.
|
||||
resolved_control_points = canonicalize_linear_easing_function_control_points(resolved_control_points);
|
||||
|
||||
return LinearEasingFunction { resolved_control_points, linear.to_string(SerializationMode::ResolvedValue) };
|
||||
},
|
||||
[](EasingStyleValue::CubicBezier const& cubic_bezier) -> EasingFunction {
|
||||
|
|
|
|||
|
|
@ -2931,9 +2931,9 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
|
|||
if (argument_tokens.has_next_token() || !output.has_value())
|
||||
return nullptr;
|
||||
|
||||
stops.append({ output.value(), first_input, first_input.has_value() });
|
||||
stops.append({ output.value(), first_input });
|
||||
if (second_input.has_value())
|
||||
stops.append({ output.value(), second_input, true });
|
||||
stops.append({ output.value(), second_input });
|
||||
}
|
||||
|
||||
if (stops.is_empty())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Web::CSS {
|
|||
// https://drafts.csswg.org/css-easing-1/#valdef-easing-function-linear
|
||||
EasingStyleValue::Linear EasingStyleValue::Linear::identity()
|
||||
{
|
||||
static Linear linear { { { 0, {}, false }, { 1, {}, false } } };
|
||||
static Linear linear { { { 0, {} }, { 1, {} } } };
|
||||
return linear;
|
||||
}
|
||||
|
||||
|
|
@ -60,60 +60,6 @@ EasingStyleValue::Steps EasingStyleValue::Steps::step_end()
|
|||
return steps;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#linear-canonicalization
|
||||
EasingStyleValue::Linear::Linear(Vector<EasingStyleValue::Linear::Stop> stops)
|
||||
{
|
||||
// To canonicalize a linear() function’s control points, perform the following:
|
||||
|
||||
// 1. If the first control point lacks an input progress value, set its input progress value to 0.
|
||||
if (!stops.first().input.has_value())
|
||||
stops.first().input = 0;
|
||||
|
||||
// 2. If the last control point lacks an input progress value, set its input progress value to 1.
|
||||
if (!stops.last().input.has_value())
|
||||
stops.last().input = 1;
|
||||
|
||||
// 3. If any control point has an input progress value that is less than
|
||||
// the input progress value of any preceding control point,
|
||||
// set its input progress value to the largest input progress value of any preceding control point.
|
||||
double largest_input = 0;
|
||||
for (auto& stop : stops) {
|
||||
if (stop.input.has_value()) {
|
||||
if (stop.input.value() < largest_input) {
|
||||
stop.input = largest_input;
|
||||
} else {
|
||||
largest_input = stop.input.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If any control point still lacks an input progress value,
|
||||
// then for each contiguous run of such control points,
|
||||
// set their input progress values so that they are evenly spaced
|
||||
// between the preceding and following control points with input progress values.
|
||||
Optional<size_t> run_start_idx;
|
||||
for (size_t idx = 0; idx < stops.size(); idx++) {
|
||||
auto& stop = stops[idx];
|
||||
if (stop.input.has_value() && run_start_idx.has_value()) {
|
||||
// Note: this stop is immediately after a run
|
||||
// set inputs of [start, idx-1] stops to be evenly spaced between start-1 and idx
|
||||
auto start_input = stops[run_start_idx.value() - 1].input.value();
|
||||
auto end_input = stops[idx].input.value();
|
||||
auto run_stop_count = idx - run_start_idx.value() + 1;
|
||||
auto delta = (end_input - start_input) / run_stop_count;
|
||||
for (size_t run_idx = 0; run_idx < run_stop_count; run_idx++) {
|
||||
stops[run_idx + run_start_idx.value() - 1].input = start_input + delta * run_idx;
|
||||
}
|
||||
run_start_idx = {};
|
||||
} else if (!stop.input.has_value() && !run_start_idx.has_value()) {
|
||||
// Note: this stop is the start of a run
|
||||
run_start_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
this->stops = move(stops);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#linear-easing-function-serializing
|
||||
String EasingStyleValue::Linear::to_string(SerializationMode) const
|
||||
{
|
||||
|
|
@ -144,7 +90,7 @@ String EasingStyleValue::Linear::to_string(SerializationMode) const
|
|||
// 2. If the control point originally lacked an input progress value, return s.
|
||||
// 3. Otherwise, append " " (U+0020 SPACE) to s,
|
||||
// then serialize the control point’s input progress value as a <percentage> and append it to s.
|
||||
if (stop.had_explicit_input) {
|
||||
if (stop.input.has_value()) {
|
||||
builder.appendff(" {}%", stop.input.value() * 100);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ public:
|
|||
double output;
|
||||
Optional<double> input;
|
||||
|
||||
// "NOTE: Serialization relies on whether or not an input progress value was originally supplied,
|
||||
// so that information should be retained in the internal representation."
|
||||
bool had_explicit_input;
|
||||
|
||||
bool operator==(Stop const&) const = default;
|
||||
};
|
||||
|
||||
|
|
@ -37,8 +33,6 @@ public:
|
|||
bool operator==(Linear const&) const = default;
|
||||
|
||||
String to_string(SerializationMode) const;
|
||||
|
||||
Linear(Vector<Stop> stops);
|
||||
};
|
||||
|
||||
struct CubicBezier {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ Harness status: OK
|
|||
|
||||
Found 35 tests
|
||||
|
||||
12 Pass
|
||||
23 Fail
|
||||
13 Pass
|
||||
22 Fail
|
||||
Pass e.style['animation-timing-function'] = "linear(0 0%, 1 100%)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "linear( 0 0%, 1 100% )" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear(0, 1)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "linear(-10, -5, 0, 5, 10)" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear(-10 -10%, -5 -5%, 0, 5, 10)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "linear(-10 -10%, -5 -5%, 0, 5, 10)" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear(0 calc(0%), 0 calc(100%))" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear(0 calc(50% - 50%), 0 calc(50% + 50%))" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear(0 calc(50%), 0 100%)" should set the property value
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user