diff --git a/Tests/LibWeb/Ref/reference/z-index-should-not-affect-svg-painting-order-ref.html b/Tests/LibWeb/Ref/reference/z-index-should-not-affect-svg-painting-order-ref.html
new file mode 100644
index 00000000000..f79aa40d8b5
--- /dev/null
+++ b/Tests/LibWeb/Ref/reference/z-index-should-not-affect-svg-painting-order-ref.html
@@ -0,0 +1,22 @@
+
+
+
diff --git a/Tests/LibWeb/Ref/z-index-should-not-affect-svg-painting-order.html b/Tests/LibWeb/Ref/z-index-should-not-affect-svg-painting-order.html
new file mode 100644
index 00000000000..47c50971174
--- /dev/null
+++ b/Tests/LibWeb/Ref/z-index-should-not-affect-svg-painting-order.html
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png b/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png
index f66cc990c84..73be241ee7c 100644
Binary files a/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png and b/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png differ
diff --git a/Tests/LibWeb/Screenshot/images/svg-foreign-object-mask-ref.png b/Tests/LibWeb/Screenshot/images/svg-foreign-object-mask-ref.png
index 4e86a598539..ee930bd3de8 100644
Binary files a/Tests/LibWeb/Screenshot/images/svg-foreign-object-mask-ref.png and b/Tests/LibWeb/Screenshot/images/svg-foreign-object-mask-ref.png differ
diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp
index b1d070dd356..48cf9bba87d 100644
--- a/Userland/Libraries/LibWeb/Layout/Node.cpp
+++ b/Userland/Libraries/LibWeb/Layout/Node.cpp
@@ -152,6 +152,9 @@ bool Node::establishes_stacking_context() const
if (!has_style())
return false;
+ if (is_svg_box() || is_svg_svg_box())
+ return false;
+
// We make a stacking context for the viewport. Painting and hit testing starts from here.
if (is_viewport())
return true;
diff --git a/Userland/Libraries/LibWeb/Painting/Command.h b/Userland/Libraries/LibWeb/Painting/Command.h
index f8bcd56f119..dd80c05b8e0 100644
--- a/Userland/Libraries/LibWeb/Painting/Command.h
+++ b/Userland/Libraries/LibWeb/Painting/Command.h
@@ -105,11 +105,6 @@ struct StackingContextTransform {
Gfx::FloatMatrix4x4 matrix;
};
-struct StackingContextMask {
- NonnullRefPtr mask_bitmap;
- Gfx::Bitmap::MaskKind mask_kind;
-};
-
struct PushStackingContext {
float opacity;
// The bounding box of the source paintable (pre-transform).
@@ -117,7 +112,6 @@ struct PushStackingContext {
// A translation to be applied after the stacking context has been transformed.
Gfx::IntPoint post_transform_translation;
StackingContextTransform transform;
- Optional mask = {};
Optional clip_path = {};
void translate_by(Gfx::IntPoint const& offset)
@@ -387,6 +381,32 @@ struct PaintScrollBar {
}
};
+struct ApplyOpacity {
+ float opacity;
+};
+
+struct ApplyTransform {
+ Gfx::FloatPoint origin;
+ Gfx::FloatMatrix4x4 matrix;
+ Gfx::IntPoint post_transform_translation;
+
+ void translate_by(Gfx::IntPoint const& offset)
+ {
+ origin.translate_by(offset.to_type());
+ }
+};
+
+struct ApplyMaskBitmap {
+ Gfx::IntPoint origin;
+ NonnullRefPtr bitmap;
+ Gfx::Bitmap::MaskKind kind;
+
+ void translate_by(Gfx::IntPoint const& offset)
+ {
+ origin.translate_by(offset);
+ }
+};
+
using Command = Variant<
DrawGlyphRun,
FillRect,
@@ -418,6 +438,9 @@ using Command = Variant<
AddRoundedRectClip,
AddMask,
PaintNestedDisplayList,
- PaintScrollBar>;
+ PaintScrollBar,
+ ApplyOpacity,
+ ApplyTransform,
+ ApplyMaskBitmap>;
}
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
index be641277ee5..0dfddbcd6ff 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
@@ -99,6 +99,9 @@ void DisplayListPlayer::execute(DisplayList& display_list)
else HANDLE_COMMAND(AddMask, add_mask)
else HANDLE_COMMAND(PaintScrollBar, paint_scrollbar)
else HANDLE_COMMAND(PaintNestedDisplayList, paint_nested_display_list)
+ else HANDLE_COMMAND(ApplyOpacity, apply_opacity)
+ else HANDLE_COMMAND(ApplyTransform, apply_transform)
+ else HANDLE_COMMAND(ApplyMaskBitmap, apply_mask_bitmap)
else VERIFY_NOT_REACHED();
// clang-format on
}
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.h b/Userland/Libraries/LibWeb/Painting/DisplayList.h
index 3ffbb504961..aee3e4849e2 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayList.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayList.h
@@ -73,6 +73,9 @@ private:
virtual void add_mask(AddMask const&) = 0;
virtual void paint_nested_display_list(PaintNestedDisplayList const&) = 0;
virtual void paint_scrollbar(PaintScrollBar const&) = 0;
+ virtual void apply_opacity(ApplyOpacity const&) = 0;
+ virtual void apply_transform(ApplyTransform const&) = 0;
+ virtual void apply_mask_bitmap(ApplyMaskBitmap const&) = 0;
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
};
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
index 5489ab6ceb5..bcd496751c5 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
@@ -452,47 +452,6 @@ void DisplayListPlayerSkia::push_stacking_context(PushStackingContext const& com
canvas.clipPath(to_skia_path(command.clip_path.value()), true);
}
- if (command.mask.has_value()) {
- auto sk_bitmap = to_skia_bitmap(*command.mask.value().mask_bitmap);
- auto mask_image = SkImages::RasterFromBitmap(sk_bitmap);
-
- char const* sksl_shader = nullptr;
- if (command.mask->mask_kind == Gfx::Bitmap::MaskKind::Luminance) {
- sksl_shader = R"(
- uniform shader mask_image;
- half4 main(float2 coord) {
- half4 color = mask_image.eval(coord);
- half luminance = 0.2126 * color.b + 0.7152 * color.g + 0.0722 * color.r;
- return half4(0.0, 0.0, 0.0, color.a * luminance);
- }
- )";
- } else if (command.mask->mask_kind == Gfx::Bitmap::MaskKind::Alpha) {
- sksl_shader = R"(
- uniform shader mask_image;
- half4 main(float2 coord) {
- half4 color = mask_image.eval(coord);
- return half4(0.0, 0.0, 0.0, color.a);
- }
- )";
- } else {
- VERIFY_NOT_REACHED();
- }
-
- auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl_shader));
- if (!effect) {
- dbgln("SkSL error: {}", error.c_str());
- VERIFY_NOT_REACHED();
- }
-
- SkMatrix mask_matrix;
- auto mask_position = command.source_paintable_rect.location();
- mask_matrix.setTranslate(mask_position.x(), mask_position.y());
-
- SkRuntimeShaderBuilder builder(effect);
- builder.child("mask_image") = mask_image->makeShader(SkSamplingOptions(), mask_matrix);
- canvas.clipShader(builder.makeShader());
- }
-
canvas.concat(matrix);
}
@@ -1283,6 +1242,70 @@ void DisplayListPlayerSkia::paint_scrollbar(PaintScrollBar const& command)
canvas.drawRRect(rrect, stroke_paint);
}
+void DisplayListPlayerSkia::apply_opacity(ApplyOpacity const& command)
+{
+ auto& canvas = surface().canvas();
+ SkPaint paint;
+ paint.setAlphaf(command.opacity);
+ canvas.saveLayer(nullptr, &paint);
+}
+
+void DisplayListPlayerSkia::apply_transform(ApplyTransform const& command)
+{
+ auto affine_transform = Gfx::extract_2d_affine_transform(command.matrix);
+ auto new_transform = Gfx::AffineTransform {}
+ .set_translation(command.post_transform_translation.to_type())
+ .translate(command.origin)
+ .multiply(affine_transform)
+ .translate(-command.origin);
+ auto matrix = to_skia_matrix(new_transform);
+ surface().canvas().concat(matrix);
+}
+
+void DisplayListPlayerSkia::apply_mask_bitmap(ApplyMaskBitmap const& command)
+{
+ auto& canvas = surface().canvas();
+
+ auto sk_bitmap = to_skia_bitmap(*command.bitmap);
+ auto mask_image = SkImages::RasterFromBitmap(sk_bitmap);
+
+ char const* sksl_shader = nullptr;
+ if (command.kind == Gfx::Bitmap::MaskKind::Luminance) {
+ sksl_shader = R"(
+ uniform shader mask_image;
+ half4 main(float2 coord) {
+ half4 color = mask_image.eval(coord);
+ half luminance = 0.2126 * color.b + 0.7152 * color.g + 0.0722 * color.r;
+ return half4(0.0, 0.0, 0.0, color.a * luminance);
+ }
+ )";
+ } else if (command.kind == Gfx::Bitmap::MaskKind::Alpha) {
+ sksl_shader = R"(
+ uniform shader mask_image;
+ half4 main(float2 coord) {
+ half4 color = mask_image.eval(coord);
+ return half4(0.0, 0.0, 0.0, color.a);
+ }
+ )";
+ } else {
+ VERIFY_NOT_REACHED();
+ }
+
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl_shader));
+ if (!effect) {
+ dbgln("SkSL error: {}", error.c_str());
+ VERIFY_NOT_REACHED();
+ }
+
+ SkMatrix mask_matrix;
+ auto mask_position = command.origin;
+ mask_matrix.setTranslate(mask_position.x(), mask_position.y());
+
+ SkRuntimeShaderBuilder builder(effect);
+ builder.child("mask_image") = mask_image->makeShader(SkSamplingOptions(), mask_matrix);
+ canvas.clipShader(builder.makeShader());
+}
+
bool DisplayListPlayerSkia::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
{
return surface().canvas().quickReject(to_skia_rect(rect));
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
index e13bafc174f..03f7bb267fe 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
@@ -79,6 +79,9 @@ private:
void add_mask(AddMask const&) override;
void paint_scrollbar(PaintScrollBar const&) override;
void paint_nested_display_list(PaintNestedDisplayList const&) override;
+ void apply_opacity(ApplyOpacity const&) override;
+ void apply_transform(ApplyTransform const&) override;
+ void apply_mask_bitmap(ApplyMaskBitmap const&) override;
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
index 6c763a1166e..8a24da42ead 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
@@ -306,7 +306,6 @@ void DisplayListRecorder::push_stacking_context(PushStackingContextParams params
.origin = params.transform.origin,
.matrix = params.transform.matrix,
},
- .mask = params.mask,
.clip_path = params.clip_path });
m_state_stack.append(State());
}
@@ -410,4 +409,27 @@ void DisplayListRecorder::paint_scrollbar(int scroll_frame_id, Gfx::IntRect rect
.vertical = vertical });
}
+void DisplayListRecorder::apply_opacity(float opacity)
+{
+ append(ApplyOpacity { .opacity = opacity });
+}
+
+void DisplayListRecorder::apply_transform(Gfx::FloatPoint origin, Gfx::FloatMatrix4x4 matrix)
+{
+ append(ApplyTransform {
+ .origin = origin,
+ .matrix = matrix,
+ .post_transform_translation = state().translation.translation().to_rounded(),
+ });
+}
+
+void DisplayListRecorder::apply_mask_bitmap(Gfx::IntPoint origin, Gfx::Bitmap const& bitmap, Gfx::Bitmap::MaskKind kind)
+{
+ append(ApplyMaskBitmap {
+ .origin = origin,
+ .bitmap = bitmap,
+ .kind = kind,
+ });
+}
+
}
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
index e329e57e88a..60349ae641f 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
@@ -122,7 +122,6 @@ public:
bool is_fixed_position;
Gfx::IntRect source_paintable_rect;
StackingContextTransform transform;
- Optional mask = {};
Optional clip_path = {};
};
void push_stacking_context(PushStackingContextParams params);
@@ -147,6 +146,10 @@ public:
void paint_scrollbar(int scroll_frame_id, Gfx::IntRect, CSSPixelFraction scroll_size, bool vertical);
+ void apply_opacity(float opacity);
+ void apply_transform(Gfx::FloatPoint origin, Gfx::FloatMatrix4x4);
+ void apply_mask_bitmap(Gfx::IntPoint origin, Gfx::Bitmap const&, Gfx::Bitmap::MaskKind);
+
DisplayListRecorder(DisplayList&);
~DisplayListRecorder();
diff --git a/Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp b/Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp
index 16875022ede..f52b8daf4ae 100644
--- a/Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp
@@ -18,6 +18,8 @@ namespace Web::Painting {
template
static T const* first_child_layout_node_of_type(SVG::SVGGraphicsElement const& graphics_element)
{
+ if (!graphics_element.layout_node())
+ return nullptr;
return graphics_element.layout_node()->first_child_of_type();
}
@@ -87,7 +89,7 @@ RefPtr SVGMaskable::calculate_mask_of_svg(PaintContext& context, CS
auto paint_context = context.clone(display_list_recorder);
paint_context.set_svg_transform(graphics_element.get_transform());
paint_context.set_draw_svg_geometry_for_clip_path(is(paintable));
- StackingContext::paint_node_as_stacking_context(paintable, paint_context);
+ StackingContext::paint_svg(paint_context, paintable, PaintPhase::Foreground);
DisplayListPlayerSkia display_list_player { *mask_bitmap };
display_list_player.execute(display_list);
return mask_bitmap;
diff --git a/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp
index 5fbdf919902..3fb691d81e4 100644
--- a/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp
@@ -43,4 +43,69 @@ void SVGSVGPaintable::after_children_paint(PaintContext& context, PaintPhase pha
context.display_list_recorder().restore();
}
+static Gfx::FloatMatrix4x4 matrix_with_scaled_translation(Gfx::FloatMatrix4x4 matrix, float scale)
+{
+ auto* m = matrix.elements();
+ m[0][3] *= scale;
+ m[1][3] *= scale;
+ m[2][3] *= scale;
+ return matrix;
+}
+
+void SVGSVGPaintable::paint_descendants(PaintContext& context, PaintableBox const& paintable, PaintPhase phase)
+{
+ if (phase != PaintPhase::Foreground)
+ return;
+
+ auto paint_svg_box = [&](auto& svg_box) {
+ auto const& computed_values = svg_box.computed_values();
+
+ auto masking_area = svg_box.get_masking_area();
+ auto needs_to_save_state = computed_values.opacity() < 1 || svg_box.has_css_transform() || svg_box.get_masking_area().has_value();
+
+ if (needs_to_save_state) {
+ context.display_list_recorder().save();
+ }
+
+ if (computed_values.opacity() < 1) {
+ context.display_list_recorder().apply_opacity(computed_values.opacity());
+ }
+
+ if (svg_box.has_css_transform()) {
+ auto transform_matrix = svg_box.transform();
+ Gfx::FloatPoint transform_origin = svg_box.transform_origin().template to_type();
+ auto to_device_pixels_scale = float(context.device_pixels_per_css_pixel());
+ context.display_list_recorder().apply_transform(transform_origin.scaled(to_device_pixels_scale), matrix_with_scaled_translation(transform_matrix, to_device_pixels_scale));
+ }
+
+ if (masking_area.has_value()) {
+ if (masking_area->is_empty())
+ return;
+ auto mask_bitmap = svg_box.calculate_mask(context, *masking_area);
+ if (mask_bitmap) {
+ auto source_paintable_rect = context.enclosing_device_rect(*masking_area).template to_type();
+ auto origin = source_paintable_rect.location();
+ context.display_list_recorder().apply_mask_bitmap(origin, mask_bitmap.release_nonnull(), *svg_box.get_mask_type());
+ }
+ }
+
+ svg_box.before_paint(context, PaintPhase::Foreground);
+ svg_box.paint(context, PaintPhase::Foreground);
+ svg_box.after_paint(context, PaintPhase::Foreground);
+
+ paint_descendants(context, svg_box, phase);
+
+ if (needs_to_save_state) {
+ context.display_list_recorder().restore();
+ }
+ };
+
+ paintable.before_children_paint(context, PaintPhase::Foreground);
+ paintable.for_each_child([&](auto& child) {
+ paint_svg_box(verify_cast(child));
+ return IterationDecision::Continue;
+ });
+ paintable.after_children_paint(context, PaintPhase::Foreground);
+}
+
}
diff --git a/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.h b/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.h
index dd0ca04218e..a8697b13523 100644
--- a/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.h
+++ b/Userland/Libraries/LibWeb/Painting/SVGSVGPaintable.h
@@ -23,6 +23,8 @@ public:
Layout::SVGSVGBox const& layout_box() const;
+ static void paint_descendants(PaintContext& context, PaintableBox const& paintable, PaintPhase phase);
+
protected:
SVGSVGPaintable(Layout::SVGSVGBox const&);
};
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
index 4a6006d3fcc..bd199ac98d0 100644
--- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
@@ -5,19 +5,16 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include
#include
-#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
-#include
+#include
#include
#include
@@ -81,6 +78,11 @@ static PaintPhase to_paint_phase(StackingContext::StackingContextPaintPhase phas
void StackingContext::paint_node_as_stacking_context(Paintable const& paintable, PaintContext& context)
{
+ if (paintable.layout_node().is_svg_svg_box()) {
+ paint_svg(context, static_cast(paintable), PaintPhase::Foreground);
+ return;
+ }
+ VERIFY(!paintable.layout_node().is_svg_svg_box());
paint_node(paintable, context, PaintPhase::Background);
paint_node(paintable, context, PaintPhase::Border);
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBorders);
@@ -93,6 +95,18 @@ void StackingContext::paint_node_as_stacking_context(Paintable const& paintable,
paint_descendants(context, paintable, StackingContextPaintPhase::FocusAndOverlay);
}
+void StackingContext::paint_svg(PaintContext& context, PaintableBox const& paintable, PaintPhase phase)
+{
+ if (phase != PaintPhase::Foreground)
+ return;
+
+ paintable.apply_clip_overflow_rect(context, PaintPhase::Foreground);
+ paint_node(paintable, context, PaintPhase::Background);
+ paint_node(paintable, context, PaintPhase::Border);
+ SVGSVGPaintable::paint_descendants(context, paintable, phase);
+ paintable.clear_clip_overflow_rect(context, PaintPhase::Foreground);
+}
+
void StackingContext::paint_descendants(PaintContext& context, Paintable const& paintable, StackingContextPaintPhase phase)
{
paintable.before_children_paint(context, to_paint_phase(phase));
@@ -101,6 +115,11 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&
auto* stacking_context = child.stacking_context();
auto const& z_index = child.computed_values().z_index();
+ if (child.layout_node().is_svg_svg_box()) {
+ paint_svg(context, static_cast(child), to_paint_phase(phase));
+ return IterationDecision::Continue;
+ }
+
// NOTE: Grid specification https://www.w3.org/TR/css-grid-2/#z-order says that grid items should be treated
// the same way as CSS2 defines for inline-blocks:
// "For each one of these, treat the element as if it created a new stacking context, but any positioned
@@ -174,6 +193,9 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&
void StackingContext::paint_child(PaintContext& context, StackingContext const& child)
{
+ VERIFY(!child.paintable().layout_node().is_svg_box());
+ VERIFY(!child.paintable().layout_node().is_svg_svg_box());
+
const_cast(child).set_last_paint_generation_id(context.paint_generation_id());
auto parent_paintable = child.paintable().parent();
@@ -188,6 +210,12 @@ void StackingContext::paint_child(PaintContext& context, StackingContext const&
void StackingContext::paint_internal(PaintContext& context) const
{
+ VERIFY(!paintable().layout_node().is_svg_box());
+ if (paintable().layout_node().is_svg_svg_box()) {
+ paint_svg(context, paintable_box(), PaintPhase::Foreground);
+ return;
+ }
+
// For a more elaborate description of the algorithm, see CSS 2.1 Appendix E
// Draw the background and borders for the context root (steps 1, 2)
paint_node(paintable(), context, PaintPhase::Background);
@@ -299,22 +327,6 @@ void StackingContext::paint(PaintContext& context) const
},
};
- if (paintable().is_paintable_box()) {
- if (auto masking_area = paintable_box().get_masking_area(); masking_area.has_value()) {
- if (masking_area->is_empty())
- return;
- auto mask_bitmap = paintable_box().calculate_mask(context, *masking_area);
- if (mask_bitmap) {
- auto source_paintable_rect = context.enclosing_device_rect(*masking_area).to_type();
- push_stacking_context_params.source_paintable_rect = source_paintable_rect;
- push_stacking_context_params.mask = StackingContextMask {
- .mask_bitmap = mask_bitmap.release_nonnull(),
- .mask_kind = *paintable_box().get_mask_type()
- };
- }
- }
- }
-
auto const& computed_values = paintable().computed_values();
if (auto clip_path = computed_values.clip_path(); clip_path.has_value() && clip_path->is_basic_shape()) {
auto const& masking_area = paintable_box().get_masking_area();
@@ -332,6 +344,19 @@ void StackingContext::paint(PaintContext& context) const
if (paintable().is_paintable_box() && paintable_box().scroll_frame_id().has_value())
context.display_list_recorder().set_scroll_frame_id(*paintable_box().scroll_frame_id());
context.display_list_recorder().push_stacking_context(push_stacking_context_params);
+
+ if (paintable().is_paintable_box()) {
+ if (auto masking_area = paintable_box().get_masking_area(); masking_area.has_value()) {
+ if (masking_area->is_empty())
+ return;
+ auto mask_bitmap = paintable_box().calculate_mask(context, *masking_area);
+ if (mask_bitmap) {
+ auto masking_area_rect = context.enclosing_device_rect(*masking_area).to_type();
+ context.display_list_recorder().apply_mask_bitmap(masking_area_rect.location(), mask_bitmap.release_nonnull(), *paintable_box().get_mask_type());
+ }
+ }
+ }
+
paint_internal(context);
context.display_list_recorder().pop_stacking_context();
if (has_css_transform)
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h
index 8beb5627eeb..8fb9ecb6e88 100644
--- a/Userland/Libraries/LibWeb/Painting/StackingContext.h
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h
@@ -36,6 +36,7 @@ public:
static void paint_node_as_stacking_context(Paintable const&, PaintContext&);
static void paint_descendants(PaintContext&, Paintable const&, StackingContextPaintPhase);
+ static void paint_svg(PaintContext&, PaintableBox const&, PaintPhase);
void paint(PaintContext&) const;
[[nodiscard]] TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function const& callback) const;