mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 07:37:46 +00:00
PixelPaint: Add "Brush Mode" to EraseTool :^)
Previously EraseTool would only let you have hard lines, similar to PenTool. After inheriting from BrushTool in previous commits, making the eraser (optionally) behave like a brush is much easier. We only need to change how the colors are handled for the hardness, which is why the `draw_point()` call is a bit more involved. Just blending the colors doesn't work here since we actually want to replace the previous color, unlike in BrushTool where we are just layering the color on top.
This commit is contained in:
parent
ec73247e90
commit
111ef19114
2 changed files with 66 additions and 4 deletions
|
@ -14,6 +14,7 @@
|
||||||
#include <LibGUI/Label.h>
|
#include <LibGUI/Label.h>
|
||||||
#include <LibGUI/Menu.h>
|
#include <LibGUI/Menu.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGUI/RadioButton.h>
|
||||||
#include <LibGUI/ValueSlider.h>
|
#include <LibGUI/ValueSlider.h>
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
|
|
||||||
|
@ -36,10 +37,26 @@ Color EraseTool::color_for(GUI::MouseEvent const&)
|
||||||
|
|
||||||
void EraseTool::draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point)
|
void EraseTool::draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point)
|
||||||
{
|
{
|
||||||
int radius = size() / 2;
|
if (m_draw_mode == DrawMode::Pencil) {
|
||||||
Gfx::IntRect rect { point.x() - radius, point.y() - radius, size(), size() };
|
int radius = size() / 2;
|
||||||
GUI::Painter painter(bitmap);
|
Gfx::IntRect rect { point.x() - radius, point.y() - radius, size(), size() };
|
||||||
painter.clear_rect(rect, color);
|
GUI::Painter painter(bitmap);
|
||||||
|
painter.clear_rect(rect, color);
|
||||||
|
} else {
|
||||||
|
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 });
|
||||||
|
if (x < 0 || x >= bitmap.width() || y < 0 || y >= bitmap.height())
|
||||||
|
continue;
|
||||||
|
if (distance >= size())
|
||||||
|
continue;
|
||||||
|
auto old_color = bitmap.get_pixel(x, y);
|
||||||
|
auto falloff = (1.0 - double { distance / size() }) * (1.0 / (100 - hardness()));
|
||||||
|
auto new_color = old_color.interpolate(color, falloff);
|
||||||
|
bitmap.set_pixel(x, y, new_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI::Widget* EraseTool::get_properties_widget()
|
GUI::Widget* EraseTool::get_properties_widget()
|
||||||
|
@ -65,6 +82,23 @@ GUI::Widget* EraseTool::get_properties_widget()
|
||||||
};
|
};
|
||||||
set_primary_slider(&size_slider);
|
set_primary_slider(&size_slider);
|
||||||
|
|
||||||
|
auto& hardness_container = m_properties_widget->add<GUI::Widget>();
|
||||||
|
hardness_container.set_fixed_height(20);
|
||||||
|
hardness_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||||
|
|
||||||
|
auto& hardness_label = hardness_container.add<GUI::Label>("Hardness:");
|
||||||
|
hardness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||||
|
hardness_label.set_fixed_size(80, 20);
|
||||||
|
|
||||||
|
auto& hardness_slider = hardness_container.add<GUI::ValueSlider>(Orientation::Horizontal, "%");
|
||||||
|
hardness_slider.set_range(1, 99);
|
||||||
|
hardness_slider.set_value(hardness());
|
||||||
|
|
||||||
|
hardness_slider.on_change = [&](int value) {
|
||||||
|
set_hardness(value);
|
||||||
|
};
|
||||||
|
set_secondary_slider(&hardness_slider);
|
||||||
|
|
||||||
auto& secondary_color_container = m_properties_widget->add<GUI::Widget>();
|
auto& secondary_color_container = m_properties_widget->add<GUI::Widget>();
|
||||||
secondary_color_container.set_fixed_height(20);
|
secondary_color_container.set_fixed_height(20);
|
||||||
secondary_color_container.set_layout<GUI::HorizontalBoxLayout>();
|
secondary_color_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||||
|
@ -75,6 +109,29 @@ GUI::Widget* EraseTool::get_properties_widget()
|
||||||
use_secondary_color_checkbox.on_checked = [&](bool checked) {
|
use_secondary_color_checkbox.on_checked = [&](bool checked) {
|
||||||
m_use_secondary_color = checked;
|
m_use_secondary_color = checked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto& mode_container = m_properties_widget->add<GUI::Widget>();
|
||||||
|
mode_container.set_fixed_height(46);
|
||||||
|
mode_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||||
|
auto& mode_label = mode_container.add<GUI::Label>("Draw Mode:");
|
||||||
|
mode_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||||
|
mode_label.set_fixed_size(80, 20);
|
||||||
|
|
||||||
|
auto& mode_radio_container = mode_container.add<GUI::Widget>();
|
||||||
|
mode_radio_container.set_layout<GUI::VerticalBoxLayout>();
|
||||||
|
auto& pencil_mode_radio = mode_radio_container.add<GUI::RadioButton>("Pencil");
|
||||||
|
auto& brush_mode_radio = mode_radio_container.add<GUI::RadioButton>("Brush");
|
||||||
|
|
||||||
|
pencil_mode_radio.on_checked = [&](bool) {
|
||||||
|
m_draw_mode = DrawMode::Pencil;
|
||||||
|
hardness_slider.set_enabled(false);
|
||||||
|
};
|
||||||
|
brush_mode_radio.on_checked = [&](bool) {
|
||||||
|
m_draw_mode = DrawMode::Brush;
|
||||||
|
hardness_slider.set_enabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
pencil_mode_radio.set_checked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_properties_widget.ptr();
|
return m_properties_widget.ptr();
|
||||||
|
|
|
@ -28,6 +28,11 @@ protected:
|
||||||
private:
|
private:
|
||||||
RefPtr<GUI::Widget> m_properties_widget;
|
RefPtr<GUI::Widget> m_properties_widget;
|
||||||
|
|
||||||
|
enum class DrawMode {
|
||||||
|
Pencil,
|
||||||
|
Brush,
|
||||||
|
};
|
||||||
|
DrawMode m_draw_mode { DrawMode::Brush };
|
||||||
bool m_use_secondary_color { false };
|
bool m_use_secondary_color { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue