ladybird/Libraries/LibWeb/CSS/StyleValues/EasingStyleValue.cpp
Callum Law 06a57a280d LibWeb: Clamp calculated cubic-bezier() X coords using normal system
Previously we were doing this ad-hoc later in the process but we now
have the `calc` clamping system which can simplify things
2025-10-20 11:27:44 +01:00

189 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "EasingStyleValue.h"
#include <AK/BinarySearch.h>
#include <AK/StringBuilder.h>
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
namespace Web::CSS {
// https://drafts.csswg.org/css-easing-1/#valdef-easing-function-linear
EasingStyleValue::Linear EasingStyleValue::Linear::identity()
{
static Linear linear { { { NumberStyleValue::create(0), {} }, { NumberStyleValue::create(1), {} } } };
return linear;
}
// NOTE: Magic cubic bezier values from https://www.w3.org/TR/css-easing-1/#valdef-cubic-bezier-easing-function-ease
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease()
{
static CubicBezier bezier { 0.25, 0.1, 0.25, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in()
{
static CubicBezier bezier { 0.42, 0.0, 1.0, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_out()
{
static CubicBezier bezier { 0.0, 0.0, 0.58, 1.0 };
return bezier;
}
EasingStyleValue::CubicBezier EasingStyleValue::CubicBezier::ease_in_out()
{
static CubicBezier bezier { 0.42, 0.0, 0.58, 1.0 };
return bezier;
}
EasingStyleValue::Steps EasingStyleValue::Steps::step_start()
{
static Steps steps { 1, StepPosition::Start };
return steps;
}
EasingStyleValue::Steps EasingStyleValue::Steps::step_end()
{
static Steps steps { 1, StepPosition::End };
return steps;
}
// https://drafts.csswg.org/css-easing/#linear-easing-function-serializing
String EasingStyleValue::Linear::to_string(SerializationMode mode) const
{
// The linear keyword is serialized as itself.
if (*this == identity())
return "linear"_string;
// To serialize a linear() function:
// 1. Let s be the string "linear(".
StringBuilder builder;
builder.append("linear("sv);
// 2. Serialize each control point of the function,
// concatenate the results using the separator ", ",
// and append the result to s.
bool first = true;
for (auto stop : stops) {
if (first) {
first = false;
} else {
builder.append(", "sv);
}
// To serialize a linear() control point:
// 1. Let s be the serialization, as a <number>, of the control points output progress value.
builder.appendff(stop.output->to_string(mode));
// 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 points input progress value as a <percentage> and append it to s.
if (stop.input) {
builder.appendff(" {}", stop.input->to_string(mode));
}
// 4. Return s.
}
// 4. Append ")" to s, and return it.
builder.append(')');
return MUST(builder.to_string());
}
// https://drafts.csswg.org/css-easing/#bezier-serialization
String EasingStyleValue::CubicBezier::to_string(SerializationMode mode) const
{
StringBuilder builder;
if (*this == CubicBezier::ease()) {
builder.append("ease"sv);
} else if (*this == CubicBezier::ease_in()) {
builder.append("ease-in"sv);
} else if (*this == CubicBezier::ease_out()) {
builder.append("ease-out"sv);
} else if (*this == CubicBezier::ease_in_out()) {
builder.append("ease-in-out"sv);
} else {
builder.appendff("cubic-bezier({}, {}, {}, {})", x1.to_string(mode), y1.to_string(mode), x2.to_string(mode), y2.to_string(mode));
}
return MUST(builder.to_string());
}
// https://drafts.csswg.org/css-easing/#steps-serialization
String EasingStyleValue::Steps::to_string(SerializationMode mode) const
{
StringBuilder builder;
// Unlike the other easing function keywords, step-start and step-end do not serialize as themselves.
// Instead, they serialize as "steps(1, start)" and "steps(1)", respectively.
if (*this == Steps::step_start()) {
builder.append("steps(1, start)"sv);
} else if (*this == Steps::step_end()) {
builder.append("steps(1)"sv);
} else {
auto position = [&] -> Optional<StringView> {
if (first_is_one_of(this->position, StepPosition::JumpEnd, StepPosition::End))
return {};
return CSS::to_string(this->position);
}();
auto intervals = number_of_intervals;
if (mode == SerializationMode::ResolvedValue) {
auto resolved_value = number_of_intervals.resolved({}).value_or(1);
intervals = max(resolved_value, this->position == StepPosition::JumpNone ? 2 : 1);
}
if (position.has_value()) {
builder.appendff("steps({}, {})", intervals.to_string(mode), position.value());
} else {
builder.appendff("steps({})", intervals.to_string(mode));
}
}
return MUST(builder.to_string());
}
String EasingStyleValue::Function::to_string(SerializationMode mode) const
{
return visit(
[&](auto const& curve) {
return curve.to_string(mode);
});
}
ValueComparingNonnullRefPtr<StyleValue const> EasingStyleValue::absolutized(ComputationContext const& computation_context) const
{
auto const& absolutized_function = m_function.visit(
[&](Linear const& linear) -> Function {
Vector<Linear::Stop> absolutized_stops;
for (auto stop : linear.stops) {
RefPtr<StyleValue const> absolutized_input;
if (stop.input)
absolutized_input = stop.input->absolutized(computation_context);
absolutized_stops.append({ stop.output->absolutized(computation_context), absolutized_input });
}
return Linear { absolutized_stops };
},
[&](CubicBezier const& cubic_bezier) -> Function {
return cubic_bezier;
},
[&](Steps const& steps) -> Function {
return steps;
});
return EasingStyleValue::create(absolutized_function);
}
}