diff --git a/Libraries/LibWeb/CSS/Interpolation.cpp b/Libraries/LibWeb/CSS/Interpolation.cpp index 694bed9fda9..d00171ed6b5 100644 --- a/Libraries/LibWeb/CSS/Interpolation.cpp +++ b/Libraries/LibWeb/CSS/Interpolation.cpp @@ -1024,123 +1024,128 @@ RefPtr interpolate_box_shadow(DOM::Element& element, Calculati return StyleValueList::create(move(result_shadows), StyleValueList::Separator::Comma); } +static RefPtr interpolate_mixed_value(CalculationContext const& calculation_context, StyleValue const& from, StyleValue const& to, float delta) +{ + auto get_value_type_of_numeric_style_value = [&calculation_context](StyleValue const& value) -> Optional { + switch (value.type()) { + case StyleValue::Type::Angle: + return ValueType::Angle; + case StyleValue::Type::Frequency: + return ValueType::Frequency; + case StyleValue::Type::Integer: + return ValueType::Integer; + case StyleValue::Type::Length: + return ValueType::Length; + case StyleValue::Type::Number: + return ValueType::Number; + case StyleValue::Type::Percentage: + return calculation_context.percentages_resolve_as.value_or(ValueType::Percentage); + case StyleValue::Type::Resolution: + return ValueType::Resolution; + case StyleValue::Type::Time: + return ValueType::Time; + case StyleValue::Type::Calculated: { + auto const& calculated = value.as_calculated(); + if (calculated.resolves_to_angle_percentage()) + return ValueType::Angle; + if (calculated.resolves_to_frequency_percentage()) + return ValueType::Frequency; + if (calculated.resolves_to_length_percentage()) + return ValueType::Length; + if (calculated.resolves_to_resolution()) + return ValueType::Resolution; + if (calculated.resolves_to_number()) + return calculation_context.resolve_numbers_as_integers ? ValueType::Integer : ValueType::Number; + if (calculated.resolves_to_percentage()) + return calculation_context.percentages_resolve_as.value_or(ValueType::Percentage); + if (calculated.resolves_to_time_percentage()) + return ValueType::Time; + + return {}; + } + default: + return {}; + } + }; + + auto from_value_type = get_value_type_of_numeric_style_value(from); + auto to_value_type = get_value_type_of_numeric_style_value(to); + + if (from_value_type.has_value() && from_value_type == to_value_type) { + auto to_calculation_node = [&calculation_context](StyleValue const& value) -> NonnullRefPtr { + switch (value.type()) { + case StyleValue::Type::Angle: + return NumericCalculationNode::create(value.as_angle().angle(), calculation_context); + case StyleValue::Type::Frequency: + return NumericCalculationNode::create(value.as_frequency().frequency(), calculation_context); + case StyleValue::Type::Integer: + // https://drafts.csswg.org/css-values-4/#combine-integers + // Interpolation of is defined as Vresult = round((1 - p) × VA + p × VB); that is, + // interpolation happens in the real number space as for s, and the result is converted to an + // by rounding to the nearest integer. + return NumericCalculationNode::create(Number { Number::Type::Number, static_cast(value.as_integer().integer()) }, calculation_context); + case StyleValue::Type::Length: + return NumericCalculationNode::create(value.as_length().length(), calculation_context); + case StyleValue::Type::Number: + return NumericCalculationNode::create(Number { Number::Type::Number, value.as_number().number() }, calculation_context); + case StyleValue::Type::Percentage: + return NumericCalculationNode::create(value.as_percentage().percentage(), calculation_context); + case StyleValue::Type::Time: + return NumericCalculationNode::create(value.as_time().time(), calculation_context); + case StyleValue::Type::Calculated: + return value.as_calculated().calculation(); + default: + VERIFY_NOT_REACHED(); + } + }; + + // https://drafts.csswg.org/css-values-4/#combine-mixed + // The computed value of a percentage-dimension mix is defined as + // FIXME: a computed dimension if the percentage component is zero or is defined specifically to compute to a dimension value + // a computed percentage if the dimension component is zero + // a computed calc() expression otherwise + if (auto const* from_dimension_value = as_if(from); from_dimension_value && to.type() == StyleValue::Type::Percentage) { + auto dimension_component = from_dimension_value->raw_value() * (1.f - delta); + auto percentage_component = to.as_percentage().raw_value() * delta; + if (dimension_component == 0.f) + return PercentageStyleValue::create(Percentage { percentage_component }); + } else if (auto const* to_dimension_value = as_if(to); to_dimension_value && from.type() == StyleValue::Type::Percentage) { + auto dimension_component = to_dimension_value->raw_value() * delta; + auto percentage_component = from.as_percentage().raw_value() * (1.f - delta); + if (dimension_component == 0) + return PercentageStyleValue::create(Percentage { percentage_component }); + } + + auto from_node = to_calculation_node(from); + auto to_node = to_calculation_node(to); + + // https://drafts.csswg.org/css-values-4/#combine-math + // Interpolation of math functions, with each other or with numeric values and other numeric-valued functions, is defined as Vresult = calc((1 - p) * VA + p * VB). + auto from_contribution = ProductCalculationNode::create({ + from_node, + NumericCalculationNode::create(Number { Number::Type::Number, 1.f - delta }, calculation_context), + }); + + auto to_contribution = ProductCalculationNode::create({ + to_node, + NumericCalculationNode::create(Number { Number::Type::Number, delta }, calculation_context), + }); + + return CalculatedStyleValue::create( + simplify_a_calculation_tree(SumCalculationNode::create({ from_contribution, to_contribution }), calculation_context, {}), + *from_node->numeric_type()->added_to(*to_node->numeric_type()), + calculation_context); + } + + return {}; +} + static RefPtr interpolate_value_impl(DOM::Element& element, CalculationContext const& calculation_context, StyleValue const& from, StyleValue const& to, float delta, AllowDiscrete allow_discrete) { if (from.type() != to.type() || from.is_calculated() || to.is_calculated()) { // Handle mixed percentage and dimension types, as well as CalculatedStyleValues // https://www.w3.org/TR/css-values-4/#mixed-percentages - auto get_value_type_of_numeric_style_value = [&calculation_context](StyleValue const& value) -> Optional { - switch (value.type()) { - case StyleValue::Type::Angle: - return ValueType::Angle; - case StyleValue::Type::Frequency: - return ValueType::Frequency; - case StyleValue::Type::Integer: - return ValueType::Integer; - case StyleValue::Type::Length: - return ValueType::Length; - case StyleValue::Type::Number: - return ValueType::Number; - case StyleValue::Type::Percentage: - return calculation_context.percentages_resolve_as.value_or(ValueType::Percentage); - case StyleValue::Type::Resolution: - return ValueType::Resolution; - case StyleValue::Type::Time: - return ValueType::Time; - case StyleValue::Type::Calculated: { - auto const& calculated = value.as_calculated(); - if (calculated.resolves_to_angle_percentage()) - return ValueType::Angle; - if (calculated.resolves_to_frequency_percentage()) - return ValueType::Frequency; - if (calculated.resolves_to_length_percentage()) - return ValueType::Length; - if (calculated.resolves_to_resolution()) - return ValueType::Resolution; - if (calculated.resolves_to_number()) - return calculation_context.resolve_numbers_as_integers ? ValueType::Integer : ValueType::Number; - if (calculated.resolves_to_percentage()) - return calculation_context.percentages_resolve_as.value_or(ValueType::Percentage); - if (calculated.resolves_to_time_percentage()) - return ValueType::Time; - - return {}; - } - default: - return {}; - } - }; - - auto from_value_type = get_value_type_of_numeric_style_value(from); - auto to_value_type = get_value_type_of_numeric_style_value(to); - - if (from_value_type.has_value() && from_value_type == to_value_type) { - auto to_calculation_node = [&calculation_context](StyleValue const& value) -> NonnullRefPtr { - switch (value.type()) { - case StyleValue::Type::Angle: - return NumericCalculationNode::create(value.as_angle().angle(), calculation_context); - case StyleValue::Type::Frequency: - return NumericCalculationNode::create(value.as_frequency().frequency(), calculation_context); - case StyleValue::Type::Integer: - // https://drafts.csswg.org/css-values-4/#combine-integers - // Interpolation of is defined as Vresult = round((1 - p) × VA + p × VB); that is, - // interpolation happens in the real number space as for s, and the result is converted to an - // by rounding to the nearest integer. - return NumericCalculationNode::create(Number { Number::Type::Number, static_cast(value.as_integer().integer()) }, calculation_context); - case StyleValue::Type::Length: - return NumericCalculationNode::create(value.as_length().length(), calculation_context); - case StyleValue::Type::Number: - return NumericCalculationNode::create(Number { Number::Type::Number, value.as_number().number() }, calculation_context); - case StyleValue::Type::Percentage: - return NumericCalculationNode::create(value.as_percentage().percentage(), calculation_context); - case StyleValue::Type::Time: - return NumericCalculationNode::create(value.as_time().time(), calculation_context); - case StyleValue::Type::Calculated: - return value.as_calculated().calculation(); - default: - VERIFY_NOT_REACHED(); - } - }; - - // https://drafts.csswg.org/css-values-4/#combine-mixed - // The computed value of a percentage-dimension mix is defined as - // FIXME: a computed dimension if the percentage component is zero or is defined specifically to compute to a dimension value - // a computed percentage if the dimension component is zero - // a computed calc() expression otherwise - if (auto const* from_dimension_value = as_if(from); from_dimension_value && to.type() == StyleValue::Type::Percentage) { - auto dimension_component = from_dimension_value->raw_value() * (1.f - delta); - auto percentage_component = to.as_percentage().raw_value() * delta; - if (dimension_component == 0.f) - return PercentageStyleValue::create(Percentage { percentage_component }); - } else if (auto const* to_dimension_value = as_if(to); to_dimension_value && from.type() == StyleValue::Type::Percentage) { - auto dimension_component = to_dimension_value->raw_value() * delta; - auto percentage_component = from.as_percentage().raw_value() * (1.f - delta); - if (dimension_component == 0) - return PercentageStyleValue::create(Percentage { percentage_component }); - } - - auto from_node = to_calculation_node(from); - auto to_node = to_calculation_node(to); - - // https://drafts.csswg.org/css-values-4/#combine-math - // Interpolation of math functions, with each other or with numeric values and other numeric-valued functions, is defined as Vresult = calc((1 - p) * VA + p * VB). - auto from_contribution = ProductCalculationNode::create({ - from_node, - NumericCalculationNode::create(Number { Number::Type::Number, 1.f - delta }, calculation_context), - }); - - auto to_contribution = ProductCalculationNode::create({ - to_node, - NumericCalculationNode::create(Number { Number::Type::Number, delta }, calculation_context), - }); - - return CalculatedStyleValue::create( - simplify_a_calculation_tree(SumCalculationNode::create({ from_contribution, to_contribution }), calculation_context, {}), - *from_node->numeric_type()->added_to(*to_node->numeric_type()), - calculation_context); - } - - return {}; + return interpolate_mixed_value(calculation_context, from, to, delta); } static auto interpolate_length_percentage = [](CalculationContext const& calculation_context, LengthPercentage const& from, LengthPercentage const& to, float delta) -> Optional {