mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-04 08:36:12 +00:00
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.
This commit is contained in:
parent
aba435a652
commit
7047fcf761
Notes:
github-actions[bot]
2024-07-23 07:01:41 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 7047fcf761
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/766
8 changed files with 292 additions and 266 deletions
|
@ -242,4 +242,85 @@ CSSPixelRect InlinePaintable::bounding_rect() const
|
||||||
return bounding_rect;
|
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<Painting::ShadowData> 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<Painting::ShadowData> 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<Painting::PaintableFragment&>(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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
void set_outline_offset(CSSPixels outline_offset) { m_outline_offset = outline_offset; }
|
void set_outline_offset(CSSPixels outline_offset) { m_outline_offset = outline_offset; }
|
||||||
CSSPixels outline_offset() const { return m_outline_offset; }
|
CSSPixels outline_offset() const { return m_outline_offset; }
|
||||||
|
|
||||||
|
virtual void resolve_paint_properties() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InlinePaintable(Layout::InlineNode const&);
|
InlinePaintable(Layout::InlineNode const&);
|
||||||
|
|
||||||
|
|
|
@ -189,4 +189,54 @@ Gfx::AffineTransform Paintable::compute_combined_css_transform() const
|
||||||
return combined_transform;
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,6 +214,8 @@ public:
|
||||||
|
|
||||||
Gfx::AffineTransform compute_combined_css_transform() const;
|
Gfx::AffineTransform compute_combined_css_transform() const;
|
||||||
|
|
||||||
|
virtual void resolve_paint_properties() {};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Paintable(Layout::Node const&);
|
explicit Paintable(Layout::Node const&);
|
||||||
|
|
||||||
|
@ -255,4 +257,6 @@ inline bool Paintable::fast_is<PaintableWithLines>() const { return is_paintable
|
||||||
template<>
|
template<>
|
||||||
inline bool Paintable::fast_is<TextPaintable>() const { return is_text_paintable(); }
|
inline bool Paintable::fast_is<TextPaintable>() 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <LibWeb/Painting/BackgroundPainting.h>
|
#include <LibWeb/Painting/BackgroundPainting.h>
|
||||||
#include <LibWeb/Painting/FilterPainting.h>
|
#include <LibWeb/Painting/FilterPainting.h>
|
||||||
#include <LibWeb/Painting/PaintableBox.h>
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
|
#include <LibWeb/Painting/SVGPaintable.h>
|
||||||
|
#include <LibWeb/Painting/SVGSVGPaintable.h>
|
||||||
#include <LibWeb/Painting/StackingContext.h>
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
#include <LibWeb/Painting/TextPaintable.h>
|
#include <LibWeb/Painting/TextPaintable.h>
|
||||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||||
|
@ -1032,4 +1034,150 @@ RefPtr<Gfx::Bitmap> PaintableBox::calculate_mask(PaintContext& context, CSSPixel
|
||||||
return bitmap;
|
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<Painting::ShadowData> 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 <svg> one. Is that correct?
|
||||||
|
// And is it correct to use `else` below?
|
||||||
|
if (is<Painting::SVGPaintable>(*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<Painting::SVGSVGPaintable>();
|
||||||
|
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<Painting::ShadowData> 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<Painting::PaintableFragment&>(fragment).set_shadows(move(resolved_shadow_data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,6 +207,8 @@ public:
|
||||||
|
|
||||||
virtual bool wants_mouse_events() const override;
|
virtual bool wants_mouse_events() const override;
|
||||||
|
|
||||||
|
virtual void resolve_paint_properties() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit PaintableBox(Layout::Box const&);
|
explicit PaintableBox(Layout::Box const&);
|
||||||
|
|
||||||
|
@ -303,6 +305,8 @@ public:
|
||||||
visitor.visit(JS::NonnullGCPtr { fragment.layout_node() });
|
visitor.visit(JS::NonnullGCPtr { fragment.layout_node() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void resolve_paint_properties() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PaintableWithLines(Layout::BlockContainer const&);
|
PaintableWithLines(Layout::BlockContainer const&);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
namespace Web::Painting {
|
namespace Web::Painting {
|
||||||
|
|
||||||
class PaintableFragment {
|
class PaintableFragment {
|
||||||
friend class ViewportPaintable;
|
friend class InlinePaintable;
|
||||||
|
friend class PaintableWithLines;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PaintableFragment(Layout::LineBoxFragment const&);
|
explicit PaintableFragment(Layout::LineBoxFragment const&);
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <LibWeb/DOM/Range.h>
|
#include <LibWeb/DOM/Range.h>
|
||||||
#include <LibWeb/Layout/Viewport.h>
|
#include <LibWeb/Layout/Viewport.h>
|
||||||
#include <LibWeb/Painting/SVGPaintable.h>
|
|
||||||
#include <LibWeb/Painting/SVGSVGPaintable.h>
|
|
||||||
#include <LibWeb/Painting/StackingContext.h>
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||||
#include <LibWeb/Selection/Selection.h>
|
#include <LibWeb/Selection/Selection.h>
|
||||||
|
@ -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()
|
void ViewportPaintable::resolve_paint_only_properties()
|
||||||
{
|
{
|
||||||
// Resolves layout-dependent properties not handled during layout and stores them in the paint tree.
|
// 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
|
// - Transform origins
|
||||||
// - Outlines
|
// - Outlines
|
||||||
for_each_in_inclusive_subtree([&](Paintable& paintable) {
|
for_each_in_inclusive_subtree([&](Paintable& paintable) {
|
||||||
auto& layout_node = paintable.layout_node();
|
paintable.resolve_paint_properties();
|
||||||
|
|
||||||
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<Painting::InlinePaintable&>(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<Painting::PaintableBox&>(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<Painting::ShadowData> 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<Painting::PaintableBox>(paintable)) {
|
|
||||||
auto& paintable_box = static_cast<Painting::PaintableBox&>(paintable);
|
|
||||||
paintable_box.set_box_shadow_data(move(resolved_box_shadow_data));
|
|
||||||
} else if (is<Painting::InlinePaintable>(paintable)) {
|
|
||||||
auto& inline_paintable = static_cast<Painting::InlinePaintable&>(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<Painting::PaintableWithLines const&>(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<Painting::ShadowData> 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<Painting::PaintableFragment&>(fragment).set_shadows(move(resolved_shadow_data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform and transform origin
|
|
||||||
if (is_paintable_box) {
|
|
||||||
auto& paintable_box = static_cast<Painting::PaintableBox&>(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 <svg> one. Is that correct?
|
|
||||||
// And is it correct to use `else` below?
|
|
||||||
if (is<Painting::SVGPaintable>(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<Painting::SVGSVGPaintable>();
|
|
||||||
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<Painting::PaintableBox&>(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<Painting::InlinePaintable&>(paintable);
|
|
||||||
inline_paintable.set_outline_data(outline_data);
|
|
||||||
inline_paintable.set_outline_offset(outline_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_paintable_box) {
|
|
||||||
auto& paintable_box = static_cast<Painting::PaintableBox&>(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<Painting::InlinePaintable&>(paintable);
|
|
||||||
auto combined_transform = paintable.compute_combined_css_transform();
|
|
||||||
inline_paintable.set_combined_css_transform(combined_transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TraversalDecision::Continue;
|
return TraversalDecision::Continue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue