From 7047fcf761ba3cac775befca78e7e6f758dc7c8b Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 22 Jul 2024 18:43:01 +0300 Subject: [PATCH] LibWeb: Separate paint-only property resolution by paintable type Having resolution of all properties for all paintable types in a single function was hard to iterate on, so this change separates it into smaller functions per paintable type. --- .../LibWeb/Painting/InlinePaintable.cpp | 81 ++++++ .../LibWeb/Painting/InlinePaintable.h | 2 + .../Libraries/LibWeb/Painting/Paintable.cpp | 50 ++++ .../Libraries/LibWeb/Painting/Paintable.h | 4 + .../LibWeb/Painting/PaintableBox.cpp | 148 ++++++++++ .../Libraries/LibWeb/Painting/PaintableBox.h | 4 + .../LibWeb/Painting/PaintableFragment.h | 3 +- .../LibWeb/Painting/ViewportPaintable.cpp | 266 +----------------- 8 files changed, 292 insertions(+), 266 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp index ae5a2beb5f0..624dceb262d 100644 --- a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp @@ -242,4 +242,85 @@ CSSPixelRect InlinePaintable::bounding_rect() const return bounding_rect; } +void InlinePaintable::resolve_paint_properties() +{ + auto const& computed_values = this->computed_values(); + auto const& layout_node = this->layout_node(); + auto& fragments = this->fragments(); + + // Border radii + auto const& top_left_border_radius = computed_values.border_top_left_radius(); + auto const& top_right_border_radius = computed_values.border_top_right_radius(); + auto const& bottom_right_border_radius = computed_values.border_bottom_right_radius(); + auto const& bottom_left_border_radius = computed_values.border_bottom_left_radius(); + auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position(); + for (size_t i = 0; i < fragments.size(); ++i) { + auto is_first_fragment = i == 0; + auto is_last_fragment = i == fragments.size() - 1; + auto& fragment = fragments[i]; + CSSPixelRect absolute_fragment_rect { + containing_block_position_in_absolute_coordinates.translated(fragment.offset()), + fragment.size() + }; + if (is_first_fragment) { + auto extra_start_width = box_model().padding.left; + absolute_fragment_rect.translate_by(-extra_start_width, 0); + absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width); + } + if (is_last_fragment) { + auto extra_end_width = box_model().padding.right; + absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width); + } + auto border_radii_data = normalize_border_radii_data(layout_node, + absolute_fragment_rect, top_left_border_radius, + top_right_border_radius, + bottom_right_border_radius, + bottom_left_border_radius); + fragment.set_border_radii_data(border_radii_data); + } + + auto const& box_shadow_data = computed_values.box_shadow(); + Vector resolved_box_shadow_data; + resolved_box_shadow_data.ensure_capacity(box_shadow_data.size()); + for (auto const& layer : box_shadow_data) { + resolved_box_shadow_data.empend( + layer.color, + layer.offset_x.to_px(layout_node), + layer.offset_y.to_px(layout_node), + layer.blur_radius.to_px(layout_node), + layer.spread_distance.to_px(layout_node), + layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer + : Painting::ShadowPlacement::Inner); + } + set_box_shadow_data(move(resolved_box_shadow_data)); + + for (auto const& fragment : fragments) { + auto const& text_shadow = fragment.m_layout_node->computed_values().text_shadow(); + if (!text_shadow.is_empty()) { + Vector resolved_shadow_data; + resolved_shadow_data.ensure_capacity(text_shadow.size()); + for (auto const& layer : text_shadow) { + resolved_shadow_data.empend( + layer.color, + layer.offset_x.to_px(layout_node), + layer.offset_y.to_px(layout_node), + layer.blur_radius.to_px(layout_node), + layer.spread_distance.to_px(layout_node), + Painting::ShadowPlacement::Outer); + } + const_cast(fragment).set_shadows(move(resolved_shadow_data)); + } + } + + // Outlines + auto outline_width = computed_values.outline_width().to_px(layout_node); + auto outline_data = borders_data_for_outline(layout_node, computed_values.outline_color(), computed_values.outline_style(), outline_width); + auto outline_offset = computed_values.outline_offset().to_px(layout_node); + set_outline_data(outline_data); + set_outline_offset(outline_offset); + + auto combined_transform = compute_combined_css_transform(); + set_combined_css_transform(combined_transform); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/InlinePaintable.h b/Userland/Libraries/LibWeb/Painting/InlinePaintable.h index d1e33106b3f..b4ce01ddb06 100644 --- a/Userland/Libraries/LibWeb/Painting/InlinePaintable.h +++ b/Userland/Libraries/LibWeb/Painting/InlinePaintable.h @@ -46,6 +46,8 @@ public: void set_outline_offset(CSSPixels outline_offset) { m_outline_offset = outline_offset; } CSSPixels outline_offset() const { return m_outline_offset; } + virtual void resolve_paint_properties() override; + private: InlinePaintable(Layout::InlineNode const&); diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.cpp b/Userland/Libraries/LibWeb/Painting/Paintable.cpp index ca874c42f6d..e07a2365a76 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/Paintable.cpp @@ -189,4 +189,54 @@ Gfx::AffineTransform Paintable::compute_combined_css_transform() const return combined_transform; } +Painting::BorderRadiiData normalize_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData const& top_left_radius, CSS::BorderRadiusData const& top_right_radius, CSS::BorderRadiusData const& bottom_right_radius, CSS::BorderRadiusData const& bottom_left_radius) +{ + Painting::BorderRadiusData bottom_left_radius_px {}; + Painting::BorderRadiusData bottom_right_radius_px {}; + Painting::BorderRadiusData top_left_radius_px {}; + Painting::BorderRadiusData top_right_radius_px {}; + + bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width()); + bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width()); + top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width()); + top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width()); + + bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height()); + bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height()); + top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height()); + top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height()); + + // Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap + // Let f = min(Li/Si), where i ∈ {top, right, bottom, left}, + // Si is the sum of the two corresponding radii of the corners on side i, + // and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box. + auto l_top = rect.width(); + auto l_bottom = l_top; + auto l_left = rect.height(); + auto l_right = l_left; + auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius); + auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius); + auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius); + auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius); + CSSPixelFraction f = 1; + f = (s_top != 0) ? min(f, l_top / s_top) : f; + f = (s_right != 0) ? min(f, l_right / s_right) : f; + f = (s_bottom != 0) ? min(f, l_bottom / s_bottom) : f; + f = (s_left != 0) ? min(f, l_left / s_left) : f; + + // If f < 1, then all corner radii are reduced by multiplying them by f. + if (f < 1) { + top_left_radius_px.horizontal_radius *= f; + top_left_radius_px.vertical_radius *= f; + top_right_radius_px.horizontal_radius *= f; + top_right_radius_px.vertical_radius *= f; + bottom_right_radius_px.horizontal_radius *= f; + bottom_right_radius_px.vertical_radius *= f; + bottom_left_radius_px.horizontal_radius *= f; + bottom_left_radius_px.vertical_radius *= f; + } + + return Painting::BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px }; +} + } diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.h b/Userland/Libraries/LibWeb/Painting/Paintable.h index 6d1b5aa003e..f9f2c592788 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.h +++ b/Userland/Libraries/LibWeb/Painting/Paintable.h @@ -214,6 +214,8 @@ public: Gfx::AffineTransform compute_combined_css_transform() const; + virtual void resolve_paint_properties() {}; + protected: explicit Paintable(Layout::Node const&); @@ -255,4 +257,6 @@ inline bool Paintable::fast_is() const { return is_paintable template<> inline bool Paintable::fast_is() const { return is_text_paintable(); } +Painting::BorderRadiiData normalize_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData const& top_left_radius, CSS::BorderRadiusData const& top_right_radius, CSS::BorderRadiusData const& bottom_right_radius, CSS::BorderRadiusData const& bottom_left_radius); + } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index e17231a6861..72026fa11da 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -1032,4 +1034,150 @@ RefPtr PaintableBox::calculate_mask(PaintContext& context, CSSPixel return bitmap; } +void PaintableBox::resolve_paint_properties() +{ + auto const& computed_values = this->computed_values(); + auto const& layout_node = this->layout_node(); + + // Border radii + CSSPixelRect const border_rect { 0, 0, border_box_width(), border_box_height() }; + auto const& border_top_left_radius = computed_values.border_top_left_radius(); + auto const& border_top_right_radius = computed_values.border_top_right_radius(); + auto const& border_bottom_right_radius = computed_values.border_bottom_right_radius(); + auto const& border_bottom_left_radius = computed_values.border_bottom_left_radius(); + + auto radii_data = normalize_border_radii_data(layout_node, border_rect, border_top_left_radius, + border_top_right_radius, border_bottom_right_radius, + border_bottom_left_radius); + set_border_radii_data(radii_data); + + // Box shadows + auto const& box_shadow_data = computed_values.box_shadow(); + Vector resolved_box_shadow_data; + resolved_box_shadow_data.ensure_capacity(box_shadow_data.size()); + for (auto const& layer : box_shadow_data) { + resolved_box_shadow_data.empend( + layer.color, + layer.offset_x.to_px(layout_node), + layer.offset_y.to_px(layout_node), + layer.blur_radius.to_px(layout_node), + layer.spread_distance.to_px(layout_node), + layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer + : Painting::ShadowPlacement::Inner); + } + set_box_shadow_data(move(resolved_box_shadow_data)); + + auto const& transformations = computed_values.transformations(); + if (!transformations.is_empty()) { + auto matrix = Gfx::FloatMatrix4x4::identity(); + for (auto const& transform : transformations) + matrix = matrix * transform.to_matrix(*this).release_value(); + set_transform(matrix); + } + + auto const& transform_origin = computed_values.transform_origin(); + // https://www.w3.org/TR/css-transforms-1/#transform-box + auto transform_box = computed_values.transform_box(); + // For SVG elements without associated CSS layout box, the used value for content-box is fill-box and for + // border-box is stroke-box. + // FIXME: This currently detects any SVG element except the one. Is that correct? + // And is it correct to use `else` below? + if (is(*this)) { + switch (transform_box) { + case CSS::TransformBox::ContentBox: + transform_box = CSS::TransformBox::FillBox; + break; + case CSS::TransformBox::BorderBox: + transform_box = CSS::TransformBox::StrokeBox; + break; + default: + break; + } + } + // For elements with associated CSS layout box, the used value for fill-box is content-box and for + // stroke-box and view-box is border-box. + else { + switch (transform_box) { + case CSS::TransformBox::FillBox: + transform_box = CSS::TransformBox::ContentBox; + break; + case CSS::TransformBox::StrokeBox: + case CSS::TransformBox::ViewBox: + transform_box = CSS::TransformBox::BorderBox; + break; + default: + break; + } + } + + CSSPixelRect reference_box = [&]() { + switch (transform_box) { + case CSS::TransformBox::ContentBox: + // Uses the content box as reference box. + // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. + return absolute_rect(); + case CSS::TransformBox::BorderBox: + // Uses the border box as reference box. + // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. + return absolute_border_box_rect(); + case CSS::TransformBox::FillBox: + // Uses the object bounding box as reference box. + // FIXME: For now we're using the content rect as an approximation. + return absolute_rect(); + case CSS::TransformBox::StrokeBox: + // Uses the stroke bounding box as reference box. + // FIXME: For now we're using the border rect as an approximation. + return absolute_border_box_rect(); + case CSS::TransformBox::ViewBox: + // Uses the nearest SVG viewport as reference box. + // FIXME: If a viewBox attribute is specified for the SVG viewport creating element: + // - The reference box is positioned at the origin of the coordinate system established by the viewBox attribute. + // - The dimension of the reference box is set to the width and height values of the viewBox attribute. + auto* svg_paintable = first_ancestor_of_type(); + if (!svg_paintable) + return absolute_border_box_rect(); + return svg_paintable->absolute_rect(); + } + VERIFY_NOT_REACHED(); + }(); + auto x = reference_box.left() + transform_origin.x.to_px(layout_node, reference_box.width()); + auto y = reference_box.top() + transform_origin.y.to_px(layout_node, reference_box.height()); + set_transform_origin({ x, y }); + set_transform_origin({ x, y }); + + // Outlines + auto outline_width = computed_values.outline_width().to_px(layout_node); + auto outline_data = borders_data_for_outline(layout_node, computed_values.outline_color(), computed_values.outline_style(), outline_width); + auto outline_offset = computed_values.outline_offset().to_px(layout_node); + set_outline_data(outline_data); + set_outline_offset(outline_offset); + + auto combined_transform = compute_combined_css_transform(); + set_combined_css_transform(combined_transform); +} + +void PaintableWithLines::resolve_paint_properties() +{ + PaintableBox::resolve_paint_properties(); + + auto const& layout_node = this->layout_node(); + for (auto const& fragment : fragments()) { + auto const& text_shadow = fragment.m_layout_node->computed_values().text_shadow(); + if (!text_shadow.is_empty()) { + Vector resolved_shadow_data; + resolved_shadow_data.ensure_capacity(text_shadow.size()); + for (auto const& layer : text_shadow) { + resolved_shadow_data.empend( + layer.color, + layer.offset_x.to_px(layout_node), + layer.offset_y.to_px(layout_node), + layer.blur_radius.to_px(layout_node), + layer.spread_distance.to_px(layout_node), + Painting::ShadowPlacement::Outer); + } + const_cast(fragment).set_shadows(move(resolved_shadow_data)); + } + } +} + } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 6be7a260dce..be203a0a0d0 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -207,6 +207,8 @@ public: virtual bool wants_mouse_events() const override; + virtual void resolve_paint_properties() override; + protected: explicit PaintableBox(Layout::Box const&); @@ -303,6 +305,8 @@ public: visitor.visit(JS::NonnullGCPtr { fragment.layout_node() }); } + virtual void resolve_paint_properties() override; + protected: PaintableWithLines(Layout::BlockContainer const&); diff --git a/Userland/Libraries/LibWeb/Painting/PaintableFragment.h b/Userland/Libraries/LibWeb/Painting/PaintableFragment.h index 5be106778a7..f044d336ee3 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableFragment.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableFragment.h @@ -14,7 +14,8 @@ namespace Web::Painting { class PaintableFragment { - friend class ViewportPaintable; + friend class InlinePaintable; + friend class PaintableWithLines; public: explicit PaintableFragment(Layout::LineBoxFragment const&); diff --git a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp index 75ab3bd70cc..5afd9e876ff 100644 --- a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp @@ -6,8 +6,6 @@ #include #include -#include -#include #include #include #include @@ -184,56 +182,6 @@ void ViewportPaintable::refresh_clip_state() } } -static Painting::BorderRadiiData normalize_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData const& top_left_radius, CSS::BorderRadiusData const& top_right_radius, CSS::BorderRadiusData const& bottom_right_radius, CSS::BorderRadiusData const& bottom_left_radius) -{ - Painting::BorderRadiusData bottom_left_radius_px {}; - Painting::BorderRadiusData bottom_right_radius_px {}; - Painting::BorderRadiusData top_left_radius_px {}; - Painting::BorderRadiusData top_right_radius_px {}; - - bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width()); - bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width()); - top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width()); - top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width()); - - bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height()); - bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height()); - top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height()); - top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height()); - - // Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap - // Let f = min(Li/Si), where i ∈ {top, right, bottom, left}, - // Si is the sum of the two corresponding radii of the corners on side i, - // and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box. - auto l_top = rect.width(); - auto l_bottom = l_top; - auto l_left = rect.height(); - auto l_right = l_left; - auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius); - auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius); - auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius); - auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius); - CSSPixelFraction f = 1; - f = (s_top != 0) ? min(f, l_top / s_top) : f; - f = (s_right != 0) ? min(f, l_right / s_right) : f; - f = (s_bottom != 0) ? min(f, l_bottom / s_bottom) : f; - f = (s_left != 0) ? min(f, l_left / s_left) : f; - - // If f < 1, then all corner radii are reduced by multiplying them by f. - if (f < 1) { - top_left_radius_px.horizontal_radius *= f; - top_left_radius_px.vertical_radius *= f; - top_right_radius_px.horizontal_radius *= f; - top_right_radius_px.vertical_radius *= f; - bottom_right_radius_px.horizontal_radius *= f; - bottom_right_radius_px.vertical_radius *= f; - bottom_left_radius_px.horizontal_radius *= f; - bottom_left_radius_px.vertical_radius *= f; - } - - return Painting::BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px }; -} - void ViewportPaintable::resolve_paint_only_properties() { // Resolves layout-dependent properties not handled during layout and stores them in the paint tree. @@ -245,219 +193,7 @@ void ViewportPaintable::resolve_paint_only_properties() // - Transform origins // - Outlines for_each_in_inclusive_subtree([&](Paintable& paintable) { - auto& layout_node = paintable.layout_node(); - - auto const is_inline_paintable = paintable.is_inline_paintable(); - auto const is_paintable_box = paintable.is_paintable_box(); - auto const is_paintable_with_lines = paintable.is_paintable_with_lines(); - auto const& computed_values = layout_node.computed_values(); - - // Border radii - if (is_inline_paintable) { - auto& inline_paintable = static_cast(paintable); - auto& fragments = inline_paintable.fragments(); - - auto const& top_left_border_radius = computed_values.border_top_left_radius(); - auto const& top_right_border_radius = computed_values.border_top_right_radius(); - auto const& bottom_right_border_radius = computed_values.border_bottom_right_radius(); - auto const& bottom_left_border_radius = computed_values.border_bottom_left_radius(); - - auto containing_block_position_in_absolute_coordinates = inline_paintable.containing_block()->absolute_position(); - for (size_t i = 0; i < fragments.size(); ++i) { - auto is_first_fragment = i == 0; - auto is_last_fragment = i == fragments.size() - 1; - auto& fragment = fragments[i]; - CSSPixelRect absolute_fragment_rect { - containing_block_position_in_absolute_coordinates.translated(fragment.offset()), - fragment.size() - }; - if (is_first_fragment) { - auto extra_start_width = inline_paintable.box_model().padding.left; - absolute_fragment_rect.translate_by(-extra_start_width, 0); - absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width); - } - if (is_last_fragment) { - auto extra_end_width = inline_paintable.box_model().padding.right; - absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width); - } - auto border_radii_data = normalize_border_radii_data(layout_node, - absolute_fragment_rect, top_left_border_radius, - top_right_border_radius, - bottom_right_border_radius, - bottom_left_border_radius); - fragment.set_border_radii_data(border_radii_data); - } - } - - // Border radii - if (is_paintable_box) { - auto& paintable_box = static_cast(paintable); - - CSSPixelRect const border_rect { 0, 0, paintable_box.border_box_width(), paintable_box.border_box_height() }; - auto const& border_top_left_radius = computed_values.border_top_left_radius(); - auto const& border_top_right_radius = computed_values.border_top_right_radius(); - auto const& border_bottom_right_radius = computed_values.border_bottom_right_radius(); - auto const& border_bottom_left_radius = computed_values.border_bottom_left_radius(); - - auto radii_data = normalize_border_radii_data(layout_node, border_rect, border_top_left_radius, - border_top_right_radius, border_bottom_right_radius, - border_bottom_left_radius); - paintable_box.set_border_radii_data(radii_data); - } - - // Box shadows - auto const& box_shadow_data = computed_values.box_shadow(); - if (!box_shadow_data.is_empty()) { - Vector resolved_box_shadow_data; - resolved_box_shadow_data.ensure_capacity(box_shadow_data.size()); - for (auto const& layer : box_shadow_data) { - resolved_box_shadow_data.empend( - layer.color, - layer.offset_x.to_px(layout_node), - layer.offset_y.to_px(layout_node), - layer.blur_radius.to_px(layout_node), - layer.spread_distance.to_px(layout_node), - layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer - : Painting::ShadowPlacement::Inner); - } - - if (is(paintable)) { - auto& paintable_box = static_cast(paintable); - paintable_box.set_box_shadow_data(move(resolved_box_shadow_data)); - } else if (is(paintable)) { - auto& inline_paintable = static_cast(paintable); - inline_paintable.set_box_shadow_data(move(resolved_box_shadow_data)); - } - } - - // Text shadows - if (is_paintable_with_lines) { - auto const& paintable_with_lines = static_cast(paintable); - for (auto const& fragment : paintable_with_lines.fragments()) { - auto const& text_shadow = fragment.m_layout_node->computed_values().text_shadow(); - if (!text_shadow.is_empty()) { - Vector resolved_shadow_data; - resolved_shadow_data.ensure_capacity(text_shadow.size()); - for (auto const& layer : text_shadow) { - resolved_shadow_data.empend( - layer.color, - layer.offset_x.to_px(layout_node), - layer.offset_y.to_px(layout_node), - layer.blur_radius.to_px(layout_node), - layer.spread_distance.to_px(layout_node), - Painting::ShadowPlacement::Outer); - } - const_cast(fragment).set_shadows(move(resolved_shadow_data)); - } - } - } - - // Transform and transform origin - if (is_paintable_box) { - auto& paintable_box = static_cast(paintable); - auto const& transformations = paintable_box.computed_values().transformations(); - if (!transformations.is_empty()) { - auto matrix = Gfx::FloatMatrix4x4::identity(); - for (auto const& transform : transformations) - matrix = matrix * transform.to_matrix(paintable_box).release_value(); - paintable_box.set_transform(matrix); - } - - auto const& transform_origin = paintable_box.computed_values().transform_origin(); - // https://www.w3.org/TR/css-transforms-1/#transform-box - auto transform_box = paintable_box.computed_values().transform_box(); - // For SVG elements without associated CSS layout box, the used value for content-box is fill-box and for - // border-box is stroke-box. - // FIXME: This currently detects any SVG element except the one. Is that correct? - // And is it correct to use `else` below? - if (is(paintable_box)) { - switch (transform_box) { - case CSS::TransformBox::ContentBox: - transform_box = CSS::TransformBox::FillBox; - break; - case CSS::TransformBox::BorderBox: - transform_box = CSS::TransformBox::StrokeBox; - break; - default: - break; - } - } - // For elements with associated CSS layout box, the used value for fill-box is content-box and for - // stroke-box and view-box is border-box. - else { - switch (transform_box) { - case CSS::TransformBox::FillBox: - transform_box = CSS::TransformBox::ContentBox; - break; - case CSS::TransformBox::StrokeBox: - case CSS::TransformBox::ViewBox: - transform_box = CSS::TransformBox::BorderBox; - break; - default: - break; - } - } - - CSSPixelRect reference_box = [&]() { - switch (transform_box) { - case CSS::TransformBox::ContentBox: - // Uses the content box as reference box. - // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. - return paintable_box.absolute_rect(); - case CSS::TransformBox::BorderBox: - // Uses the border box as reference box. - // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. - return paintable_box.absolute_border_box_rect(); - case CSS::TransformBox::FillBox: - // Uses the object bounding box as reference box. - // FIXME: For now we're using the content rect as an approximation. - return paintable_box.absolute_rect(); - case CSS::TransformBox::StrokeBox: - // Uses the stroke bounding box as reference box. - // FIXME: For now we're using the border rect as an approximation. - return paintable_box.absolute_border_box_rect(); - case CSS::TransformBox::ViewBox: - // Uses the nearest SVG viewport as reference box. - // FIXME: If a viewBox attribute is specified for the SVG viewport creating element: - // - The reference box is positioned at the origin of the coordinate system established by the viewBox attribute. - // - The dimension of the reference box is set to the width and height values of the viewBox attribute. - auto* svg_paintable = paintable_box.first_ancestor_of_type(); - if (!svg_paintable) - return paintable_box.absolute_border_box_rect(); - return svg_paintable->absolute_rect(); - } - VERIFY_NOT_REACHED(); - }(); - auto x = reference_box.left() + transform_origin.x.to_px(layout_node, reference_box.width()); - auto y = reference_box.top() + transform_origin.y.to_px(layout_node, reference_box.height()); - paintable_box.set_transform_origin({ x, y }); - paintable_box.set_transform_origin({ x, y }); - } - - // Outlines - auto outline_width = computed_values.outline_width().to_px(layout_node); - auto outline_data = borders_data_for_outline(layout_node, computed_values.outline_color(), computed_values.outline_style(), outline_width); - auto outline_offset = computed_values.outline_offset().to_px(layout_node); - if (is_paintable_box) { - auto& paintable_box = static_cast(paintable); - paintable_box.set_outline_data(outline_data); - paintable_box.set_outline_offset(outline_offset); - } else if (is_inline_paintable) { - auto& inline_paintable = static_cast(paintable); - inline_paintable.set_outline_data(outline_data); - inline_paintable.set_outline_offset(outline_offset); - } - - if (is_paintable_box) { - auto& paintable_box = static_cast(paintable); - auto combined_transform = paintable.compute_combined_css_transform(); - paintable_box.set_combined_css_transform(combined_transform); - } else if (is_inline_paintable) { - auto& inline_paintable = static_cast(paintable); - auto combined_transform = paintable.compute_combined_css_transform(); - inline_paintable.set_combined_css_transform(combined_transform); - } - + paintable.resolve_paint_properties(); return TraversalDecision::Continue; }); }