Ver código fonte

PixelPaint: Add function to visualize editing-masks

This patch adds a function to make the editing-eask visible while
beeing in mask-mode so that the user can see which parts are covered
by the masks and can therefore be modified by other tools that support
editing masks.
Torstennator 2 anos atrás
pai
commit
660d6f171c

+ 14 - 0
Userland/Applications/PixelPaint/ImageEditor.cpp

@@ -290,6 +290,9 @@ Gfx::IntRect ImageEditor::mouse_indicator_rect_y() const
 
 void ImageEditor::second_paint_event(GUI::PaintEvent& event)
 {
+    if (m_active_layer && m_active_layer->mask_type() != Layer::MaskType::None)
+        m_active_layer->on_second_paint(*this);
+
     if (m_active_tool) {
         if (m_show_rulers) {
             auto clipped_event = GUI::PaintEvent(subtract_rulers_from_rect(event.rect()), event.window_size());
@@ -945,4 +948,15 @@ DeprecatedString ImageEditor::generate_unique_layer_name(DeprecatedString const&
     return new_layer_name.to_deprecated_string();
 }
 
+Gfx::IntRect ImageEditor::active_layer_visible_rect()
+{
+    if (!active_layer())
+        return {};
+
+    auto scaled_layer_rect = active_layer()->relative_rect().to_type<float>().scaled(scale(), scale()).to_type<int>().translated(content_rect().location());
+    auto visible_editor_rect = ruler_visibility() ? subtract_rulers_from_rect(rect()) : rect();
+    scaled_layer_rect.intersect(visible_editor_rect);
+    return scaled_layer_rect;
+}
+
 }

+ 1 - 0
Userland/Applications/PixelPaint/ImageEditor.h

@@ -37,6 +37,7 @@ public:
 
     Layer* active_layer() { return m_active_layer; }
     void set_active_layer(Layer*);
+    Gfx::IntRect active_layer_visible_rect();
 
     ErrorOr<void> add_new_layer_from_selection();
     Tool* active_tool() { return m_active_tool; }

+ 35 - 0
Userland/Applications/PixelPaint/Layer.cpp

@@ -8,9 +8,11 @@
 
 #include "Layer.h"
 #include "Image.h"
+#include "ImageEditor.h"
 #include "Selection.h"
 #include <AK/RefPtr.h>
 #include <AK/Try.h>
+#include <LibGUI/Painter.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Painter.h>
 
@@ -45,6 +47,7 @@ ErrorOr<NonnullRefPtr<Layer>> Layer::create_snapshot(Image& image, Layer const&
         snapshot->m_mask_bitmap = TRY(layer.mask_bitmap()->clone());
         snapshot->m_edit_mode = layer.m_edit_mode;
         snapshot->m_mask_type = layer.m_mask_type;
+        snapshot->m_visible_mask = layer.m_visible_mask;
     }
 
     /*
@@ -322,6 +325,7 @@ void Layer::delete_mask()
 {
     m_mask_bitmap = nullptr;
     m_mask_type = MaskType::None;
+    m_visible_mask = false;
     set_edit_mode(EditMode::Content);
     update_cached_bitmap();
 }
@@ -464,4 +468,35 @@ Layer::MaskType Layer::mask_type()
     return m_mask_type;
 }
 
+void Layer::on_second_paint(ImageEditor& editor)
+{
+    if (!m_visible_mask || edit_mode() != EditMode::Mask)
+        return;
+
+    auto visible_rect = editor.active_layer_visible_rect();
+    if (visible_rect.width() == 0 || visible_rect.height() == 0)
+        return;
+
+    GUI::Painter painter(editor);
+    painter.translate(visible_rect.location());
+
+    auto content_offset = editor.content_to_frame_position(location());
+    auto drawing_cursor_offset = visible_rect.location() - content_offset.to_type<int>();
+
+    Gfx::Color editing_mask_color = editor.primary_color();
+    int mask_alpha;
+    Gfx::IntPoint mask_coordinates;
+
+    for (int y = 0; y < visible_rect.height(); y++) {
+        for (int x = 0; x < visible_rect.width(); x++) {
+            mask_coordinates = (Gfx::FloatPoint(drawing_cursor_offset.x() + x, drawing_cursor_offset.y() + y) / editor.scale()).to_type<int>();
+            mask_alpha = mask_bitmap()->get_pixel(mask_coordinates).alpha();
+            if (!mask_alpha)
+                continue;
+
+            painter.set_pixel(x, y, editing_mask_color.with_alpha(mask_alpha), true);
+        }
+    }
+}
+
 }

+ 7 - 0
Userland/Applications/PixelPaint/Layer.h

@@ -13,12 +13,14 @@
 #include <AK/Noncopyable.h>
 #include <AK/RefCounted.h>
 #include <AK/Weakable.h>
+#include <LibGUI/Event.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Painter.h>
 
 namespace PixelPaint {
 
 class Image;
+class ImageEditor;
 class Selection;
 
 class Layer
@@ -56,6 +58,8 @@ public:
     void apply_mask();
     void invert_mask();
     void clear_mask();
+    void set_mask_visibility(bool visible) { m_visible_mask = visible; }
+    bool mask_visibility() { return m_visible_mask; }
 
     Gfx::Bitmap& get_scratch_edited_bitmap();
 
@@ -126,6 +130,8 @@ public:
         return current_color.mixed_with(target_color, mask_intensity);
     }
 
+    void on_second_paint(ImageEditor&);
+
 private:
     Layer(Image&, NonnullRefPtr<Gfx::Bitmap>, DeprecatedString name);
 
@@ -140,6 +146,7 @@ private:
 
     bool m_selected { false };
     bool m_visible { true };
+    bool m_visible_mask { false };
 
     int m_opacity_percent { 100 };
 

+ 18 - 0
Userland/Applications/PixelPaint/MainWidget.cpp

@@ -853,6 +853,20 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
         }));
     TRY(m_layer_menu->try_add_action(*m_clear_mask_action));
 
+    m_toggle_mask_visibility_action = GUI::Action::create_checkable(
+        "Show Mask", [&](auto&) {
+            auto* editor = current_image_editor();
+            VERIFY(editor);
+            if (!editor->active_layer())
+                return;
+
+            VERIFY(editor->active_layer()->is_masked());
+            editor->active_layer()->set_mask_visibility(m_toggle_mask_visibility_action->is_checked());
+            editor->update();
+        });
+
+    TRY(m_layer_menu->try_add_action(*m_toggle_mask_visibility_action));
+
     TRY(m_layer_menu->try_add_separator());
 
     TRY(m_layer_menu->try_add_action(GUI::Action::create(
@@ -1232,6 +1246,8 @@ void MainWidget::set_mask_actions_for_layer(Layer* layer)
     m_clear_mask_action->set_visible(masked);
     m_delete_mask_action->set_visible(masked);
     m_apply_mask_action->set_visible(layer->mask_type() == Layer::MaskType::BasicMask);
+    m_toggle_mask_visibility_action->set_visible(layer->mask_type() == Layer::MaskType::EditingMask);
+    m_toggle_mask_visibility_action->set_checked(layer->mask_visibility());
 }
 
 void MainWidget::open_image(FileSystemAccessClient::File file)
@@ -1373,6 +1389,8 @@ ImageEditor& MainWidget::create_new_editor(NonnullRefPtr<Image> image)
         m_palette_widget->set_primary_color(color);
         if (image_editor.active_tool())
             image_editor.active_tool()->on_primary_color_change(color);
+        if (image_editor.active_layer()->mask_visibility())
+            image_editor.update();
     };
     image_editor.on_secondary_color_change = [&](Color color) {
         m_palette_widget->set_secondary_color(color);

+ 1 - 0
Userland/Applications/PixelPaint/MainWidget.h

@@ -117,6 +117,7 @@ private:
     RefPtr<GUI::Action> m_add_editing_mask_action;
     RefPtr<GUI::Action> m_invert_mask_action;
     RefPtr<GUI::Action> m_clear_mask_action;
+    RefPtr<GUI::Action> m_toggle_mask_visibility_action;
 
     Gfx::IntPoint m_last_image_editor_mouse_position;
 };