mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
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.
This commit is contained in:
parent
0828c75e57
commit
d922e35579
Notes:
sideshowbarker
2024-07-18 11:55:59 +09:00
Author: https://github.com/Davipb Commit: https://github.com/SerenityOS/serenity/commit/d922e35579b Pull-request: https://github.com/SerenityOS/serenity/pull/8171
3 changed files with 80 additions and 21 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue