mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-28 12:18:56 +00:00
LibWeb: Support masking of SVGForeignObjectPaintable
This commit is contained in:
parent
7d05fe84bc
commit
613cd6104d
Notes:
sideshowbarker
2024-07-17 08:36:27 +09:00
Author: https://github.com/kalenikaliaksandr
Commit: 613cd6104d
Pull-request: https://github.com/SerenityOS/serenity/pull/24114
Reviewed-by: https://github.com/awesomekling
8 changed files with 41 additions and 1 deletions
|
@ -0,0 +1,4 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<svg width="40" height="40" viewBox="0 0 40 40">
|
||||||
|
<circle cx="16" cy="16" r="16" fill="black"></circle>
|
||||||
|
</svg>
|
10
Tests/LibWeb/Ref/svg-foreign-object-mask.html
Normal file
10
Tests/LibWeb/Ref/svg-foreign-object-mask.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="match" href="reference/svg-foreign-object-mask-ref.html" />
|
||||||
|
<svg width="40" height="40" viewBox="0 0 40 40">
|
||||||
|
<mask id=":r0:" width="32" height="32">
|
||||||
|
<circle cx="16" cy="16" r="16" fill="white"></circle>
|
||||||
|
</mask>
|
||||||
|
<foreignobject x="0" y="0" width="32" height="32" mask="url(#:r0:)">
|
||||||
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAGUlEQVR4nO3BMQEAAADCoPVP7WENoAAAAG4MIAABt9NlCQAAAABJRU5ErkJggg=="/>
|
||||||
|
</foreignobject>
|
||||||
|
</svg>
|
|
@ -623,6 +623,11 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
|
||||||
|
|
||||||
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box);
|
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box);
|
||||||
|
|
||||||
|
// NOTE: It is possible to encounter SVGMaskBox nodes while doing layout of formatting context established by <foreignObject> with a mask.
|
||||||
|
// We should skip and let SVGFormattingContext take care of them.
|
||||||
|
if (box.is_svg_mask_box())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!independent_formatting_context && !is<BlockContainer>(box)) {
|
if (!independent_formatting_context && !is<BlockContainer>(box)) {
|
||||||
dbgln("FIXME: Block-level box is not BlockContainer but does not create formatting context: {}", box.debug_description());
|
dbgln("FIXME: Block-level box is not BlockContainer but does not create formatting context: {}", box.debug_description());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -111,6 +111,11 @@ void InlineLevelIterator::compute_next()
|
||||||
return;
|
return;
|
||||||
do {
|
do {
|
||||||
m_next_node = next_inline_node_in_pre_order(*m_next_node, m_containing_block);
|
m_next_node = next_inline_node_in_pre_order(*m_next_node, m_containing_block);
|
||||||
|
if (m_next_node && m_next_node->is_svg_mask_box()) {
|
||||||
|
// NOTE: It is possible to encounter SVGMaskBox nodes while doing layout of formatting context established by <foreignObject> with a mask.
|
||||||
|
// We should skip and let SVGFormattingContext take care of them.
|
||||||
|
m_next_node = m_next_node->next_sibling();
|
||||||
|
}
|
||||||
} while (m_next_node && (!m_next_node->is_inline() && !m_next_node->is_out_of_flow(m_inline_formatting_context)));
|
} while (m_next_node && (!m_next_node->is_inline() && !m_next_node->is_out_of_flow(m_inline_formatting_context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ public:
|
||||||
virtual bool is_viewport() const { return false; }
|
virtual bool is_viewport() const { return false; }
|
||||||
virtual bool is_svg_box() const { return false; }
|
virtual bool is_svg_box() const { return false; }
|
||||||
virtual bool is_svg_geometry_box() const { return false; }
|
virtual bool is_svg_geometry_box() const { return false; }
|
||||||
|
virtual bool is_svg_mask_box() const { return false; }
|
||||||
virtual bool is_svg_svg_box() const { return false; }
|
virtual bool is_svg_svg_box() const { return false; }
|
||||||
virtual bool is_label() const { return false; }
|
virtual bool is_label() const { return false; }
|
||||||
virtual bool is_replaced_box() const { return false; }
|
virtual bool is_replaced_box() const { return false; }
|
||||||
|
|
|
@ -262,6 +262,9 @@ void SVGFormattingContext::layout_svg_element(Box const& child)
|
||||||
bfc.run(child, LayoutMode::Normal, *m_available_space);
|
bfc.run(child, LayoutMode::Normal, *m_available_space);
|
||||||
auto& child_state = m_state.get_mutable(child);
|
auto& child_state = m_state.get_mutable(child);
|
||||||
child_state.set_content_offset(child_state.offset.translated(m_svg_offset));
|
child_state.set_content_offset(child_state.offset.translated(m_svg_offset));
|
||||||
|
child.for_each_child_of_type<SVGMaskBox>([&](SVGMaskBox const& child) {
|
||||||
|
layout_svg_element(child);
|
||||||
|
});
|
||||||
} else if (is<SVGGraphicsBox>(child)) {
|
} else if (is<SVGGraphicsBox>(child)) {
|
||||||
layout_graphics_element(static_cast<SVGGraphicsBox const&>(child));
|
layout_graphics_element(static_cast<SVGGraphicsBox const&>(child));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,15 @@ public:
|
||||||
SVGMaskBox(DOM::Document&, SVG::SVGMaskElement&, NonnullRefPtr<CSS::StyleProperties>);
|
SVGMaskBox(DOM::Document&, SVG::SVGMaskElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~SVGMaskBox() override = default;
|
virtual ~SVGMaskBox() override = default;
|
||||||
|
|
||||||
|
virtual bool is_svg_mask_box() const override { return true; }
|
||||||
|
|
||||||
SVG::SVGMaskElement& dom_node() { return verify_cast<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
|
SVG::SVGMaskElement& dom_node() { return verify_cast<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
|
||||||
SVG::SVGMaskElement const& dom_node() const { return verify_cast<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
|
SVG::SVGMaskElement const& dom_node() const { return verify_cast<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
|
||||||
|
|
||||||
virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;
|
virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool Node::fast_is<SVGMaskBox>() const { return is_svg_mask_box(); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,12 @@
|
||||||
|
|
||||||
#include <LibWeb/Layout/SVGForeignObjectBox.h>
|
#include <LibWeb/Layout/SVGForeignObjectBox.h>
|
||||||
#include <LibWeb/Painting/PaintableBox.h>
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
|
#include <LibWeb/Painting/SVGMaskable.h>
|
||||||
|
|
||||||
namespace Web::Painting {
|
namespace Web::Painting {
|
||||||
|
|
||||||
class SVGForeignObjectPaintable final : public PaintableWithLines {
|
class SVGForeignObjectPaintable final : public PaintableWithLines
|
||||||
|
, public SVGMaskable {
|
||||||
JS_CELL(SVGForeignObjectPaintable, PaintableWithLines);
|
JS_CELL(SVGForeignObjectPaintable, PaintableWithLines);
|
||||||
JS_DECLARE_ALLOCATOR(SVGForeignObjectPaintable);
|
JS_DECLARE_ALLOCATOR(SVGForeignObjectPaintable);
|
||||||
|
|
||||||
|
@ -24,6 +26,11 @@ public:
|
||||||
|
|
||||||
Layout::SVGForeignObjectBox const& layout_box() const;
|
Layout::SVGForeignObjectBox const& layout_box() const;
|
||||||
|
|
||||||
|
virtual JS::GCPtr<DOM::Node const> dom_node_of_svg() const override { return dom_node(); }
|
||||||
|
virtual Optional<CSSPixelRect> get_masking_area() const override { return get_masking_area_of_svg(); }
|
||||||
|
virtual Optional<Gfx::Bitmap::MaskKind> get_mask_type() const override { return get_mask_type_of_svg(); }
|
||||||
|
virtual RefPtr<Gfx::Bitmap> calculate_mask(PaintContext& paint_context, CSSPixelRect const& masking_area) const override { return calculate_mask_of_svg(paint_context, masking_area); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SVGForeignObjectPaintable(Layout::SVGForeignObjectBox const&);
|
SVGForeignObjectPaintable(Layout::SVGForeignObjectBox const&);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue