Ver Fonte

LibWeb: Support masking of SVGForeignObjectPaintable

Aliaksandr Kalenik há 1 ano atrás
pai
commit
613cd6104d

+ 4 - 0
Tests/LibWeb/Ref/reference/svg-foreign-object-mask-ref.html

@@ -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 - 0
Tests/LibWeb/Ref/svg-foreign-object-mask.html

@@ -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>

+ 5 - 0
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp

@@ -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;

+ 5 - 0
Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp

@@ -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)));
 }
 }
 
 

+ 1 - 0
Userland/Libraries/LibWeb/Layout/Node.h

@@ -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; }

+ 3 - 0
Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp

@@ -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));
     }
     }

+ 5 - 0
Userland/Libraries/LibWeb/Layout/SVGMaskBox.h

@@ -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 - 1
Userland/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h

@@ -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&);
 };
 };