Skip to content

Commit 660d6f1

Browse files
Torstennatorgmta
authored andcommitted
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.
1 parent 69650a5 commit 660d6f1

File tree

6 files changed

+76
-0
lines changed

6 files changed

+76
-0
lines changed

Userland/Applications/PixelPaint/ImageEditor.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ Gfx::IntRect ImageEditor::mouse_indicator_rect_y() const
290290

291291
void ImageEditor::second_paint_event(GUI::PaintEvent& event)
292292
{
293+
if (m_active_layer && m_active_layer->mask_type() != Layer::MaskType::None)
294+
m_active_layer->on_second_paint(*this);
295+
293296
if (m_active_tool) {
294297
if (m_show_rulers) {
295298
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&
945948
return new_layer_name.to_deprecated_string();
946949
}
947950

951+
Gfx::IntRect ImageEditor::active_layer_visible_rect()
952+
{
953+
if (!active_layer())
954+
return {};
955+
956+
auto scaled_layer_rect = active_layer()->relative_rect().to_type<float>().scaled(scale(), scale()).to_type<int>().translated(content_rect().location());
957+
auto visible_editor_rect = ruler_visibility() ? subtract_rulers_from_rect(rect()) : rect();
958+
scaled_layer_rect.intersect(visible_editor_rect);
959+
return scaled_layer_rect;
960+
}
961+
948962
}

Userland/Applications/PixelPaint/ImageEditor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ImageEditor final
3737

3838
Layer* active_layer() { return m_active_layer; }
3939
void set_active_layer(Layer*);
40+
Gfx::IntRect active_layer_visible_rect();
4041

4142
ErrorOr<void> add_new_layer_from_selection();
4243
Tool* active_tool() { return m_active_tool; }

Userland/Applications/PixelPaint/Layer.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
#include "Layer.h"
1010
#include "Image.h"
11+
#include "ImageEditor.h"
1112
#include "Selection.h"
1213
#include <AK/RefPtr.h>
1314
#include <AK/Try.h>
15+
#include <LibGUI/Painter.h>
1416
#include <LibGfx/Bitmap.h>
1517
#include <LibGfx/Painter.h>
1618

@@ -45,6 +47,7 @@ ErrorOr<NonnullRefPtr<Layer>> Layer::create_snapshot(Image& image, Layer const&
4547
snapshot->m_mask_bitmap = TRY(layer.mask_bitmap()->clone());
4648
snapshot->m_edit_mode = layer.m_edit_mode;
4749
snapshot->m_mask_type = layer.m_mask_type;
50+
snapshot->m_visible_mask = layer.m_visible_mask;
4851
}
4952

5053
/*
@@ -322,6 +325,7 @@ void Layer::delete_mask()
322325
{
323326
m_mask_bitmap = nullptr;
324327
m_mask_type = MaskType::None;
328+
m_visible_mask = false;
325329
set_edit_mode(EditMode::Content);
326330
update_cached_bitmap();
327331
}
@@ -464,4 +468,35 @@ Layer::MaskType Layer::mask_type()
464468
return m_mask_type;
465469
}
466470

471+
void Layer::on_second_paint(ImageEditor& editor)
472+
{
473+
if (!m_visible_mask || edit_mode() != EditMode::Mask)
474+
return;
475+
476+
auto visible_rect = editor.active_layer_visible_rect();
477+
if (visible_rect.width() == 0 || visible_rect.height() == 0)
478+
return;
479+
480+
GUI::Painter painter(editor);
481+
painter.translate(visible_rect.location());
482+
483+
auto content_offset = editor.content_to_frame_position(location());
484+
auto drawing_cursor_offset = visible_rect.location() - content_offset.to_type<int>();
485+
486+
Gfx::Color editing_mask_color = editor.primary_color();
487+
int mask_alpha;
488+
Gfx::IntPoint mask_coordinates;
489+
490+
for (int y = 0; y < visible_rect.height(); y++) {
491+
for (int x = 0; x < visible_rect.width(); x++) {
492+
mask_coordinates = (Gfx::FloatPoint(drawing_cursor_offset.x() + x, drawing_cursor_offset.y() + y) / editor.scale()).to_type<int>();
493+
mask_alpha = mask_bitmap()->get_pixel(mask_coordinates).alpha();
494+
if (!mask_alpha)
495+
continue;
496+
497+
painter.set_pixel(x, y, editing_mask_color.with_alpha(mask_alpha), true);
498+
}
499+
}
500+
}
501+
467502
}

Userland/Applications/PixelPaint/Layer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
#include <AK/Noncopyable.h>
1414
#include <AK/RefCounted.h>
1515
#include <AK/Weakable.h>
16+
#include <LibGUI/Event.h>
1617
#include <LibGfx/Bitmap.h>
1718
#include <LibGfx/Painter.h>
1819

1920
namespace PixelPaint {
2021

2122
class Image;
23+
class ImageEditor;
2224
class Selection;
2325

2426
class Layer
@@ -56,6 +58,8 @@ class Layer
5658
void apply_mask();
5759
void invert_mask();
5860
void clear_mask();
61+
void set_mask_visibility(bool visible) { m_visible_mask = visible; }
62+
bool mask_visibility() { return m_visible_mask; }
5963

6064
Gfx::Bitmap& get_scratch_edited_bitmap();
6165

@@ -126,6 +130,8 @@ class Layer
126130
return current_color.mixed_with(target_color, mask_intensity);
127131
}
128132

133+
void on_second_paint(ImageEditor&);
134+
129135
private:
130136
Layer(Image&, NonnullRefPtr<Gfx::Bitmap>, DeprecatedString name);
131137

@@ -140,6 +146,7 @@ class Layer
140146

141147
bool m_selected { false };
142148
bool m_visible { true };
149+
bool m_visible_mask { false };
143150

144151
int m_opacity_percent { 100 };
145152

Userland/Applications/PixelPaint/MainWidget.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,20 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
853853
}));
854854
TRY(m_layer_menu->try_add_action(*m_clear_mask_action));
855855

856+
m_toggle_mask_visibility_action = GUI::Action::create_checkable(
857+
"Show Mask", [&](auto&) {
858+
auto* editor = current_image_editor();
859+
VERIFY(editor);
860+
if (!editor->active_layer())
861+
return;
862+
863+
VERIFY(editor->active_layer()->is_masked());
864+
editor->active_layer()->set_mask_visibility(m_toggle_mask_visibility_action->is_checked());
865+
editor->update();
866+
});
867+
868+
TRY(m_layer_menu->try_add_action(*m_toggle_mask_visibility_action));
869+
856870
TRY(m_layer_menu->try_add_separator());
857871

858872
TRY(m_layer_menu->try_add_action(GUI::Action::create(
@@ -1232,6 +1246,8 @@ void MainWidget::set_mask_actions_for_layer(Layer* layer)
12321246
m_clear_mask_action->set_visible(masked);
12331247
m_delete_mask_action->set_visible(masked);
12341248
m_apply_mask_action->set_visible(layer->mask_type() == Layer::MaskType::BasicMask);
1249+
m_toggle_mask_visibility_action->set_visible(layer->mask_type() == Layer::MaskType::EditingMask);
1250+
m_toggle_mask_visibility_action->set_checked(layer->mask_visibility());
12351251
}
12361252

12371253
void MainWidget::open_image(FileSystemAccessClient::File file)
@@ -1373,6 +1389,8 @@ ImageEditor& MainWidget::create_new_editor(NonnullRefPtr<Image> image)
13731389
m_palette_widget->set_primary_color(color);
13741390
if (image_editor.active_tool())
13751391
image_editor.active_tool()->on_primary_color_change(color);
1392+
if (image_editor.active_layer()->mask_visibility())
1393+
image_editor.update();
13761394
};
13771395
image_editor.on_secondary_color_change = [&](Color color) {
13781396
m_palette_widget->set_secondary_color(color);

Userland/Applications/PixelPaint/MainWidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class MainWidget : public GUI::Widget {
117117
RefPtr<GUI::Action> m_add_editing_mask_action;
118118
RefPtr<GUI::Action> m_invert_mask_action;
119119
RefPtr<GUI::Action> m_clear_mask_action;
120+
RefPtr<GUI::Action> m_toggle_mask_visibility_action;
120121

121122
Gfx::IntPoint m_last_image_editor_mouse_position;
122123
};

0 commit comments

Comments
 (0)