mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:07:35 +00:00
PixelPaint: BrushTool performance optimization
This patch optimizes how the Brush-Tool modifies the pixels. The new logic generates a "reference brush" with the required size, falloff and color only once and uses that for the rawing operations. If no editing mask is used the reference brush is writen via a blit operation to the content or mask image. This increases the drawing speed and therefore also allows bigger brush sizes.
This commit is contained in:
parent
31ee20e179
commit
b9b4ca064f
3 changed files with 62 additions and 8 deletions
|
@ -12,6 +12,7 @@
|
||||||
#include <LibGUI/Action.h>
|
#include <LibGUI/Action.h>
|
||||||
#include <LibGUI/BoxLayout.h>
|
#include <LibGUI/BoxLayout.h>
|
||||||
#include <LibGUI/Label.h>
|
#include <LibGUI/Label.h>
|
||||||
|
#include <LibGUI/MessageBox.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
#include <LibGUI/ValueSlider.h>
|
#include <LibGUI/ValueSlider.h>
|
||||||
#include <LibGfx/AntiAliasingPainter.h>
|
#include <LibGfx/AntiAliasingPainter.h>
|
||||||
|
@ -26,6 +27,9 @@ void BrushTool::set_size(int size)
|
||||||
return;
|
return;
|
||||||
m_size = size;
|
m_size = size;
|
||||||
refresh_editor_cursor();
|
refresh_editor_cursor();
|
||||||
|
auto may_have_failed = ensure_brush_reference_bitmap(m_ensured_color);
|
||||||
|
if (may_have_failed.is_error())
|
||||||
|
GUI::MessageBox::show_error(nullptr, MUST(String::formatted("Failed to create the brush. error: {}", may_have_failed.release_error())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
||||||
|
@ -87,18 +91,28 @@ Color BrushTool::color_for(GUI::MouseEvent const& event)
|
||||||
|
|
||||||
void BrushTool::draw_point(Gfx::Bitmap& bitmap, Gfx::Color color, Gfx::IntPoint point)
|
void BrushTool::draw_point(Gfx::Bitmap& bitmap, Gfx::Color color, Gfx::IntPoint point)
|
||||||
{
|
{
|
||||||
constexpr auto flow_scale = 10;
|
if (ensure_brush_reference_bitmap(color).is_error())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_editor->active_layer()->mask_type() != Layer::MaskType::EditingMask || m_editor->active_layer()->edit_mode() == Layer::EditMode::Mask) {
|
||||||
|
Gfx::Painter painter = Gfx::Painter(bitmap);
|
||||||
|
painter.blit(point.translated(-size()), *m_brush_reference, m_brush_reference->rect());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have to deal with an EditingMask we need to set the pixel individually
|
||||||
|
int ref_x, ref_y;
|
||||||
for (int y = point.y() - size(); y < point.y() + size(); y++) {
|
for (int y = point.y() - size(); y < point.y() + size(); y++) {
|
||||||
for (int x = point.x() - size(); x < point.x() + size(); x++) {
|
for (int x = point.x() - size(); x < point.x() + size(); x++) {
|
||||||
auto distance = point.distance_from({ x, y });
|
ref_x = x + size() - point.x();
|
||||||
|
ref_y = y + size() - point.y();
|
||||||
if (x < 0 || x >= bitmap.width() || y < 0 || y >= bitmap.height())
|
if (x < 0 || x >= bitmap.width() || y < 0 || y >= bitmap.height())
|
||||||
continue;
|
continue;
|
||||||
if (distance >= size())
|
|
||||||
|
auto pixel_color = m_brush_reference->get_pixel<Gfx::StorageFormat::BGRA8888>(ref_x, ref_y);
|
||||||
|
if (!pixel_color.alpha())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto falloff = get_falloff(distance) * flow_scale;
|
|
||||||
auto pixel_color = color;
|
|
||||||
pixel_color.set_alpha(AK::min(falloff * 255, 255));
|
|
||||||
set_pixel_with_possible_mask(x, y, bitmap.get_pixel(x, y).blend(pixel_color), bitmap);
|
set_pixel_with_possible_mask(x, y, bitmap.get_pixel(x, y).blend(pixel_color), bitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +229,36 @@ void BrushTool::refresh_editor_cursor()
|
||||||
m_editor->update_tool_cursor();
|
m_editor->update_tool_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> BrushTool::ensure_brush_reference_bitmap(Gfx::Color color)
|
||||||
|
{
|
||||||
|
Gfx::IntSize brush_size = Gfx::IntSize(size() * 2, size() * 2);
|
||||||
|
|
||||||
|
if (m_brush_reference.is_null() || m_brush_reference->size() != brush_size)
|
||||||
|
m_brush_reference = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, brush_size));
|
||||||
|
else if (m_ensured_color != color || m_ensured_hardness != hardness())
|
||||||
|
m_brush_reference->fill(Color::Transparent);
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
|
||||||
|
m_ensured_color = color;
|
||||||
|
m_ensured_hardness = hardness();
|
||||||
|
constexpr auto flow_scale = 10;
|
||||||
|
Gfx::IntPoint center_point = { size(), size() };
|
||||||
|
for (int y = 0; y < m_brush_reference->height(); y++) {
|
||||||
|
for (int x = 0; x < m_brush_reference->width(); x++) {
|
||||||
|
auto distance = center_point.distance_from({ x, y });
|
||||||
|
if (distance >= size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto falloff = get_falloff(distance) * flow_scale;
|
||||||
|
auto pixel_color = color;
|
||||||
|
pixel_color.set_alpha(AK::min(falloff * 255, 255));
|
||||||
|
m_brush_reference->set_pixel(x, y, pixel_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
float BrushTool::preferred_cursor_size()
|
float BrushTool::preferred_cursor_size()
|
||||||
{
|
{
|
||||||
return 2 * size() * (m_editor ? m_editor->scale() : 1);
|
return 2 * size() * (m_editor ? m_editor->scale() : 1);
|
||||||
|
|
|
@ -62,6 +62,10 @@ private:
|
||||||
bool m_has_clicked { false };
|
bool m_has_clicked { false };
|
||||||
Gfx::IntPoint m_last_position;
|
Gfx::IntPoint m_last_position;
|
||||||
NonnullRefPtr<Gfx::Bitmap const> m_cursor = build_cursor();
|
NonnullRefPtr<Gfx::Bitmap const> m_cursor = build_cursor();
|
||||||
|
RefPtr<Gfx::Bitmap> m_brush_reference = nullptr;
|
||||||
|
Gfx::Color m_ensured_color {};
|
||||||
|
int m_ensured_hardness = 0;
|
||||||
|
ErrorOr<void> ensure_brush_reference_bitmap(Gfx::Color);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,10 @@ void Tool::set_pixel_with_possible_mask<Gfx::StorageFormat::BGRA8888>(int x, int
|
||||||
|
|
||||||
switch (m_editor->active_layer()->edit_mode()) {
|
switch (m_editor->active_layer()->edit_mode()) {
|
||||||
case Layer::EditMode::Content:
|
case Layer::EditMode::Content:
|
||||||
bitmap.set_pixel<Gfx::StorageFormat::BGRA8888>(x, y, m_editor->active_layer()->modify_pixel_with_editing_mask(x, y, color, bitmap.get_pixel(x, y)));
|
if (m_editor->active_layer()->mask_type() == Layer::MaskType::EditingMask)
|
||||||
|
bitmap.set_pixel<Gfx::StorageFormat::BGRA8888>(x, y, m_editor->active_layer()->modify_pixel_with_editing_mask(x, y, color, bitmap.get_pixel(x, y)));
|
||||||
|
else
|
||||||
|
bitmap.set_pixel(x, y, color);
|
||||||
break;
|
break;
|
||||||
case Layer::EditMode::Mask:
|
case Layer::EditMode::Mask:
|
||||||
bitmap.set_pixel<Gfx::StorageFormat::BGRA8888>(x, y, color);
|
bitmap.set_pixel<Gfx::StorageFormat::BGRA8888>(x, y, color);
|
||||||
|
@ -106,7 +109,10 @@ void Tool::set_pixel_with_possible_mask(int x, int y, Gfx::Color color, Gfx::Bit
|
||||||
|
|
||||||
switch (m_editor->active_layer()->edit_mode()) {
|
switch (m_editor->active_layer()->edit_mode()) {
|
||||||
case Layer::EditMode::Content:
|
case Layer::EditMode::Content:
|
||||||
bitmap.set_pixel(x, y, m_editor->active_layer()->modify_pixel_with_editing_mask(x, y, color, bitmap.get_pixel(x, y)));
|
if (m_editor->active_layer()->mask_type() == Layer::MaskType::EditingMask)
|
||||||
|
bitmap.set_pixel(x, y, m_editor->active_layer()->modify_pixel_with_editing_mask(x, y, color, bitmap.get_pixel(x, y)));
|
||||||
|
else
|
||||||
|
bitmap.set_pixel(x, y, color);
|
||||||
break;
|
break;
|
||||||
case Layer::EditMode::Mask:
|
case Layer::EditMode::Mask:
|
||||||
bitmap.set_pixel(x, y, color);
|
bitmap.set_pixel(x, y, color);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue