mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
PixelPaint: Limit editing tools to selection
This effectively creates a double-buffer for tools to use when modifying the layer's bitmap (content or mask). Once the changes have been made the tool reports to the layer that it has made changes along with a Rect of the changed region. The layer will then merge the changes from the scratch image to the real bitmap. This merge is done as follows: If a given pixel is inside the selected region, the pixel from the scratch bitmap is copied to the real bitmap. If the pixel is not inside the selected region, the pixel from the real bitmap is copied to the scratch bitmap. As an optimization, when there is no selection active, the new method for getting the scratch bitmap will return the real bitmap and no merging will need to take place.
This commit is contained in:
parent
7c33f8f7df
commit
0d7d759095
Notes:
sideshowbarker
2024-07-17 09:49:33 +09:00
Author: https://github.com/tslater2006 Commit: https://github.com/SerenityOS/serenity/commit/0d7d759095 Pull-request: https://github.com/SerenityOS/serenity/pull/15755
8 changed files with 51 additions and 14 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -65,6 +66,21 @@ Layer::Layer(Image& image, NonnullRefPtr<Gfx::Bitmap> bitmap, String name)
|
|||
|
||||
void Layer::did_modify_bitmap(Gfx::IntRect const& rect)
|
||||
{
|
||||
if (!m_scratch_edited_bitmap.is_null()) {
|
||||
for (int y = 0; y < rect.height(); ++y) {
|
||||
for (int x = 0; x < rect.width(); ++x) {
|
||||
Gfx::IntPoint next_point = { rect.left() + x, rect.top() + y };
|
||||
if (!m_scratch_edited_bitmap->rect().contains(next_point))
|
||||
continue;
|
||||
|
||||
if (this->image().selection().is_selected(next_point.translated(this->location())))
|
||||
currently_edited_bitmap().set_pixel(next_point, m_scratch_edited_bitmap->get_pixel(next_point));
|
||||
else
|
||||
m_scratch_edited_bitmap->set_pixel(next_point, currently_edited_bitmap().get_pixel(next_point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_image.layer_did_modify_bitmap({}, *this, rect);
|
||||
update_cached_bitmap();
|
||||
}
|
||||
|
@ -93,6 +109,21 @@ void Layer::set_name(String name)
|
|||
m_image.layer_did_modify_properties({}, *this);
|
||||
}
|
||||
|
||||
Gfx::Bitmap& Layer::get_scratch_edited_bitmap()
|
||||
{
|
||||
if (this->image().selection().is_empty()) {
|
||||
m_scratch_edited_bitmap = nullptr;
|
||||
return currently_edited_bitmap();
|
||||
}
|
||||
|
||||
if (!m_scratch_edited_bitmap.is_null())
|
||||
return *m_scratch_edited_bitmap;
|
||||
|
||||
m_scratch_edited_bitmap = MUST(currently_edited_bitmap().clone());
|
||||
|
||||
return *m_scratch_edited_bitmap;
|
||||
}
|
||||
|
||||
RefPtr<Gfx::Bitmap> Layer::try_copy_bitmap(Selection const& selection) const
|
||||
{
|
||||
if (selection.is_empty()) {
|
||||
|
@ -158,6 +189,7 @@ ErrorOr<void> Layer::try_set_bitmaps(NonnullRefPtr<Gfx::Bitmap> content, RefPtr<
|
|||
|
||||
m_content_bitmap = move(content);
|
||||
m_mask_bitmap = move(mask);
|
||||
m_scratch_edited_bitmap = nullptr;
|
||||
update_cached_bitmap();
|
||||
return {};
|
||||
}
|
||||
|
@ -274,7 +306,7 @@ void Layer::set_edit_mode(Layer::EditMode mode)
|
|||
{
|
||||
if (m_edit_mode == mode)
|
||||
return;
|
||||
|
||||
m_scratch_edited_bitmap = nullptr;
|
||||
m_edit_mode = mode;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -45,6 +46,7 @@ public:
|
|||
Gfx::Bitmap* mask_bitmap() { return m_mask_bitmap; }
|
||||
|
||||
void create_mask();
|
||||
Gfx::Bitmap& get_scratch_edited_bitmap();
|
||||
|
||||
Gfx::IntSize size() const { return content_bitmap().size(); }
|
||||
|
||||
|
@ -102,6 +104,7 @@ private:
|
|||
String m_name;
|
||||
Gfx::IntPoint m_location;
|
||||
NonnullRefPtr<Gfx::Bitmap> m_content_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_scratch_edited_bitmap { nullptr };
|
||||
RefPtr<Gfx::Bitmap> m_mask_bitmap { nullptr };
|
||||
NonnullRefPtr<Gfx::Bitmap> m_cached_display_bitmap;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
|||
|
||||
// Shift+Click draws a line from the last position to current one.
|
||||
if (layer_event.shift() && m_has_clicked) {
|
||||
draw_line(layer->currently_edited_bitmap(), color_for(layer_event), m_last_position, layer_event.position());
|
||||
draw_line(layer->get_scratch_edited_bitmap(), color_for(layer_event), m_last_position, layer_event.position());
|
||||
auto modified_rect = Gfx::IntRect::from_two_points(m_last_position, layer_event.position()).inflated(m_size * 2, m_size * 2);
|
||||
layer->did_modify_bitmap(modified_rect);
|
||||
m_last_position = layer_event.position();
|
||||
|
@ -39,7 +39,7 @@ void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
|||
int const first_draw_opacity = 10;
|
||||
|
||||
for (int i = 0; i < first_draw_opacity; ++i)
|
||||
draw_point(layer->currently_edited_bitmap(), color_for(layer_event), layer_event.position());
|
||||
draw_point(layer->get_scratch_edited_bitmap(), color_for(layer_event), layer_event.position());
|
||||
|
||||
layer->did_modify_bitmap(Gfx::IntRect::centered_on(layer_event.position(), Gfx::IntSize { m_size * 2, m_size * 2 }));
|
||||
m_last_position = layer_event.position();
|
||||
|
@ -55,7 +55,7 @@ void BrushTool::on_mousemove(Layer* layer, MouseEvent& event)
|
|||
if (!(layer_event.buttons() & GUI::MouseButton::Primary || layer_event.buttons() & GUI::MouseButton::Secondary))
|
||||
return;
|
||||
|
||||
draw_line(layer->currently_edited_bitmap(), color_for(layer_event), m_last_position, layer_event.position());
|
||||
draw_line(layer->get_scratch_edited_bitmap(), color_for(layer_event), m_last_position, layer_event.position());
|
||||
|
||||
auto modified_rect = Gfx::IntRect::from_two_points(m_last_position, layer_event.position()).inflated(m_size * 2, m_size * 2);
|
||||
|
||||
|
|
|
@ -50,11 +50,11 @@ void BucketTool::on_mousedown(Layer* layer, MouseEvent& event)
|
|||
if (!layer->rect().contains(layer_event.position()))
|
||||
return;
|
||||
|
||||
GUI::Painter painter(layer->currently_edited_bitmap());
|
||||
GUI::Painter painter(layer->get_scratch_edited_bitmap());
|
||||
|
||||
flood_fill(layer->currently_edited_bitmap(), layer_event.position(), m_editor->color_for(layer_event), m_threshold);
|
||||
flood_fill(layer->get_scratch_edited_bitmap(), layer_event.position(), m_editor->color_for(layer_event), m_threshold);
|
||||
|
||||
layer->did_modify_bitmap();
|
||||
layer->did_modify_bitmap(layer->get_scratch_edited_bitmap().rect());
|
||||
m_editor->did_complete_action(tool_name());
|
||||
}
|
||||
|
||||
|
|
|
@ -84,10 +84,10 @@ void EllipseTool::on_mouseup(Layer* layer, MouseEvent& event)
|
|||
return;
|
||||
|
||||
if (event.layer_event().button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer->currently_edited_bitmap());
|
||||
GUI::Painter painter(layer->get_scratch_edited_bitmap());
|
||||
draw_using(painter, m_ellipse_start_position, m_ellipse_end_position, m_thickness);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
layer->did_modify_bitmap();
|
||||
layer->did_modify_bitmap(layer->get_scratch_edited_bitmap().rect());
|
||||
m_editor->update();
|
||||
m_editor->did_complete_action(tool_name());
|
||||
}
|
||||
|
|
|
@ -76,10 +76,11 @@ void LineTool::on_mouseup(Layer* layer, MouseEvent& event)
|
|||
|
||||
auto& layer_event = event.layer_event();
|
||||
if (layer_event.button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer->currently_edited_bitmap());
|
||||
GUI::Painter painter(layer->get_scratch_edited_bitmap());
|
||||
draw_using(painter, m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
layer->did_modify_bitmap();
|
||||
auto modified_rect = Gfx::IntRect::from_two_points(m_line_start_position, m_line_end_position).inflated(m_thickness * 2, m_thickness * 2);
|
||||
layer->did_modify_bitmap(modified_rect);
|
||||
m_editor->update();
|
||||
m_editor->did_complete_action(tool_name());
|
||||
}
|
||||
|
|
|
@ -87,10 +87,11 @@ void RectangleTool::on_mouseup(Layer* layer, MouseEvent& event)
|
|||
return;
|
||||
|
||||
if (event.layer_event().button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer->currently_edited_bitmap());
|
||||
GUI::Painter painter(layer->get_scratch_edited_bitmap());
|
||||
draw_using(painter, m_rectangle_start_position, m_rectangle_end_position, m_thickness, m_corner_radius);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
layer->did_modify_bitmap();
|
||||
auto modified_rect = Gfx::IntRect::from_two_points(m_rectangle_start_position, m_rectangle_end_position).inflated(m_thickness * 2, m_thickness * 2);
|
||||
layer->did_modify_bitmap(modified_rect);
|
||||
m_editor->update();
|
||||
m_editor->did_complete_action(tool_name());
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ void SprayTool::paint_it()
|
|||
if (!layer)
|
||||
return;
|
||||
|
||||
auto& bitmap = layer->currently_edited_bitmap();
|
||||
auto& bitmap = layer->get_scratch_edited_bitmap();
|
||||
GUI::Painter painter(bitmap);
|
||||
VERIFY(bitmap.bpp() == 32);
|
||||
double const minimal_radius = 2;
|
||||
|
|
Loading…
Reference in a new issue