mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibWeb: Separate svg mask calculation into SVGMaskable
Preparation work before adding support for SVGForeignObjectElement masking. Having a mixin makes possible sharing mask calculation between SVGForeignObjectElement's paintable and SVGGraphicsPaintable. Both has to support masking: - PaintableWithLines (from SVGForeignObjectElement) -> PaintableBox - SVGGraphicsPaintable -> SVGPaintable -> PaintableBox After this change it will be possible to introduce a new paintable type for foreignObject that inherits from both PaintableWithLines and SVGMaskable.
This commit is contained in:
parent
88d0702763
commit
acd5369774
Notes:
sideshowbarker
2024-07-17 17:40:13 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/acd5369774 Pull-request: https://github.com/SerenityOS/serenity/pull/24114 Reviewed-by: https://github.com/awesomekling
5 changed files with 129 additions and 83 deletions
|
@ -543,6 +543,7 @@ set(SOURCES
|
||||||
Painting/SVGPathPaintable.cpp
|
Painting/SVGPathPaintable.cpp
|
||||||
Painting/SVGGraphicsPaintable.cpp
|
Painting/SVGGraphicsPaintable.cpp
|
||||||
Painting/SVGMaskPaintable.cpp
|
Painting/SVGMaskPaintable.cpp
|
||||||
|
Painting/SVGMaskable.cpp
|
||||||
Painting/SVGClipPaintable.cpp
|
Painting/SVGClipPaintable.cpp
|
||||||
Painting/SVGPaintable.cpp
|
Painting/SVGPaintable.cpp
|
||||||
Painting/SVGSVGPaintable.cpp
|
Painting/SVGSVGPaintable.cpp
|
||||||
|
|
|
@ -7,11 +7,9 @@
|
||||||
#include <LibWeb/Layout/ImageBox.h>
|
#include <LibWeb/Layout/ImageBox.h>
|
||||||
#include <LibWeb/Layout/SVGClipBox.h>
|
#include <LibWeb/Layout/SVGClipBox.h>
|
||||||
#include <LibWeb/Layout/SVGMaskBox.h>
|
#include <LibWeb/Layout/SVGMaskBox.h>
|
||||||
#include <LibWeb/Painting/CommandExecutorCPU.h>
|
|
||||||
#include <LibWeb/Painting/SVGClipPaintable.h>
|
#include <LibWeb/Painting/SVGClipPaintable.h>
|
||||||
#include <LibWeb/Painting/SVGGraphicsPaintable.h>
|
#include <LibWeb/Painting/SVGGraphicsPaintable.h>
|
||||||
#include <LibWeb/Painting/StackingContext.h>
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
#include <LibWeb/SVG/SVGMaskElement.h>
|
|
||||||
#include <LibWeb/SVG/SVGSVGElement.h>
|
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||||
|
|
||||||
namespace Web::Painting {
|
namespace Web::Painting {
|
||||||
|
@ -31,81 +29,4 @@ Layout::SVGGraphicsBox const& SVGGraphicsPaintable::layout_box() const
|
||||||
return static_cast<Layout::SVGGraphicsBox const&>(layout_node());
|
return static_cast<Layout::SVGGraphicsBox const&>(layout_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<CSSPixelRect> SVGGraphicsPaintable::get_masking_area() const
|
|
||||||
{
|
|
||||||
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node());
|
|
||||||
Optional<CSSPixelRect> masking_area = {};
|
|
||||||
if (auto* mask_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGMaskBox>())
|
|
||||||
masking_area = mask_box->dom_node().resolve_masking_area(mask_box->paintable_box()->absolute_border_box_rect());
|
|
||||||
if (auto* clip_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGClipBox>()) {
|
|
||||||
// This is a bit ad-hoc, but if we have both a mask and a clip-path, intersect the two areas to find the masking area.
|
|
||||||
auto clip_area = clip_box->paintable_box()->absolute_border_box_rect();
|
|
||||||
if (masking_area.has_value())
|
|
||||||
masking_area = masking_area->intersected(clip_area);
|
|
||||||
else
|
|
||||||
masking_area = clip_area;
|
|
||||||
}
|
|
||||||
return masking_area;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Gfx::Bitmap::MaskKind mask_type_to_gfx_mask_kind(CSS::MaskType mask_type)
|
|
||||||
{
|
|
||||||
switch (mask_type) {
|
|
||||||
case CSS::MaskType::Alpha:
|
|
||||||
return Gfx::Bitmap::MaskKind::Alpha;
|
|
||||||
case CSS::MaskType::Luminance:
|
|
||||||
return Gfx::Bitmap::MaskKind::Luminance;
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Gfx::Bitmap::MaskKind> SVGGraphicsPaintable::get_mask_type() const
|
|
||||||
{
|
|
||||||
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node());
|
|
||||||
if (auto mask = graphics_element.mask())
|
|
||||||
return mask_type_to_gfx_mask_kind(mask->layout_node()->computed_values().mask_type());
|
|
||||||
if (graphics_element.clip_path())
|
|
||||||
return Gfx::Bitmap::MaskKind::Alpha;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> SVGGraphicsPaintable::calculate_mask(PaintContext& context, CSSPixelRect const& masking_area) const
|
|
||||||
{
|
|
||||||
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node());
|
|
||||||
auto mask_rect = context.enclosing_device_rect(masking_area);
|
|
||||||
auto paint_mask_or_clip = [&](PaintableBox const& paintable) -> RefPtr<Gfx::Bitmap> {
|
|
||||||
auto mask_bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, mask_rect.size().to_type<int>());
|
|
||||||
RefPtr<Gfx::Bitmap> mask_bitmap = {};
|
|
||||||
if (mask_bitmap_or_error.is_error())
|
|
||||||
return {};
|
|
||||||
mask_bitmap = mask_bitmap_or_error.release_value();
|
|
||||||
CommandList painting_commands;
|
|
||||||
RecordingPainter recording_painter(painting_commands);
|
|
||||||
recording_painter.translate(-mask_rect.location().to_type<int>());
|
|
||||||
auto paint_context = context.clone(recording_painter);
|
|
||||||
paint_context.set_svg_transform(graphics_element.get_transform());
|
|
||||||
paint_context.set_draw_svg_geometry_for_clip_path(is<SVGClipPaintable>(paintable));
|
|
||||||
StackingContext::paint_node_as_stacking_context(paintable, paint_context);
|
|
||||||
CommandExecutorCPU executor { *mask_bitmap };
|
|
||||||
painting_commands.execute(executor);
|
|
||||||
return mask_bitmap;
|
|
||||||
};
|
|
||||||
RefPtr<Gfx::Bitmap> mask_bitmap = {};
|
|
||||||
if (auto* mask_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGMaskBox>()) {
|
|
||||||
auto& mask_paintable = static_cast<PaintableBox const&>(*mask_box->paintable());
|
|
||||||
mask_bitmap = paint_mask_or_clip(mask_paintable);
|
|
||||||
}
|
|
||||||
if (auto* clip_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGClipBox>()) {
|
|
||||||
auto& clip_paintable = static_cast<PaintableBox const&>(*clip_box->paintable());
|
|
||||||
auto clip_bitmap = paint_mask_or_clip(clip_paintable);
|
|
||||||
// Combine the clip-path with the mask (if present).
|
|
||||||
if (mask_bitmap && clip_bitmap)
|
|
||||||
mask_bitmap->apply_mask(*clip_bitmap, Gfx::Bitmap::MaskKind::Alpha);
|
|
||||||
if (!mask_bitmap)
|
|
||||||
mask_bitmap = clip_bitmap;
|
|
||||||
}
|
|
||||||
return mask_bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <LibWeb/Layout/SVGGraphicsBox.h>
|
#include <LibWeb/Layout/SVGGraphicsBox.h>
|
||||||
|
#include <LibWeb/Painting/SVGMaskable.h>
|
||||||
#include <LibWeb/Painting/SVGPaintable.h>
|
#include <LibWeb/Painting/SVGPaintable.h>
|
||||||
|
|
||||||
namespace Web::Painting {
|
namespace Web::Painting {
|
||||||
|
|
||||||
class SVGGraphicsPaintable : public SVGPaintable {
|
class SVGGraphicsPaintable : public SVGPaintable
|
||||||
|
, public SVGMaskable {
|
||||||
JS_CELL(SVGGraphicsPaintable, SVGPaintable);
|
JS_CELL(SVGGraphicsPaintable, SVGPaintable);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -49,9 +51,10 @@ public:
|
||||||
|
|
||||||
Layout::SVGGraphicsBox const& layout_box() const;
|
Layout::SVGGraphicsBox const& layout_box() const;
|
||||||
|
|
||||||
virtual Optional<CSSPixelRect> get_masking_area() const override;
|
virtual JS::GCPtr<DOM::Node const> dom_node_of_svg() const override { return dom_node(); }
|
||||||
virtual Optional<Gfx::Bitmap::MaskKind> get_mask_type() const override;
|
virtual Optional<CSSPixelRect> get_masking_area() const override { return get_masking_area_of_svg(); }
|
||||||
virtual RefPtr<Gfx::Bitmap> calculate_mask(PaintContext&, CSSPixelRect const& masking_area) const override;
|
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); }
|
||||||
|
|
||||||
void set_computed_transforms(ComputedTransforms computed_transforms)
|
void set_computed_transforms(ComputedTransforms computed_transforms)
|
||||||
{
|
{
|
||||||
|
|
96
Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp
Normal file
96
Userland/Libraries/LibWeb/Painting/SVGMaskable.cpp
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/Layout/ImageBox.h>
|
||||||
|
#include <LibWeb/Layout/SVGClipBox.h>
|
||||||
|
#include <LibWeb/Layout/SVGMaskBox.h>
|
||||||
|
#include <LibWeb/Painting/CommandExecutorCPU.h>
|
||||||
|
#include <LibWeb/Painting/SVGClipPaintable.h>
|
||||||
|
#include <LibWeb/Painting/SVGGraphicsPaintable.h>
|
||||||
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
|
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||||
|
|
||||||
|
namespace Web::Painting {
|
||||||
|
|
||||||
|
Optional<CSSPixelRect> SVGMaskable::get_masking_area_of_svg() const
|
||||||
|
{
|
||||||
|
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node_of_svg());
|
||||||
|
Optional<CSSPixelRect> masking_area = {};
|
||||||
|
if (auto* mask_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGMaskBox>()) {
|
||||||
|
masking_area = mask_box->dom_node().resolve_masking_area(mask_box->paintable_box()->absolute_border_box_rect());
|
||||||
|
}
|
||||||
|
if (auto* clip_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGClipBox>()) {
|
||||||
|
// This is a bit ad-hoc, but if we have both a mask and a clip-path, intersect the two areas to find the masking area.
|
||||||
|
auto clip_area = clip_box->paintable_box()->absolute_border_box_rect();
|
||||||
|
if (masking_area.has_value())
|
||||||
|
masking_area = masking_area->intersected(clip_area);
|
||||||
|
else
|
||||||
|
masking_area = clip_area;
|
||||||
|
}
|
||||||
|
return masking_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Gfx::Bitmap::MaskKind mask_type_to_gfx_mask_kind(CSS::MaskType mask_type)
|
||||||
|
{
|
||||||
|
switch (mask_type) {
|
||||||
|
case CSS::MaskType::Alpha:
|
||||||
|
return Gfx::Bitmap::MaskKind::Alpha;
|
||||||
|
case CSS::MaskType::Luminance:
|
||||||
|
return Gfx::Bitmap::MaskKind::Luminance;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Gfx::Bitmap::MaskKind> SVGMaskable::get_mask_type_of_svg() const
|
||||||
|
{
|
||||||
|
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node_of_svg());
|
||||||
|
if (auto mask = graphics_element.mask())
|
||||||
|
return mask_type_to_gfx_mask_kind(mask->layout_node()->computed_values().mask_type());
|
||||||
|
if (graphics_element.clip_path())
|
||||||
|
return Gfx::Bitmap::MaskKind::Alpha;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Gfx::Bitmap> SVGMaskable::calculate_mask_of_svg(PaintContext& context, CSSPixelRect const& masking_area) const
|
||||||
|
{
|
||||||
|
auto const& graphics_element = verify_cast<SVG::SVGGraphicsElement const>(*dom_node_of_svg());
|
||||||
|
auto mask_rect = context.enclosing_device_rect(masking_area);
|
||||||
|
auto paint_mask_or_clip = [&](PaintableBox const& paintable) -> RefPtr<Gfx::Bitmap> {
|
||||||
|
auto mask_bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, mask_rect.size().to_type<int>());
|
||||||
|
RefPtr<Gfx::Bitmap> mask_bitmap = {};
|
||||||
|
if (mask_bitmap_or_error.is_error())
|
||||||
|
return {};
|
||||||
|
mask_bitmap = mask_bitmap_or_error.release_value();
|
||||||
|
CommandList painting_commands;
|
||||||
|
RecordingPainter recording_painter(painting_commands);
|
||||||
|
recording_painter.translate(-mask_rect.location().to_type<int>());
|
||||||
|
auto paint_context = context.clone(recording_painter);
|
||||||
|
paint_context.set_svg_transform(graphics_element.get_transform());
|
||||||
|
paint_context.set_draw_svg_geometry_for_clip_path(is<SVGClipPaintable>(paintable));
|
||||||
|
StackingContext::paint_node_as_stacking_context(paintable, paint_context);
|
||||||
|
CommandExecutorCPU executor { *mask_bitmap };
|
||||||
|
painting_commands.execute(executor);
|
||||||
|
return mask_bitmap;
|
||||||
|
};
|
||||||
|
RefPtr<Gfx::Bitmap> mask_bitmap = {};
|
||||||
|
if (auto* mask_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGMaskBox>()) {
|
||||||
|
auto& mask_paintable = static_cast<PaintableBox const&>(*mask_box->paintable());
|
||||||
|
mask_bitmap = paint_mask_or_clip(mask_paintable);
|
||||||
|
}
|
||||||
|
if (auto* clip_box = graphics_element.layout_node()->first_child_of_type<Layout::SVGClipBox>()) {
|
||||||
|
auto& clip_paintable = static_cast<PaintableBox const&>(*clip_box->paintable());
|
||||||
|
auto clip_bitmap = paint_mask_or_clip(clip_paintable);
|
||||||
|
// Combine the clip-path with the mask (if present).
|
||||||
|
if (mask_bitmap && clip_bitmap)
|
||||||
|
mask_bitmap->apply_mask(*clip_bitmap, Gfx::Bitmap::MaskKind::Alpha);
|
||||||
|
if (!mask_bitmap)
|
||||||
|
mask_bitmap = clip_bitmap;
|
||||||
|
}
|
||||||
|
return mask_bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
Userland/Libraries/LibWeb/Painting/SVGMaskable.h
Normal file
25
Userland/Libraries/LibWeb/Painting/SVGMaskable.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/Painting/PaintContext.h>
|
||||||
|
#include <LibWeb/PixelUnits.h>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Web::Painting {
|
||||||
|
|
||||||
|
class SVGMaskable {
|
||||||
|
public:
|
||||||
|
virtual ~SVGMaskable() = default;
|
||||||
|
|
||||||
|
virtual JS::GCPtr<DOM::Node const> dom_node_of_svg() const = 0;
|
||||||
|
|
||||||
|
Optional<CSSPixelRect> get_masking_area_of_svg() const;
|
||||||
|
Optional<Gfx::Bitmap::MaskKind> get_mask_type_of_svg() const;
|
||||||
|
RefPtr<Gfx::Bitmap> calculate_mask_of_svg(PaintContext&, CSSPixelRect const& masking_area) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue