diff options
author | Davipb <daviparca@gmail.com> | 2021-06-20 10:48:43 -0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-22 11:00:00 +0200 |
commit | d922e35579bd0a51e625fd81aebadc51af3cf7a3 (patch) | |
tree | 45b5081d1a4ab8317b698118e94b8aee76e8fba0 /Userland/Applications/PixelPaint | |
parent | 0828c75e5762fcbbc601293b6daba2fb95536a9c (diff) | |
download | serenity-d922e35579bd0a51e625fd81aebadc51af3cf7a3.zip |
PixelPaint: Use Mask internally in Selection
While the external API has not changed, this will allow us to have
non-rectangular selections in the future.
Diffstat (limited to 'Userland/Applications/PixelPaint')
-rw-r--r-- | Userland/Applications/PixelPaint/ImageEditor.h | 1 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Selection.cpp | 87 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Selection.h | 13 |
3 files changed, 80 insertions, 21 deletions
diff --git a/Userland/Applications/PixelPaint/ImageEditor.h b/Userland/Applications/PixelPaint/ImageEditor.h index 8b25b71758..88803ce1ef 100644 --- a/Userland/Applications/PixelPaint/ImageEditor.h +++ b/Userland/Applications/PixelPaint/ImageEditor.h @@ -42,6 +42,7 @@ public: Layer* layer_at_editor_position(Gfx::IntPoint const&); + float scale() const { return m_scale; } void scale_centered_on_position(Gfx::IntPoint const&, float); void reset_scale_and_position(); void scale_by(float); diff --git a/Userland/Applications/PixelPaint/Selection.cpp b/Userland/Applications/PixelPaint/Selection.cpp index 4316f960f9..776999069c 100644 --- a/Userland/Applications/PixelPaint/Selection.cpp +++ b/Userland/Applications/PixelPaint/Selection.cpp @@ -14,7 +14,7 @@ constexpr int marching_ant_length = 4; void Selection::paint(Gfx::Painter& painter) { - draw_marching_ants(painter, m_editor.image_rect_to_editor_rect(m_rect).to_type<int>()); + draw_marching_ants(painter, m_mask); } Selection::Selection(ImageEditor& editor) @@ -31,38 +31,91 @@ Selection::Selection(ImageEditor& editor) void Selection::draw_marching_ants(Gfx::Painter& painter, Gfx::IntRect const& rect) const { - int offset = m_marching_ants_offset; - - auto draw_pixel = [&](int x, int y) { - if ((offset % (marching_ant_length * 2)) < marching_ant_length) { - painter.set_pixel(x, y, Color::Black); - } else { - painter.set_pixel(x, y, Color::White); - } - offset++; - }; - // Top line for (int x = rect.left(); x <= rect.right(); ++x) - draw_pixel(x, rect.top()); + draw_marching_ants_pixel(painter, x, rect.top()); // Right line for (int y = rect.top() + 1; y <= rect.bottom(); ++y) - draw_pixel(rect.right(), y); + draw_marching_ants_pixel(painter, rect.right(), y); // Bottom line for (int x = rect.right() - 1; x >= rect.left(); --x) - draw_pixel(x, rect.bottom()); + draw_marching_ants_pixel(painter, x, rect.bottom()); // Left line for (int y = rect.bottom() - 1; y > rect.top(); --y) - draw_pixel(rect.left(), y); + draw_marching_ants_pixel(painter, rect.left(), y); +} + +void Selection::draw_marching_ants(Gfx::Painter& painter, Mask const& mask) const +{ + // If the zoom is < 100%, we can skip pixels to save a lot of time drawing the ants + int step = max(1, (int)floorf(1.0f / m_editor.scale())); + + // Only check the visible selection area when drawing for performance + auto rect = m_editor.rect(); + rect = Gfx::enclosing_int_rect(m_editor.editor_rect_to_image_rect(rect)); + rect.inflate(step * 2, step * 2); // prevent borders from having visible ants if the selection extends beyond it + + // Scan the image horizontally to find vertical borders + for (int y = rect.top(); y <= rect.bottom(); y += step) { + + bool previous_selected = false; + for (int x = rect.left(); x <= rect.right(); x += step) { + bool this_selected = mask.get(x, y) > 0; + + if (this_selected != previous_selected) { + Gfx::IntRect image_pixel { x, y, 1, 1 }; + auto pixel = m_editor.image_rect_to_editor_rect(image_pixel).to_type<int>(); + auto end = max(pixel.top(), pixel.bottom()); // for when the zoom is < 100% + + for (int pixel_y = pixel.top(); pixel_y <= end; pixel_y++) { + draw_marching_ants_pixel(painter, pixel.left(), pixel_y); + } + } + + previous_selected = this_selected; + } + } + + // Scan the image vertically to find horizontal borders + for (int x = rect.left(); x <= rect.right(); x += step) { + + bool previous_selected = false; + for (int y = rect.top(); y <= rect.bottom(); y += step) { + bool this_selected = mask.get(x, y) > 0; + + if (this_selected != previous_selected) { + Gfx::IntRect image_pixel { x, y, 1, 1 }; + auto pixel = m_editor.image_rect_to_editor_rect(image_pixel).to_type<int>(); + auto end = max(pixel.left(), pixel.right()); // for when the zoom is < 100% + + for (int pixel_x = pixel.left(); pixel_x <= end; pixel_x++) { + draw_marching_ants_pixel(painter, pixel_x, pixel.top()); + } + } + + previous_selected = this_selected; + } + } } void Selection::clear() { - m_rect = {}; + m_mask = {}; m_editor.update(); } +void Selection::draw_marching_ants_pixel(Gfx::Painter& painter, int x, int y) const +{ + int pattern_index = x + y + m_marching_ants_offset; + + if (pattern_index % (marching_ant_length * 2) < marching_ant_length) { + painter.set_pixel(x, y, Color::Black); + } else { + painter.set_pixel(x, y, Color::White); + } +} + } diff --git a/Userland/Applications/PixelPaint/Selection.h b/Userland/Applications/PixelPaint/Selection.h index 8e4d729e74..f521421f7f 100644 --- a/Userland/Applications/PixelPaint/Selection.h +++ b/Userland/Applications/PixelPaint/Selection.h @@ -9,6 +9,8 @@ #include <LibCore/Timer.h> #include <LibGfx/Rect.h> +#include "Mask.h" + namespace PixelPaint { class ImageEditor; @@ -18,24 +20,27 @@ class Selection { public: explicit Selection(ImageEditor&); - bool is_empty() const { return m_rect.is_empty(); } + bool is_empty() const { return m_mask.is_null(); } void clear(); - void set(Gfx::IntRect const& rect) { m_rect = rect; } - Gfx::IntRect bounding_rect() const { return m_rect; } + void set(Gfx::IntRect const& rect) { m_mask = Mask::full(rect); } + Gfx::IntRect bounding_rect() const { return m_mask.bounding_rect(); } void paint(Gfx::Painter&); void draw_marching_ants(Gfx::Painter&, Gfx::IntRect const&) const; + void draw_marching_ants(Gfx::Painter&, Mask const&) const; void begin_interactive_selection() { m_in_interactive_selection = true; } void end_interactive_selection() { m_in_interactive_selection = false; } private: ImageEditor& m_editor; - Gfx::IntRect m_rect; + Mask m_mask; RefPtr<Core::Timer> m_marching_ants_timer; int m_marching_ants_offset { 0 }; bool m_in_interactive_selection { false }; + + void draw_marching_ants_pixel(Gfx::Painter&, int x, int y) const; }; } |