1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:07:45 +00:00

PaintBrush: Implement a thickness setting for the pen tool.

Painter gains the ability to draw lines with arbitrary thickness.
It's basically implemented by drawing filled rects for thickness>1.

In PaintBrush, Tool classes can now override on_contextmenu() to
provide a context menu for the toolbox button. :^)
This commit is contained in:
Andreas Kling 2019-06-23 10:00:02 +02:00
parent 08c04f0a41
commit 5aefd7f828
6 changed files with 57 additions and 21 deletions

View file

@ -1,5 +1,7 @@
#include "PenTool.h" #include "PenTool.h"
#include "PaintableWidget.h" #include "PaintableWidget.h"
#include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GPainter.h> #include <LibGUI/GPainter.h>
PenTool::PenTool() PenTool::PenTool()
@ -16,8 +18,8 @@ void PenTool::on_mousedown(GMouseEvent& event)
return; return;
GPainter painter(m_widget->bitmap()); GPainter painter(m_widget->bitmap());
painter.set_pixel(event.position(), m_widget->color_for(event)); painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
m_widget->update({ event.position(), { 1, 1 } }); m_widget->update();
m_last_drawing_event_position = event.position(); m_last_drawing_event_position = event.position();
} }
@ -35,14 +37,32 @@ void PenTool::on_mousemove(GMouseEvent& event)
if (event.buttons() & GMouseButton::Left || event.buttons() & GMouseButton::Right) { if (event.buttons() & GMouseButton::Left || event.buttons() & GMouseButton::Right) {
GPainter painter(m_widget->bitmap()); GPainter painter(m_widget->bitmap());
if (m_last_drawing_event_position != Point(-1, -1)) { if (m_last_drawing_event_position != Point(-1, -1))
painter.draw_line(m_last_drawing_event_position, event.position(), m_widget->color_for(event)); painter.draw_line(m_last_drawing_event_position, event.position(), m_widget->color_for(event), m_thickness);
m_widget->update(); else
} else { painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
painter.set_pixel(event.position(), m_widget->color_for(event)); m_widget->update();
m_widget->update({ event.position(), { 1, 1 } });
}
m_last_drawing_event_position = event.position(); m_last_drawing_event_position = event.position();
} }
} }
void PenTool::on_contextmenu(GContextMenuEvent& event)
{
if (!m_context_menu) {
m_context_menu = make<GMenu>("PenTool Context Menu");
m_context_menu->add_action(GAction::create("1", [this](auto&) {
m_thickness = 1;
}));
m_context_menu->add_action(GAction::create("2", [this](auto&) {
m_thickness = 2;
}));
m_context_menu->add_action(GAction::create("3", [this](auto&) {
m_thickness = 3;
}));
m_context_menu->add_action(GAction::create("4", [this](auto&) {
m_thickness = 4;
}));
}
m_context_menu->popup(event.screen_position());
}

View file

@ -3,6 +3,8 @@
#include "Tool.h" #include "Tool.h"
#include <SharedGraphics/Point.h> #include <SharedGraphics/Point.h>
class GMenu;
class PenTool final : public Tool { class PenTool final : public Tool {
public: public:
PenTool(); PenTool();
@ -11,9 +13,12 @@ public:
virtual void on_mousedown(GMouseEvent&) override; virtual void on_mousedown(GMouseEvent&) override;
virtual void on_mousemove(GMouseEvent&) override; virtual void on_mousemove(GMouseEvent&) override;
virtual void on_mouseup(GMouseEvent&) override; virtual void on_mouseup(GMouseEvent&) override;
virtual void on_contextmenu(GContextMenuEvent&) override;
private: private:
virtual const char* class_name() const override { return "PenTool"; } virtual const char* class_name() const override { return "PenTool"; }
Point m_last_drawing_event_position { -1, -1 }; Point m_last_drawing_event_position { -1, -1 };
OwnPtr<GMenu> m_context_menu;
int m_thickness { 1 };
}; };

View file

@ -12,6 +12,7 @@ public:
virtual void on_mousedown(GMouseEvent&) { } virtual void on_mousedown(GMouseEvent&) { }
virtual void on_mousemove(GMouseEvent&) { } virtual void on_mousemove(GMouseEvent&) { }
virtual void on_mouseup(GMouseEvent&) { } virtual void on_mouseup(GMouseEvent&) { }
virtual void on_contextmenu(GContextMenuEvent&) { }
void clear() { m_widget = nullptr; } void clear() { m_widget = nullptr; }
void setup(PaintableWidget& widget) { m_widget = widget.make_weak_ptr(); } void setup(PaintableWidget& widget) { m_widget = widget.make_weak_ptr(); }

View file

