diff --git a/Userland/Applications/PixelPaint/Tools/BrushTool.cpp b/Userland/Applications/PixelPaint/Tools/BrushTool.cpp index b432c1842b..43636a4faf 100644 --- a/Userland/Applications/PixelPaint/Tools/BrushTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/BrushTool.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ void BrushTool::set_size(int size) return; m_size = size; 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) @@ -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) { - 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 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()) continue; - if (distance >= size()) + + auto pixel_color = m_brush_reference->get_pixel(ref_x, ref_y); + if (!pixel_color.alpha()) 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); } } @@ -215,6 +229,36 @@ void BrushTool::refresh_editor_cursor() m_editor->update_tool_cursor(); } +ErrorOr 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() { return 2 * size() * (m_editor ? m_editor->scale() : 1); diff --git a/Userland/Applications/PixelPaint/Tools/BrushTool.h b/Userland/Applications/PixelPaint/Tools/BrushTool.h index aab50914bc..c53db41822 100644 --- a/Userland/Applications/PixelPaint/Tools/BrushTool.h +++ b/Userland/Applications/PixelPaint/Tools/BrushTool.h @@ -62,6 +62,10 @@ private: bool m_has_clicked { false }; Gfx::IntPoint m_last_position; NonnullRefPtr m_cursor = build_cursor(); + RefPtr m_brush_reference = nullptr; + Gfx::Color m_ensured_color {}; + int m_ensured_hardness = 0; + ErrorOr ensure_brush_reference_bitmap(Gfx::Color); }; } diff --git a/Userland/Applications/PixelPaint/Tools/Tool.cpp b/Userland/Applications/PixelPaint/Tools/Tool.cpp index a7699ff1b9..7da838dfbc 100644 --- a/Userland/Applications/PixelPaint/Tools/Tool.cpp +++ b/Userland/Applications/PixelPaint/Tools/Tool.cpp @@ -91,7 +91,10 @@ void Tool::set_pixel_with_possible_mask(int x, int switch (m_editor->active_layer()->edit_mode()) { 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; case Layer::EditMode::Mask: bitmap.set_pixel(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()) { 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; case Layer::EditMode::Mask: bitmap.set_pixel(x, y, color);