diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index ff5be5ae34..100eb6c2f2 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -115,6 +115,7 @@ set(SOURCES CSS/SyntaxHighlighter/SyntaxHighlighter.cpp CSS/SystemColor.cpp CSS/Time.cpp + CSS/Transformation.cpp CSS/VisualViewport.cpp Cookie/Cookie.cpp Cookie/ParsedCookie.cpp diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index c274ce49f6..348ad8220d 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include namespace Web::CSS { @@ -232,13 +232,6 @@ public: bool operator==(BorderData const&) const = default; }; -using TransformValue = Variant; - -struct Transformation { - CSS::TransformFunction function; - Vector values; -}; - struct TransformOrigin { CSS::LengthPercentage x { Percentage(50) }; CSS::LengthPercentage y { Percentage(50) }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index a39da7fea2..9c32d24a5e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -438,8 +438,7 @@ Vector StyleProperties::transformations() const if (!it->is_transformation()) return {}; auto& transformation_style_value = it->as_transformation(); - CSS::Transformation transformation; - transformation.function = transformation_style_value.transform_function(); + auto function = transformation_style_value.transform_function(); Vector values; for (auto& transformation_value : transformation_style_value.values()) { if (transformation_value->is_calculated()) { @@ -467,8 +466,7 @@ Vector StyleProperties::transformations() const dbgln("FIXME: Unsupported value in transform! {}", transformation_value->to_string()); } } - transformation.values = move(values); - transformations.append(move(transformation)); + transformations.empend(function, move(values)); } return transformations; } diff --git a/Userland/Libraries/LibWeb/CSS/Transformation.cpp b/Userland/Libraries/LibWeb/CSS/Transformation.cpp new file mode 100644 index 0000000000..5a386fc300 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Transformation.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2022-2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Transformation.h" +#include +#include + +namespace Web::CSS { + +Transformation::Transformation(TransformFunction function, Vector&& values) + : m_function(function) + , m_values(move(values)) +{ +} + +Gfx::FloatMatrix4x4 Transformation::to_matrix(Painting::PaintableBox const& paintable_box) const +{ + auto count = m_values.size(); + auto value = [&](size_t index, CSS::Length const& reference_length = CSS::Length::make_px(0)) -> float { + return m_values[index].visit( + [&](CSS::LengthPercentage const& value) -> double { + return value.resolved(paintable_box.layout_node(), reference_length).to_px(paintable_box.layout_node()).to_float(); + }, + [&](CSS::AngleOrCalculated const& value) { + return AK::to_radians(value.resolved(paintable_box.layout_node()).to_degrees()); + }, + [](double value) { + return value; + }); + }; + + auto reference_box = paintable_box.absolute_rect(); + auto width = CSS::Length::make_px(reference_box.width()); + auto height = CSS::Length::make_px(reference_box.height()); + + switch (m_function) { + case CSS::TransformFunction::Matrix: + if (count == 6) + return Gfx::FloatMatrix4x4(value(0), value(2), 0, value(4), + value(1), value(3), 0, value(5), + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::Matrix3d: + if (count == 16) + return Gfx::FloatMatrix4x4(value(0), value(4), value(8), value(12), + value(1), value(5), value(9), value(13), + value(2), value(6), value(10), value(14), + value(3), value(7), value(11), value(15)); + break; + case CSS::TransformFunction::Translate: + if (count == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + if (count == 2) + return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), + 0, 1, 0, value(1, height), + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::Translate3d: + return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), + 0, 1, 0, value(1, height), + 0, 0, 1, value(2), + 0, 0, 0, 1); + break; + case CSS::TransformFunction::TranslateX: + if (count == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::TranslateY: + if (count == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, 0, + 0, 1, 0, value(0, height), + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::Scale: + if (count == 1) + return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, + 0, value(0), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + if (count == 2) + return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, + 0, value(1), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::ScaleX: + if (count == 1) + return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::ScaleY: + if (count == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, 0, + 0, value(0), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::RotateX: + if (count == 1) + return Gfx::rotation_matrix({ 1.0f, 0.0f, 0.0f }, value(0)); + break; + case CSS::TransformFunction::RotateY: + if (count == 1) + return Gfx::rotation_matrix({ 0.0f, 1.0f, 0.0f }, value(0)); + break; + case CSS::TransformFunction::Rotate: + case CSS::TransformFunction::RotateZ: + if (count == 1) + return Gfx::rotation_matrix({ 0.0f, 0.0f, 1.0f }, value(0)); + break; + case CSS::TransformFunction::Skew: + if (count == 1) + return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + if (count == 2) + return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, + tanf(value(1)), 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::SkewX: + if (count == 1) + return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::SkewY: + if (count == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, 0, + tanf(value(0)), 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + } + dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unhandled transformation function {} with {} arguments", to_string(m_function), m_values.size()); + return Gfx::FloatMatrix4x4::identity(); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Transformation.h b/Userland/Libraries/LibWeb/CSS/Transformation.h new file mode 100644 index 0000000000..096392046b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Transformation.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Web::CSS { + +using TransformValue = Variant; + +class Transformation { +public: + Transformation(TransformFunction function, Vector&& values); + Gfx::FloatMatrix4x4 to_matrix(Painting::PaintableBox const&) const; + +private: + TransformFunction m_function; + Vector m_values; +}; + +} diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 35c4cde8da..999c2cfc77 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -166,6 +166,7 @@ class Time; class TimeOrCalculated; class TimePercentage; class TimeStyleValue; +class Transformation; class TransformationStyleValue; class URLStyleValue; class UnicodeRange; diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index f902d18438..b14dc28d39 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -270,150 +270,12 @@ void StackingContext::paint_internal(PaintContext& context) const paint_descendants(context, paintable_box(), StackingContextPaintPhase::FocusAndOverlay); } -Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformation const& transformation) const -{ - auto count = transformation.values.size(); - auto value = [this, transformation](size_t index, CSS::Length const& reference_length = CSS::Length::make_px(0)) -> float { - return transformation.values[index].visit( - [this, reference_length](CSS::LengthPercentage const& value) -> double { - return value.resolved(paintable_box().layout_node(), reference_length).to_px(paintable_box().layout_box()).to_float(); - }, - [this](CSS::AngleOrCalculated const& value) { - return value.resolved(paintable_box().layout_node()).to_degrees() * M_DEG2RAD; - }, - [](double value) { - return value; - }); - }; - - auto reference_box = paintable_box().absolute_rect(); - auto width = CSS::Length::make_px(reference_box.width()); - auto height = CSS::Length::make_px(reference_box.height()); - - switch (transformation.function) { - case CSS::TransformFunction::Matrix: - if (count == 6) - return Gfx::FloatMatrix4x4(value(0), value(2), 0, value(4), - value(1), value(3), 0, value(5), - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::Matrix3d: - if (count == 16) - return Gfx::FloatMatrix4x4(value(0), value(4), value(8), value(12), - value(1), value(5), value(9), value(13), - value(2), value(6), value(10), value(14), - value(3), value(7), value(11), value(15)); - break; - case CSS::TransformFunction::Translate: - if (count == 1) - return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - if (count == 2) - return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), - 0, 1, 0, value(1, height), - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::Translate3d: - return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), - 0, 1, 0, value(1, height), - 0, 0, 1, value(2), - 0, 0, 0, 1); - break; - case CSS::TransformFunction::TranslateX: - if (count == 1) - return Gfx::FloatMatrix4x4(1, 0, 0, value(0, width), - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::TranslateY: - if (count == 1) - return Gfx::FloatMatrix4x4(1, 0, 0, 0, - 0, 1, 0, value(0, height), - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::Scale: - if (count == 1) - return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, - 0, value(0), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - if (count == 2) - return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, - 0, value(1), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::ScaleX: - if (count == 1) - return Gfx::FloatMatrix4x4(value(0), 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::ScaleY: - if (count == 1) - return Gfx::FloatMatrix4x4(1, 0, 0, 0, - 0, value(0), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::RotateX: - if (count == 1) - return Gfx::rotation_matrix({ 1.0f, 0.0f, 0.0f }, value(0)); - break; - case CSS::TransformFunction::RotateY: - if (count == 1) - return Gfx::rotation_matrix({ 0.0f, 1.0f, 0.0f }, value(0)); - break; - case CSS::TransformFunction::Rotate: - case CSS::TransformFunction::RotateZ: - if (count == 1) - return Gfx::rotation_matrix({ 0.0f, 0.0f, 1.0f }, value(0)); - break; - case CSS::TransformFunction::Skew: - if (count == 1) - return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - if (count == 2) - return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, - tanf(value(1)), 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::SkewX: - if (count == 1) - return Gfx::FloatMatrix4x4(1, tanf(value(0)), 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - case CSS::TransformFunction::SkewY: - if (count == 1) - return Gfx::FloatMatrix4x4(1, 0, 0, 0, - tanf(value(0)), 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - break; - default: - dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unhandled transformation function {}", CSS::TransformationStyleValue::create(transformation.function, {})->to_string()); - } - return Gfx::FloatMatrix4x4::identity(); -} - Gfx::FloatMatrix4x4 StackingContext::combine_transformations(Vector const& transformations) const { auto matrix = Gfx::FloatMatrix4x4::identity(); for (auto const& transform : transformations) - matrix = matrix * get_transformation_matrix(transform); + matrix = matrix * transform.to_matrix(paintable_box()); return matrix; } diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h index d8c81c8b0e..52a9736c62 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.h +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -51,7 +51,6 @@ private: static void paint_child(PaintContext&, StackingContext const&); void paint_internal(PaintContext&) const; - Gfx::FloatMatrix4x4 get_transformation_matrix(CSS::Transformation const& transformation) const; Gfx::FloatMatrix4x4 combine_transformations(Vector const& transformations) const; Gfx::FloatPoint transform_origin() const { return m_transform_origin; } Gfx::FloatPoint compute_transform_origin() const;