diff options
author | MacDue <macdue@dueutil.tech> | 2023-04-06 20:52:19 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2023-04-09 18:42:45 +0200 |
commit | 92d9b6edb898d380f3750801d37af47257605c2f (patch) | |
tree | 1917b89d22526b739de0ec746494166117aa7743 /Userland/Libraries | |
parent | ef58062f712283ed5e329a90e7256a7c0d060ab9 (diff) | |
download | serenity-92d9b6edb898d380f3750801d37af47257605c2f.zip |
LibWeb: Add simple canvas path clipper
This adds CanvasPathClipper and ScopedCanvasPathClip. These allow
clipping the canvas by some arbitrary path.
This initial implementation is fairly naive, with a good few
allocations, though this can probably be improved in future.
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibWeb/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.cpp | 46 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.h | 65 |
3 files changed, 112 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index a8909938a4..fc7f380299 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -206,6 +206,7 @@ set(SOURCES HTML/BrowsingContextGroup.cpp HTML/Canvas/CanvasDrawImage.cpp HTML/Canvas/CanvasPath.cpp + HTML/Canvas/CanvasPathClipper.cpp HTML/Canvas/CanvasState.cpp HTML/CanvasGradient.cpp HTML/CanvasPattern.cpp diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.cpp b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.cpp new file mode 100644 index 0000000000..1798656067 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023, MacDue <macdue@dueutil.tech> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibGfx/AntiAliasingPainter.h> +#include <LibWeb/HTML/Canvas/CanvasPathClipper.h> + +namespace Web::HTML { + +// FIXME: This pretty naive, we should be able to cut down the allocations here +// (especially for the paint style which is a bit sad). + +ErrorOr<CanvasPathClipper> CanvasPathClipper::create(Gfx::Painter& painter, CanvasClip const& canvas_clip) +{ + auto bounding_box = enclosing_int_rect(canvas_clip.path.bounding_box()); + Gfx::IntRect actual_save_rect {}; + auto maybe_bitmap = painter.get_region_bitmap(bounding_box, Gfx::BitmapFormat::BGRA8888, actual_save_rect); + RefPtr<Gfx::Bitmap> saved_clip_region; + if (!maybe_bitmap.is_error()) { + saved_clip_region = maybe_bitmap.release_value(); + } else if (actual_save_rect.is_empty()) { + // This is okay, no need to report an error. + } else { + return maybe_bitmap.release_error(); + } + painter.save(); + painter.add_clip_rect(bounding_box); + return CanvasPathClipper(move(saved_clip_region), bounding_box, canvas_clip); +} + +ErrorOr<void> CanvasPathClipper::apply_clip(Gfx::Painter& painter) +{ + painter.restore(); + if (!m_saved_clip_region) + return {}; + Gfx::IntRect actual_save_rect {}; + auto clip_area = TRY(painter.get_region_bitmap(m_bounding_box, Gfx::BitmapFormat::BGRA8888, actual_save_rect)); + painter.blit(actual_save_rect.location(), *m_saved_clip_region, m_saved_clip_region->rect(), 1.0f, false); + Gfx::AntiAliasingPainter aa_painter { painter }; + auto fill_offset = m_bounding_box.location() - actual_save_rect.location(); + aa_painter.fill_path(m_canvas_clip.path, TRY(Gfx::BitmapPaintStyle::create(clip_area, fill_offset)), m_canvas_clip.winding_rule); + return {}; +} +} diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.h new file mode 100644 index 0000000000..e9daa29a0d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathClipper.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, MacDue <macdue@dueutil.tech> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibGfx/Painter.h> +#include <LibGfx/Path.h> + +namespace Web::HTML { + +struct CanvasClip { + Gfx::Path path; + Gfx::Painter::WindingRule winding_rule; +}; + +class CanvasPathClipper { +public: + static ErrorOr<CanvasPathClipper> create(Gfx::Painter&, CanvasClip const& canvas_clip); + ErrorOr<void> apply_clip(Gfx::Painter& painter); + +private: + CanvasPathClipper(RefPtr<Gfx::Bitmap const> saved_clip_region, Gfx::IntRect bounding_box, CanvasClip const& canvas_clip) + : m_saved_clip_region(saved_clip_region) + , m_bounding_box(bounding_box) + , m_canvas_clip(canvas_clip) + { + } + + RefPtr<Gfx::Bitmap const> m_saved_clip_region; + Gfx::IntRect m_bounding_box; + CanvasClip const& m_canvas_clip; +}; + +class ScopedCanvasPathClip { + AK_MAKE_NONMOVABLE(ScopedCanvasPathClip); + AK_MAKE_NONCOPYABLE(ScopedCanvasPathClip); + +public: + ScopedCanvasPathClip(Gfx::Painter& painter, Optional<CanvasClip> const& canvas_clip) + : m_painter(painter) + { + if (canvas_clip.has_value()) { + auto clipper = CanvasPathClipper::create(painter, *canvas_clip); + if (!clipper.is_error()) + m_canvas_clipper = clipper.release_value(); + else + dbgln("CRC2D Error: Failed to apply canvas clip path: {}", clipper.error()); + } + } + + ~ScopedCanvasPathClip() + { + if (m_canvas_clipper.has_value()) + m_canvas_clipper->apply_clip(m_painter).release_value_but_fixme_should_propagate_errors(); + } + +private: + Gfx::Painter& m_painter; + Optional<CanvasPathClipper> m_canvas_clipper; +}; + +} |