diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 6a0fb41d1d..f65a17c1a9 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -73,44 +73,6 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box() // We can also layout absolutely positioned boxes within this BFC. for (auto& box : m_absolutely_positioned_boxes) layout_absolutely_positioned_element(box); - - // FIXME: Transforms should be a painting concept, not a layout concept. - apply_transformations_to_children(root()); -} - -void BlockFormattingContext::apply_transformations_to_children(Box const& box) -{ - box.for_each_child_of_type([&](auto& child_box) { - float transform_y_offset = 0.0f; - if (!child_box.computed_values().transformations().is_empty()) { - // FIXME: All transformations can be interpreted as successive 3D-matrix operations on the box, we don't do that yet. - // https://drafts.csswg.org/css-transforms/#serialization-of-the-computed-value - for (auto transformation : child_box.computed_values().transformations()) { - switch (transformation.function) { - case CSS::TransformFunction::TranslateY: - if (transformation.values.size() != 1) - continue; - transformation.values.first().visit( - [&](CSS::Length& value) { - transform_y_offset += value.to_px(child_box); - }, - [&](float value) { - transform_y_offset += value; - }, - [&](auto&) { - dbgln("FIXME: Implement unsupported transformation function value type!"); - }); - break; - default: - dbgln("FIXME: Implement missing transform function!"); - } - } - } - - auto& child_box_state = m_state.get_mutable(child_box); - auto untransformed_offset = child_box_state.offset; - child_box_state.offset = Gfx::FloatPoint { untransformed_offset.x(), untransformed_offset.y() + transform_y_offset }; - }); } void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mode) diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index 8924bc433a..af67bf20ba 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -60,8 +60,6 @@ private: void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode); - void apply_transformations_to_children(Box const&); - void layout_list_item_marker(ListItemBox const&); enum class FloatSide { diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index 7bdec9607b..0429c4f2ad 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -4,9 +4,13 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include +#include #include +#include #include #include #include @@ -137,6 +141,104 @@ void StackingContext::paint_internal(PaintContext& context) const paint_descendants(context, m_box, StackingContextPaintPhase::FocusAndOverlay); } +Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformation const& transformation) const +{ + Vector float_values; + for (auto const& value : transformation.values) { + value.visit( + [&](CSS::Length const& value) { + float_values.append(value.to_px(m_box)); + }, + [&](float value) { + float_values.append(value); + }); + } + + switch (transformation.function) { + case CSS::TransformFunction::Matrix: + if (float_values.size() == 6) + return Gfx::FloatMatrix4x4(float_values[0], float_values[2], 0, float_values[4], + float_values[1], float_values[3], 0, float_values[5], + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::Translate: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, float_values[0], + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + if (float_values.size() == 2) + return Gfx::FloatMatrix4x4(1, 0, 0, float_values[0], + 0, 1, 0, float_values[1], + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::TranslateX: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, float_values[0], + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::TranslateY: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, 0, + 0, 1, 0, float_values[0], + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::Scale: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(float_values[0], 0, 0, 0, + 0, float_values[0], 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + if (float_values.size() == 2) + return Gfx::FloatMatrix4x4(float_values[0], 0, 0, 0, + 0, float_values[1], 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::ScaleX: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(float_values[0], 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + break; + case CSS::TransformFunction::ScaleY: + if (float_values.size() == 1) + return Gfx::FloatMatrix4x4(1, 0, 0, 0, + 0, float_values[0], 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); + + return matrix; +} + +// FIXME: This extracts the affine 2D part of the full transformation matrix. +// Use the whole matrix when we get better transformation support in LibGfx or use LibGL for drawing the bitmap +Gfx::AffineTransform StackingContext::combine_transformations_2d(Vector const& transformations) const +{ + auto matrix = combine_transformations(transformations); + auto* m = matrix.elements(); + return Gfx::AffineTransform(m[0][0], m[1][0], m[0][1], m[1][1], m[0][3], m[1][3]); +} + void StackingContext::paint(PaintContext& context) const { Gfx::PainterStateSaver saver(context.painter()); @@ -148,7 +250,9 @@ void StackingContext::paint(PaintContext& context) const if (opacity == 0.0f) return; - if (opacity < 1.0f) { + auto affine_transform = combine_transformations_2d(m_box.computed_values().transformations()); + + if (opacity < 1.0f || !affine_transform.is_identity()) { auto bitmap_or_error = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, context.painter().target()->size()); if (bitmap_or_error.is_error()) return; @@ -156,7 +260,13 @@ void StackingContext::paint(PaintContext& context) const Gfx::Painter painter(bitmap); PaintContext paint_context(painter, context.palette(), context.scroll_offset()); paint_internal(paint_context); - context.painter().blit(Gfx::IntPoint(m_box.paint_box()->absolute_position()), bitmap, Gfx::IntRect(m_box.paint_box()->absolute_rect()), opacity); + + // FIXME: Use the transform origin specified in CSS or SVG + auto transform_origin = m_box.paint_box()->absolute_position(); + auto source_rect = m_box.paint_box()->absolute_rect().translated(-transform_origin); + auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform_origin); + source_rect.translate_by(transform_origin); + context.painter().draw_scaled_bitmap(Gfx::rounded_int_rect(transformed_destination_rect), *bitmap, source_rect, opacity, Gfx::Painter::ScalingMode::BilinearBlend); } else { paint_internal(context); } @@ -252,6 +362,11 @@ void StackingContext::dump(int indent) const else builder.append("auto"); builder.append(')'); + + auto affine_transform = combine_transformations_2d(m_box.computed_values().transformations()); + if (!affine_transform.is_identity()) { + builder.appendff(", transform: {}", affine_transform); + } dbgln("{}", builder.string_view()); for (auto& child : m_children) child->dump(indent + 1); diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h index b5cac7cd26..5d33dc739b 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.h +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -41,6 +42,9 @@ private: Vector m_children; void paint_internal(PaintContext&) const; + Gfx::FloatMatrix4x4 get_transformation_matrix(CSS::Transformation const& transformation) const; + Gfx::FloatMatrix4x4 combine_transformations(Vector const& transformations) const; + Gfx::AffineTransform combine_transformations_2d(Vector const& transformations) const; }; }