diff --git a/Userland/Applications/PixelPaint/ImageMasking.cpp b/Userland/Applications/PixelPaint/ImageMasking.cpp index 6e490c1dc6..1c65e16147 100644 --- a/Userland/Applications/PixelPaint/ImageMasking.cpp +++ b/Userland/Applications/PixelPaint/ImageMasking.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -159,7 +160,7 @@ void ImageMasking::generate_new_mask() { ensure_reference_mask().release_value_but_fixme_should_propagate_errors(); - if (m_reference_mask.is_null()) + if (m_reference_mask.is_null() || !m_masked_area.has_value()) return; if (m_masking_type == MaskingType::Luminosity) { @@ -172,8 +173,8 @@ void ImageMasking::generate_new_mask() bool has_end_range = max_luminosity_end != max_luminosity_full; Gfx::Color reference_mask_pixel, content_pixel; - for (int y = 0; y < m_reference_mask->height(); y++) { - for (int x = 0; x < m_reference_mask->width(); x++) { + for (int y = m_masked_area->top(); y < m_masked_area->bottom(); y++) { + for (int x = m_masked_area->left(); x < m_masked_area->right(); x++) { reference_mask_pixel = m_reference_mask->get_pixel(x, y); if (!reference_mask_pixel.alpha()) continue; @@ -225,13 +226,13 @@ void ImageMasking::generate_new_mask() Gfx::Color reference_mask_pixel; Gfx::HSV content_pixel_hsv; - for (int y = 0; y < m_reference_mask->height(); y++) { + for (int y = m_masked_area->top(); y <= m_masked_area->bottom(); y++) { auto reference_scanline = m_reference_mask->scanline(y); auto content_scanline = m_editor->active_layer()->content_bitmap().scanline(y); auto mask_scanline = m_editor->active_layer()->mask_bitmap()->scanline(y); fast_u32_fill(mask_scanline, 0, m_reference_mask->physical_width()); - for (int x = 0; x < m_reference_mask->width(); x++) { + for (int x = m_masked_area->left(); x < m_masked_area->right(); x++) { reference_mask_pixel = Color::from_argb(reference_scanline[x]); if (!reference_mask_pixel.alpha()) continue; @@ -268,9 +269,12 @@ void ImageMasking::generate_new_mask() ErrorOr ImageMasking::ensure_reference_mask() { - if (m_reference_mask.is_null()) + if (m_reference_mask.is_null()) { m_reference_mask = TRY(m_editor->active_layer()->mask_bitmap()->clone()); - + m_masked_area = m_editor->active_layer()->editing_mask_bounding_rect(); + if (!m_masked_area.has_value()) + GUI::MessageBox::show(this, "You have to draw a mask first before you can refine the mask details."sv, "Missing mask content"sv, GUI::MessageBox::Type::Information); + } return {}; } diff --git a/Userland/Applications/PixelPaint/ImageMasking.h b/Userland/Applications/PixelPaint/ImageMasking.h index f0e2a018b1..d41e3f2346 100644 --- a/Userland/Applications/PixelPaint/ImageMasking.h +++ b/Userland/Applications/PixelPaint/ImageMasking.h @@ -37,6 +37,7 @@ private: ImageEditor* m_editor { nullptr }; RefPtr m_reference_mask { nullptr }; bool m_did_change = false; + Optional m_masked_area; RefPtr m_full_masking_slider = { nullptr }; RefPtr m_edge_masking_slider = { nullptr }; diff --git a/Userland/Applications/PixelPaint/Layer.cpp b/Userland/Applications/PixelPaint/Layer.cpp index 297499ea52..4c4f803a42 100644 --- a/Userland/Applications/PixelPaint/Layer.cpp +++ b/Userland/Applications/PixelPaint/Layer.cpp @@ -453,6 +453,39 @@ Optional Layer::nonempty_content_bounding_rect() const }; } +Optional Layer::editing_mask_bounding_rect() const +{ + if (mask_type() != MaskType::EditingMask) + return {}; + + Optional min_content_y; + Optional min_content_x; + Optional max_content_y; + Optional max_content_x; + + for (int y = 0; y < m_mask_bitmap->height(); ++y) { + auto scanline = m_mask_bitmap->scanline(y); + for (int x = 0; x < m_mask_bitmap->width(); ++x) { + // Do we have any alpha values? + if (scanline[x] < 0x01000000) + continue; + min_content_x = min(min_content_x.value_or(x), x); + min_content_y = min(min_content_y.value_or(y), y); + max_content_x = max(max_content_x.value_or(x), x); + max_content_y = max(max_content_y.value_or(y), y); + } + } + + if (!min_content_x.has_value()) + return {}; + + return Gfx::IntRect { + *min_content_x, + *min_content_y, + *max_content_x - *min_content_x + 1, + *max_content_y - *min_content_y + 1 + }; +} ErrorOr> Layer::duplicate(DeprecatedString name) { auto duplicated_layer = TRY(Layer::create_snapshot(m_image, *this)); @@ -461,7 +494,7 @@ ErrorOr> Layer::duplicate(DeprecatedString name) return duplicated_layer; } -Layer::MaskType Layer::mask_type() +Layer::MaskType Layer::mask_type() const { if (m_mask_bitmap.is_null()) return MaskType::None; diff --git a/Userland/Applications/PixelPaint/Layer.h b/Userland/Applications/PixelPaint/Layer.h index 382a5c565c..75b86bf502 100644 --- a/Userland/Applications/PixelPaint/Layer.h +++ b/Userland/Applications/PixelPaint/Layer.h @@ -82,6 +82,7 @@ public: ErrorOr scale(Gfx::IntRect const& new_rect, Gfx::Painter::ScalingMode scaling_mode, NotifyClients notify_clients = NotifyClients::Yes); Optional nonempty_content_bounding_rect() const; + Optional editing_mask_bounding_rect() const; ErrorOr set_bitmaps(NonnullRefPtr content, RefPtr mask); @@ -103,7 +104,7 @@ public: void erase_selection(Selection const&); bool is_masked() const { return !m_mask_bitmap.is_null(); } - MaskType mask_type(); + MaskType mask_type() const; enum class EditMode { Content, diff --git a/Userland/Applications/PixelPaint/LevelsDialog.cpp b/Userland/Applications/PixelPaint/LevelsDialog.cpp index bf3feab082..004860614f 100644 --- a/Userland/Applications/PixelPaint/LevelsDialog.cpp +++ b/Userland/Applications/PixelPaint/LevelsDialog.cpp @@ -89,9 +89,10 @@ void LevelsDialog::generate_new_image() Color new_pixel_color; Gfx::StorageFormat storage_format = Gfx::determine_storage_format(m_editor->active_layer()->content_bitmap().format()); auto apply_only_on_mask = m_editor->active_layer()->mask_type() == Layer::MaskType::EditingMask; + auto relevant_area = m_masked_area.value_or({ 0, 0, m_reference_bitmap->width(), m_reference_bitmap->height() }); - for (int x = 0; x < m_reference_bitmap->width(); x++) { - for (int y = 0; y < m_reference_bitmap->height(); y++) { + for (int y = relevant_area.top(); y < relevant_area.bottom(); y++) { + for (int x = relevant_area.left(); x < relevant_area.right(); x++) { current_pixel_color = m_reference_bitmap->get_pixel(x, y); // Check if we can avoid setting pixels as nothing will change when we don't have a mask at x,y. @@ -123,8 +124,10 @@ void LevelsDialog::generate_new_image() ErrorOr LevelsDialog::ensure_reference_bitmap() { - if (m_reference_bitmap.is_null()) + if (m_reference_bitmap.is_null()) { m_reference_bitmap = TRY(m_editor->active_layer()->content_bitmap().clone()); + m_masked_area = m_editor->active_layer()->editing_mask_bounding_rect(); + } return {}; } diff --git a/Userland/Applications/PixelPaint/LevelsDialog.h b/Userland/Applications/PixelPaint/LevelsDialog.h index 83245a47ae..f5beebb5af 100644 --- a/Userland/Applications/PixelPaint/LevelsDialog.h +++ b/Userland/Applications/PixelPaint/LevelsDialog.h @@ -28,6 +28,7 @@ private: RefPtr m_gamma_slider = { nullptr }; bool m_did_change = false; int m_precomputed_color_correction[256]; + Optional m_masked_area; ErrorOr ensure_reference_bitmap(); void generate_new_image();