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
Notes: sideshowbarker 2024-07-19 13:30:14 +09:00
6 changed files with 57 additions and 21 deletions

View file

@ -1,5 +1,7 @@
#include "PenTool.h"
#include "PaintableWidget.h"
#include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GPainter.h>
PenTool::PenTool()
@ -16,8 +18,8 @@ void PenTool::on_mousedown(GMouseEvent& event)
return;
GPainter painter(m_widget->bitmap());
painter.set_pixel(event.position(), m_widget->color_for(event));
m_widget->update({ event.position(), { 1, 1 } });
painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
m_widget->update();
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) {
GPainter painter(m_widget->bitmap());
if (m_last_drawing_event_position != Point(-1, -1)) {
painter.draw_line(m_last_drawing_event_position, event.position(), m_widget->color_for(event));
m_widget->update();
} else {
painter.set_pixel(event.position(), m_widget->color_for(event));
m_widget->update({ event.position(), { 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), m_thickness);
else
painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
m_widget->update();
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 <SharedGraphics/Point.h>
class GMenu;
class PenTool final : public Tool {
public:
PenTool();
@ -11,9 +13,12 @@ public:
virtual void on_mousedown(GMouseEvent&) override;
virtual void on_mousemove(GMouseEvent&) override;
virtual void on_mouseup(GMouseEvent&) override;
virtual void on_contextmenu(GContextMenuEvent&) override;
private:
virtual const char* class_name() const override { return "PenTool"; }
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_mousemove(GMouseEvent&) { }
virtual void on_mouseup(GMouseEvent&) { }
virtual void on_contextmenu(GContextMenuEvent&) { }
void clear() { m_widget = nullptr; }
void setup(PaintableWidget& widget) { m_widget = widget.make_weak_ptr(); }

View file

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

View file

@ -636,7 +636,16 @@ void Painter::set_pixel(const Point& p, Color color)
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();
@ -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 max_y = min(point2.y(), clip_rect.bottom());
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;
}
@ -677,13 +686,8 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
return;
int min_x = max(point1.x(), clip_rect.left());
int max_x = min(point2.x(), clip_rect.right());
auto* pixels = m_target->scanline(point1.y());
if (draw_op() == DrawOp::Copy) {
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);
}
for (int x = min_x; x <= max_x; ++x)
draw_pixel({ x, y }, color, thickness);
return;
}
@ -709,7 +713,7 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color)
int y = point1.y();
for (int x = point1.x(); x <= point2.x(); ++x) {
if (clip_rect.contains(x, y))
m_target->scanline(y)[x] = color.value();
draw_pixel({ x, y }, color, thickness);
error += delta_error;
if (error >= 0.5) {
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();
for (int y = point1.y(); y <= point2.y(); ++y) {
if (clip_rect.contains(x, y))
m_target->scanline(y)[x] = color.value();
draw_pixel({ x, y }, color, thickness);
error += delta_error;
if (error >= 0.5) {
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 GlyphBitmap&, Color = 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 blit(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity = 1.0f);
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 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 draw_pixel(const Point&, Color, int thickness = 1);
struct State {
const Font* font;