diff options
author | Tobias Christiansen <tobyase@serenityos.org> | 2022-03-07 21:24:05 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-08 22:07:12 +0100 |
commit | 31a9196bfe94df31c902d9e0da9f2d8be0a20e91 (patch) | |
tree | 40faa88c1949f752fbe7c16aeb4684c331c6d830 /Userland/Applications/PixelPaint | |
parent | b6510f8e76f974d5d3df634b39b7f8d82416ede3 (diff) | |
download | serenity-31a9196bfe94df31c902d9e0da9f2d8be0a20e91.zip |
PixelPaint: Split bitmap() of Layer into {content, display}_bitmap
This is in preparation to support masking of Layers. We now distinguish
between the "display_bitmap" which will be the whole Layer with every
effect applied and the "content_bitmap" which contains the actual
unmodified pixels in the Layer.
Diffstat (limited to 'Userland/Applications/PixelPaint')
14 files changed, 73 insertions, 42 deletions
diff --git a/Userland/Applications/PixelPaint/FilterGallery.cpp b/Userland/Applications/PixelPaint/FilterGallery.cpp index 48844ffa9d..13757c347e 100644 --- a/Userland/Applications/PixelPaint/FilterGallery.cpp +++ b/Userland/Applications/PixelPaint/FilterGallery.cpp @@ -65,7 +65,7 @@ FilterGallery::FilterGallery(GUI::Window* parent_window, ImageEditor* editor) m_config_widget->add_child(*m_selected_filter_config_widget); }; - m_preview_widget->set_bitmap(editor->active_layer()->bitmap().clone().release_value()); + m_preview_widget->set_bitmap(editor->active_layer()->content_bitmap().clone().release_value()); apply_button->on_click = [this](auto) { if (!m_selected_filter) { diff --git a/Userland/Applications/PixelPaint/Filters/Filter.cpp b/Userland/Applications/PixelPaint/Filters/Filter.cpp index 27f0f2d65d..4cd3c708eb 100644 --- a/Userland/Applications/PixelPaint/Filters/Filter.cpp +++ b/Userland/Applications/PixelPaint/Filters/Filter.cpp @@ -31,7 +31,7 @@ void Filter::apply() const if (!m_editor) return; if (auto* layer = m_editor->active_layer()) { - apply(layer->bitmap(), layer->bitmap()); + apply(layer->content_bitmap(), layer->content_bitmap()); layer->did_modify_bitmap(layer->rect()); m_editor->did_complete_action(); } diff --git a/Userland/Applications/PixelPaint/Image.cpp b/Userland/Applications/PixelPaint/Image.cpp index 9a28b60d4c..45edd8770f 100644 --- a/Userland/Applications/PixelPaint/Image.cpp +++ b/Userland/Applications/PixelPaint/Image.cpp @@ -49,7 +49,7 @@ void Image::paint_into(GUI::Painter& painter, Gfx::IntRect const& dest_rect) con continue; auto target = dest_rect.translated(layer.location().x() * scale, layer.location().y() * scale); target.set_size(layer.size().width() * scale, layer.size().height() * scale); - painter.draw_scaled_bitmap(target, layer.bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); + painter.draw_scaled_bitmap(target, layer.display_bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); } } @@ -126,7 +126,8 @@ void Image::serialize_as_json(JsonObjectSerializer<StringBuilder>& json) const MUST(json_layer.add("opacity_percent", layer.opacity_percent())); MUST(json_layer.add("visible", layer.is_visible())); MUST(json_layer.add("selected", layer.is_selected())); - MUST(json_layer.add("bitmap", encode_base64(bmp_dumber.dump(layer.bitmap())))); + // FIXME: Respect mask + MUST(json_layer.add("bitmap", encode_base64(bmp_dumber.dump(layer.display_bitmap())))); } } } @@ -330,7 +331,7 @@ void Image::flatten_all_layers() auto& bottom_layer = m_layers.at(0); - GUI::Painter painter(bottom_layer.bitmap()); + GUI::Painter painter(bottom_layer.content_bitmap()); paint_into(painter, { 0, 0, m_size.width(), m_size.height() }); for (size_t index = m_layers.size() - 1; index > 0; index--) { @@ -351,7 +352,7 @@ void Image::merge_visible_layers() while (index < m_layers.size()) { if (m_layers.at(index).is_visible()) { auto& bottom_layer = m_layers.at(index); - GUI::Painter painter(bottom_layer.bitmap()); + GUI::Painter painter(bottom_layer.content_bitmap()); paint_into(painter, { 0, 0, m_size.width(), m_size.height() }); select_layer(&bottom_layer); index++; @@ -380,8 +381,8 @@ void Image::merge_active_layer_up(Layer& layer) } auto& layer_above = m_layers.at(layer_index + 1); - GUI::Painter painter(layer_above.bitmap()); - painter.draw_scaled_bitmap(rect(), layer.bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); + GUI::Painter painter(layer_above.content_bitmap()); + painter.draw_scaled_bitmap(rect(), layer.display_bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); remove_layer(layer); select_layer(&layer_above); } @@ -397,8 +398,8 @@ void Image::merge_active_layer_down(Layer& layer) } auto& layer_below = m_layers.at(layer_index - 1); - GUI::Painter painter(layer_below.bitmap()); - painter.draw_scaled_bitmap(rect(), layer.bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); + GUI::Painter painter(layer_below.content_bitmap()); + painter.draw_scaled_bitmap(rect(), layer.display_bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); remove_layer(layer); select_layer(&layer_below); } @@ -473,9 +474,10 @@ void ImageUndoCommand::redo() void Image::flip(Gfx::Orientation orientation) { for (auto& layer : m_layers) { - auto flipped = layer.bitmap().flipped(orientation).release_value_but_fixme_should_propagate_errors(); - layer.set_bitmap(*flipped); + auto flipped = layer.content_bitmap().flipped(orientation).release_value_but_fixme_should_propagate_errors(); + layer.set_content_bitmap(*flipped); layer.did_modify_bitmap(rect()); + // FIXME: Respect mask } did_change(); @@ -484,9 +486,10 @@ void Image::flip(Gfx::Orientation orientation) void Image::rotate(Gfx::RotationDirection direction) { for (auto& layer : m_layers) { - auto rotated = layer.bitmap().rotated(direction).release_value_but_fixme_should_propagate_errors(); - layer.set_bitmap(*rotated); + auto rotated = layer.content_bitmap().rotated(direction).release_value_but_fixme_should_propagate_errors(); + layer.set_content_bitmap(*rotated); layer.did_modify_bitmap(rect()); + // FIXME: Respect mask } m_size = { m_size.height(), m_size.width() }; @@ -496,9 +499,10 @@ void Image::rotate(Gfx::RotationDirection direction) void Image::crop(Gfx::IntRect const& cropped_rect) { for (auto& layer : m_layers) { - auto cropped = layer.bitmap().cropped(cropped_rect).release_value_but_fixme_should_propagate_errors(); - layer.set_bitmap(*cropped); + auto cropped = layer.content_bitmap().cropped(cropped_rect).release_value_but_fixme_should_propagate_errors(); + layer.set_content_bitmap(*cropped); layer.did_modify_bitmap(rect()); + // FIXME: Respect mask } m_size = { cropped_rect.width(), cropped_rect.height() }; @@ -512,7 +516,7 @@ Color Image::color_at(Gfx::IntPoint const& point) const if (!layer.is_visible() || !layer.rect().contains(point)) continue; - auto layer_color = layer.bitmap().get_pixel(point); + auto layer_color = layer.display_bitmap().get_pixel(point); float layer_opacity = layer.opacity_percent() / 100.0f; layer_color.set_alpha((u8)(layer_color.alpha() * layer_opacity)); color = color.blend(layer_color); diff --git a/Userland/Applications/PixelPaint/Layer.cpp b/Userland/Applications/PixelPaint/Layer.cpp index 3b974f8593..875bdc58aa 100644 --- a/Userland/Applications/PixelPaint/Layer.cpp +++ b/Userland/Applications/PixelPaint/Layer.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -37,7 +38,7 @@ ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_with_bitmap(Image& image, Nonnul ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_snapshot(Image& image, Layer const& layer) { - auto bitmap = TRY(layer.bitmap().clone()); + auto bitmap = TRY(layer.content_bitmap().clone()); auto snapshot = TRY(try_create_with_bitmap(image, move(bitmap), layer.name())); /* @@ -57,13 +58,15 @@ ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_snapshot(Image& image, Layer con Layer::Layer(Image& image, NonnullRefPtr<Gfx::Bitmap> bitmap, String name) : m_image(image) , m_name(move(name)) - , m_bitmap(move(bitmap)) + , m_content_bitmap(move(bitmap)) + , m_cached_display_bitmap(m_content_bitmap) { } void Layer::did_modify_bitmap(Gfx::IntRect const& rect) { m_image.layer_did_modify_bitmap({}, *this, rect); + update_cached_bitmap(); } void Layer::set_visible(bool visible) @@ -110,12 +113,12 @@ RefPtr<Gfx::Bitmap> Layer::try_copy_bitmap(Selection const& selection) const auto layer_point = image_point - m_location; auto result_point = image_point - selection_rect.top_left(); - if (!m_bitmap->physical_rect().contains(layer_point)) { + if (!m_content_bitmap->physical_rect().contains(layer_point)) { result->set_pixel(result_point, Gfx::Color::Transparent); continue; } - auto pixel = m_bitmap->get_pixel(layer_point); + auto pixel = m_content_bitmap->get_pixel(layer_point); // Widen to int before multiplying to avoid overflow issues auto pixel_alpha = static_cast<int>(pixel.alpha()); @@ -132,11 +135,28 @@ RefPtr<Gfx::Bitmap> Layer::try_copy_bitmap(Selection const& selection) const void Layer::erase_selection(Selection const& selection) { - Gfx::Painter painter { bitmap() }; + Gfx::Painter painter { content_bitmap() }; auto const image_and_selection_intersection = m_image.rect().intersected(selection.bounding_rect()); auto const translated_to_layer_space = image_and_selection_intersection.translated(-location()); painter.clear_rect(translated_to_layer_space, Color::Transparent); did_modify_bitmap(translated_to_layer_space); } +void Layer::set_content_bitmap(NonnullRefPtr<Gfx::Bitmap> bitmap) +{ + m_content_bitmap = move(bitmap); + update_cached_bitmap(); +} + +void Layer::update_cached_bitmap() +{ + if (!is_masked()) { + if (m_content_bitmap.ptr() == m_cached_display_bitmap.ptr()) + return; + + m_cached_display_bitmap = m_content_bitmap; + return; + } +} + } diff --git a/Userland/Applications/PixelPaint/Layer.h b/Userland/Applications/PixelPaint/Layer.h index 64860d867b..41a606a15d 100644 --- a/Userland/Applications/PixelPaint/Layer.h +++ b/Userland/Applications/PixelPaint/Layer.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> * Copyright (c) 2022, the SerenityOS developers. + * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -35,9 +36,11 @@ public: Gfx::IntPoint const& location() const { return m_location; } void set_location(Gfx::IntPoint const& location) { m_location = location; } - Gfx::Bitmap const& bitmap() const { return *m_bitmap; } - Gfx::Bitmap& bitmap() { return *m_bitmap; } - Gfx::IntSize size() const { return bitmap().size(); } + Gfx::Bitmap const& display_bitmap() const { return m_cached_display_bitmap; } + Gfx::Bitmap const& content_bitmap() const { return m_content_bitmap; } + Gfx::Bitmap& content_bitmap() { return m_content_bitmap; } + + Gfx::IntSize size() const { return content_bitmap().size(); } Gfx::IntRect relative_rect() const { return { location(), size() }; } Gfx::IntRect rect() const { return { {}, size() }; } @@ -45,7 +48,7 @@ public: String const& name() const { return m_name; } void set_name(String); - void set_bitmap(NonnullRefPtr<Gfx::Bitmap> bitmap) { m_bitmap = move(bitmap); } + void set_content_bitmap(NonnullRefPtr<Gfx::Bitmap> bitmap); void did_modify_bitmap(Gfx::IntRect const& = {}); @@ -71,12 +74,15 @@ private: String m_name; Gfx::IntPoint m_location; - NonnullRefPtr<Gfx::Bitmap> m_bitmap; + NonnullRefPtr<Gfx::Bitmap> m_content_bitmap; + NonnullRefPtr<Gfx::Bitmap> m_cached_display_bitmap; bool m_selected { false }; bool m_visible { true }; int m_opacity_percent { 100 }; + + void update_cached_bitmap(); }; } diff --git a/Userland/Applications/PixelPaint/LayerListWidget.cpp b/Userland/Applications/PixelPaint/LayerListWidget.cpp index 0bdf7f71ee..b52f4ae86c 100644 --- a/Userland/Applications/PixelPaint/LayerListWidget.cpp +++ b/Userland/Applications/PixelPaint/LayerListWidget.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> * Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org> + * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -128,7 +129,7 @@ void LayerListWidget::paint_event(GUI::PaintEvent& event) } painter.draw_rect(adjusted_rect, palette().color(ColorRole::BaseText)); - painter.draw_scaled_bitmap(inner_thumbnail_rect, layer.bitmap(), layer.bitmap().rect()); + painter.draw_scaled_bitmap(inner_thumbnail_rect, layer.display_bitmap(), layer.display_bitmap().rect()); if (layer.is_visible()) { painter.draw_text(text_rect, layer.name(), Gfx::TextAlignment::CenterLeft, layer.is_selected() ? palette().selection_text() : palette().button_text()); diff --git a/Userland/Applications/PixelPaint/MainWidget.cpp b/Userland/Applications/PixelPaint/MainWidget.cpp index f544db8286..785b140657 100644 --- a/Userland/Applications/PixelPaint/MainWidget.cpp +++ b/Userland/Applications/PixelPaint/MainWidget.cpp @@ -114,7 +114,7 @@ void MainWidget::initialize_menubar(GUI::Window& window) auto image = PixelPaint::Image::try_create_with_size(dialog->image_size()).release_value_but_fixme_should_propagate_errors(); auto bg_layer = PixelPaint::Layer::try_create_with_size(*image, image->size(), "Background").release_value_but_fixme_should_propagate_errors(); image->add_layer(*bg_layer); - bg_layer->bitmap().fill(Color::White); + bg_layer->content_bitmap().fill(Color::White); auto& editor = create_new_editor(*image); auto image_title = dialog->image_name().trim_whitespace(); @@ -630,7 +630,7 @@ void MainWidget::initialize_menubar(GUI::Window& window) if (auto* layer = editor->active_layer()) { Gfx::GenericConvolutionFilter<5> filter; if (auto parameters = PixelPaint::FilterParameters<Gfx::GenericConvolutionFilter<5>>::get(&window)) { - filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); + filter.apply(layer->content_bitmap(), layer->rect(), layer->content_bitmap(), layer->rect(), *parameters); layer->did_modify_bitmap(layer->rect()); editor->did_complete_action(); } @@ -735,7 +735,7 @@ void MainWidget::create_default_image() auto bg_layer = Layer::try_create_with_size(*image, image->size(), "Background").release_value_but_fixme_should_propagate_errors(); image->add_layer(*bg_layer); - bg_layer->bitmap().fill(Color::White); + bg_layer->content_bitmap().fill(Color::White); m_layer_list_widget->set_image(image); diff --git a/Userland/Applications/PixelPaint/Tools/BrushTool.cpp b/Userland/Applications/PixelPaint/Tools/BrushTool.cpp index 3e5786555f..508cd4fd17 100644 --- a/Userland/Applications/PixelPaint/Tools/BrushTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/BrushTool.cpp @@ -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->bitmap(), color_for(layer_event), m_last_position, layer_event.position()); + draw_line(layer->content_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) const int first_draw_opacity = 10; for (int i = 0; i < first_draw_opacity; ++i) - draw_point(layer->bitmap(), color_for(layer_event), layer_event.position()); + draw_point(layer->content_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->bitmap(), color_for(layer_event), m_last_position, layer_event.position()); + draw_line(layer->content_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); diff --git a/Userland/Applications/PixelPaint/Tools/BucketTool.cpp b/Userland/Applications/PixelPaint/Tools/BucketTool.cpp index 380b387728..69155c723d 100644 --- a/Userland/Applications/PixelPaint/Tools/BucketTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/BucketTool.cpp @@ -82,10 +82,10 @@ void BucketTool::on_mousedown(Layer* layer, MouseEvent& event) if (!layer->rect().contains(layer_event.position())) return; - GUI::Painter painter(layer->bitmap()); - auto target_color = layer->bitmap().get_pixel(layer_event.x(), layer_event.y()); + GUI::Painter painter(layer->content_bitmap()); + auto target_color = layer->content_bitmap().get_pixel(layer_event.x(), layer_event.y()); - flood_fill(layer->bitmap(), layer_event.position(), target_color, m_editor->color_for(layer_event), m_threshold); + flood_fill(layer->content_bitmap(), layer_event.position(), target_color, m_editor->color_for(layer_event), m_threshold); layer->did_modify_bitmap(); m_editor->did_complete_action(); diff --git a/Userland/Applications/PixelPaint/Tools/EllipseTool.cpp b/Userland/Applications/PixelPaint/Tools/EllipseTool.cpp index 998ee2cb49..dd15973593 100644 --- a/Userland/Applications/PixelPaint/Tools/EllipseTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/EllipseTool.cpp @@ -67,7 +67,7 @@ void EllipseTool::on_mouseup(Layer* layer, MouseEvent& event) return; if (event.layer_event().button() == m_drawing_button) { - GUI::Painter painter(layer->bitmap()); + GUI::Painter painter(layer->content_bitmap()); draw_using(painter, m_ellipse_start_position, m_ellipse_end_position, m_thickness); m_drawing_button = GUI::MouseButton::None; layer->did_modify_bitmap(); diff --git a/Userland/Applications/PixelPaint/Tools/LineTool.cpp b/Userland/Applications/PixelPaint/Tools/LineTool.cpp index 425f74ca64..65dd442a46 100644 --- a/Userland/Applications/PixelPaint/Tools/LineTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/LineTool.cpp @@ -60,7 +60,7 @@ 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->bitmap()); + GUI::Painter painter(layer->content_bitmap()); painter.draw_line(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(); diff --git a/Userland/Applications/PixelPaint/Tools/PickerTool.cpp b/Userland/Applications/PixelPaint/Tools/PickerTool.cpp index 54fa83c2dd..08b4309e75 100644 --- a/Userland/Applications/PixelPaint/Tools/PickerTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/PickerTool.cpp @@ -24,7 +24,7 @@ void PickerTool::on_mousedown(Layer* layer, MouseEvent& event) } else { if (!layer || !layer->rect().contains(position)) return; - color = layer->bitmap().get_pixel(position); + color = layer->content_bitmap().get_pixel(position); } // We picked a transparent pixel, do nothing. diff --git a/Userland/Applications/PixelPaint/Tools/RectangleTool.cpp b/Userland/Applications/PixelPaint/Tools/RectangleTool.cpp index b24d024a17..3e01c10ee8 100644 --- a/Userland/Applications/PixelPaint/Tools/RectangleTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/RectangleTool.cpp @@ -70,7 +70,7 @@ void RectangleTool::on_mouseup(Layer* layer, MouseEvent& event) return; if (event.layer_event().button() == m_drawing_button) { - GUI::Painter painter(layer->bitmap()); + GUI::Painter painter(layer->content_bitmap()); draw_using(painter, m_rectangle_start_position, m_rectangle_end_position, m_thickness); m_drawing_button = GUI::MouseButton::None; layer->did_modify_bitmap(); diff --git a/Userland/Applications/PixelPaint/Tools/SprayTool.cpp b/Userland/Applications/PixelPaint/Tools/SprayTool.cpp index 0d099f0d36..f21bb79d28 100644 --- a/Userland/Applications/PixelPaint/Tools/SprayTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/SprayTool.cpp @@ -40,7 +40,7 @@ void SprayTool::paint_it() if (!layer) return; - auto& bitmap = layer->bitmap(); + auto& bitmap = layer->content_bitmap(); GUI::Painter painter(bitmap); VERIFY(bitmap.bpp() == 32); const double minimal_radius = 2; |