summaryrefslogtreecommitdiff
path: root/Userland/Applications/PixelPaint
diff options
context:
space:
mode:
authorDavipb <daviparca@gmail.com>2021-06-20 10:48:43 -0300
committerAndreas Kling <kling@serenityos.org>2021-06-22 11:00:00 +0200
commitd922e35579bd0a51e625fd81aebadc51af3cf7a3 (patch)
tree45b5081d1a4ab8317b698118e94b8aee76e8fba0 /Userland/Applications/PixelPaint
parent0828c75e5762fcbbc601293b6daba2fb95536a9c (diff)
downloadserenity-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.h1
-rw-r--r--Userland/Applications/PixelPaint/Selection.cpp87
-rw-r--r--Userland/Applications/PixelPaint/Selection.h13
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;
};
}