@ -20,6 +20,11 @@ public:
const Tool& tool() const { return *m_tool; } const Tool& tool() const { return *m_tool; }
Tool& tool() { return *m_tool; } Tool& tool() { return *m_tool; }
virtual void context_menu_event(GContextMenuEvent& event) override
{
m_tool->on_contextmenu(event);
}
private: private:
OwnPtr<Tool> m_tool; OwnPtr<Tool> m_tool;
}; };

View file

@ -636,7 +636,16 @@ void Painter::set_pixel(const Point& p, Color color)
pixel ^= color.value(); pixel ^= color.value();
} }
void Painter::draw_line(const Point& p1, const Point& p2, Color color) void Painter::draw_pixel(const Point& position, Color color, int thickness)
{
ASSERT(draw_op() == DrawOp::Copy);
if (thickness == 1)
return set_pixel_with_draw_op(m_target->scanline(position.y())[position.x()], color);
Rect rect { position.translated(-(thickness / 2), -(thickness / 2)), { thickness, thickness } };
fill_rect(rect, color);
}
void Painter::draw_line(const Point& p1, const Point& p2, Color color, int thickness)
{ {
auto clip_rect = this->clip_rect(); auto clip_rect = this->clip_rect();
@ -660,7 +669,7 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
int min_y = max(point1.y(), clip_rect.top()); int min_y = max(point1.y(), clip_rect.top());
int max_y = min(point2.y(), clip_rect.bottom()); int max_y = min(point2.y(), clip_rect.bottom());
for (int y = min_y; y <= max_y; ++y) for (int y = min_y; y <= max_y; ++y)
set_pixel_with_draw_op(m_target->scanline(y)[x], color); draw_pixel({ x, y }, color, thickness);
return; return;
} }
@ -677,13 +686,8 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
return; return;
int min_x = max(point1.x(), clip_rect.left()); int min_x = max(point1.x(), clip_rect.left());
int max_x = min(point2.x(), clip_rect.right()); int max_x = min(point2.x(), clip_rect.right());
auto* pixels = m_target->scanline(point1.y()); for (int x = min_x; x <= max_x; ++x)
if (draw_op() == DrawOp::Copy) { draw_pixel({ x, y }, color, thickness);
fast_dword_fill(pixels + min_x, color.value(), max_x - min_x + 1);
} else {
for (int x = min_x; x <= max_x; ++x)
set_pixel_with_draw_op(pixels[x], color);
}
return; return;
} }
@ -709,7 +713,7 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
int y = point1.y(); int y = point1.y();
for (int x = point1.x(); x <= point2.x(); ++x) { for (int x = point1.x(); x <= point2.x(); ++x) {
if (clip_rect.contains(x, y)) if (clip_rect.contains(x, y))
m_target->scanline(y)[x] = color.value(); draw_pixel({ x, y }, color, thickness);
error += delta_error; error += delta_error;
if (error >= 0.5) { if (error >= 0.5) {
y = (double)y + y_step; y = (double)y + y_step;
@ -722,7 +726,7 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
int x = point1.x(); int x = point1.x();
for (int y = point1.y(); y <= point2.y(); ++y) { for (int y = point1.y(); y <= point2.y(); ++y) {
if (clip_rect.contains(x, y)) if (clip_rect.contains(x, y))
m_target->scanline(y)[x] = color.value(); draw_pixel({ x, y }, color, thickness);
error += delta_error; error += delta_error;
if (error >= 0.5) { if (error >= 0.5) {
x = (double)x + x_step; x = (double)x + x_step;

View file

@ -23,7 +23,7 @@ public:
void draw_bitmap(const Point&, const CharacterBitmap&, Color = Color()); void draw_bitmap(const Point&, const CharacterBitmap&, Color = Color());
void draw_bitmap(const Point&, const GlyphBitmap&, Color = Color()); void draw_bitmap(const Point&, const GlyphBitmap&, Color = Color());
void set_pixel(const Point&, Color); void set_pixel(const Point&, Color);
void draw_line(const Point&, const Point&, Color); void draw_line(const Point&, const Point&, Color, int thickness = 1);
void draw_scaled_bitmap(const Rect& dst_rect, const GraphicsBitmap&, const Rect& src_rect); void draw_scaled_bitmap(const Rect& dst_rect, const GraphicsBitmap&, const Rect& src_rect);
void blit(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity = 1.0f); void blit(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity = 1.0f);
void blit_dimmed(const Point&, const GraphicsBitmap&, const Rect& src_rect); void blit_dimmed(const Point&, const GraphicsBitmap&, const Rect& src_rect);
@ -68,6 +68,7 @@ protected:
void fill_rect_with_draw_op(const Rect&, Color); void fill_rect_with_draw_op(const Rect&, Color);
void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect); void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect);
void blit_with_opacity(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity); void blit_with_opacity(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity);
void draw_pixel(const Point&, Color, int thickness = 1);
struct State { struct State {
const Font* font; const Font* font;