diff --git a/Applications/PixelPaint/BrushTool.cpp b/Applications/PixelPaint/BrushTool.cpp new file mode 100644 index 0000000000..6e89eab88b --- /dev/null +++ b/Applications/PixelPaint/BrushTool.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, Ben Jilks + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "BrushTool.h" +#include "ImageEditor.h" +#include "Layer.h" +#include +#include +#include +#include + +namespace PixelPaint { + +BrushTool::BrushTool() +{ +} + +BrushTool::~BrushTool() +{ +} + +void BrushTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) +{ + if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right) + return; + + m_last_position = event.position(); +} + +void BrushTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) +{ + if (!(event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right)) + return; + + draw_line(layer.bitmap(), m_editor->color_for(event), m_last_position, event.position()); + layer.did_modify_bitmap(*m_editor->image()); + m_last_position = event.position(); +} + +void BrushTool::draw_point(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& point) +{ + for (int y = point.y() - m_size; y < point.y() + m_size; y++) { + for (int x = point.x() - m_size; x < point.x() + m_size; x++) { + auto distance = point.distance_from({ x, y }); + if (x < 0 || x >= bitmap.width() || y < 0 || y >= bitmap.height()) + continue; + if (distance >= m_size) + continue; + + auto falloff = (1.0 - (distance / (float)m_size)) * 0.2; + auto pixel_color = color; + pixel_color.set_alpha(falloff * 255); + bitmap.set_pixel(x, y, bitmap.get_pixel(x, y).blend(pixel_color)); + } + } +} + +void BrushTool::draw_line(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& start, const Gfx::IntPoint& end) +{ + int length_x = end.x() - start.x(); + int length_y = end.y() - start.y(); + float y_step = length_y == 0 ? 0 : (float)(length_y) / (float)(length_x); + if (y_step > abs(length_y)) + y_step = abs(length_y); + if (y_step < -abs(length_y)) + y_step = -abs(length_y); + if (y_step == 0 && start.x() == end.x()) + return; + + int start_x = start.x(); + int end_x = end.x(); + int start_y = start.y(); + int end_y = end.y(); + if (start_x > end_x) { + swap(start_x, end_x); + swap(start_y, end_y); + } + + float y = start_y; + for (int x = start_x; x <= end_x; x++) { + int start_step_y = y; + int end_step_y = y + y_step; + if (start_step_y > end_step_y) + swap(start_step_y, end_step_y); + for (int i = start_step_y; i <= end_step_y; i++) + draw_point(bitmap, color, { x, i }); + y += y_step; + } +} + +} diff --git a/Applications/PixelPaint/BrushTool.h b/Applications/PixelPaint/BrushTool.h new file mode 100644 index 0000000000..68d25385d2 --- /dev/null +++ b/Applications/PixelPaint/BrushTool.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, Ben Jilks + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Tool.h" + +namespace PixelPaint { + +class BrushTool final : public Tool { +public: + BrushTool(); + virtual ~BrushTool() override; + + virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; + virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; + +private: + int m_size { 10 }; + Gfx::IntPoint m_last_position; + + virtual const char* class_name() const override { return "BrushTool"; } + void draw_line(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& start, const Gfx::IntPoint& end); + void draw_point(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gfx::IntPoint& point); +}; + +} diff --git a/Applications/PixelPaint/CMakeLists.txt b/Applications/PixelPaint/CMakeLists.txt index 52c55e48b0..ca317ae46f 100644 --- a/Applications/PixelPaint/CMakeLists.txt +++ b/Applications/PixelPaint/CMakeLists.txt @@ -1,4 +1,5 @@ set(SOURCES + BrushTool.cpp BucketTool.cpp CreateNewLayerDialog.cpp EllipseTool.cpp diff --git a/Applications/PixelPaint/ToolboxWidget.cpp b/Applications/PixelPaint/ToolboxWidget.cpp index a57c2c3938..d497506df3 100644 --- a/Applications/PixelPaint/ToolboxWidget.cpp +++ b/Applications/PixelPaint/ToolboxWidget.cpp @@ -25,6 +25,7 @@ */ #include "ToolboxWidget.h" +#include "BrushTool.h" #include "BucketTool.h" #include "EllipseTool.h" #include "EraseTool.h" @@ -127,6 +128,7 @@ void ToolboxWidget::setup_tools() add_tool("Move", "move", { 0, Key_M }, make()); add_tool("Pen", "pen", { 0, Key_N }, make()); + add_tool("Brush", "brush", { 0, Key_P }, make()); add_tool("Bucket Fill", "bucket", { Mod_Shift, Key_B }, make()); add_tool("Spray", "spray", { Mod_Shift, Key_S }, make()); add_tool("Color Picker", "picker", { 0, Key_O }, make()); diff --git a/Base/res/icons/pixelpaint/brush.png b/Base/res/icons/pixelpaint/brush.png new file mode 100644 index 0000000000..5b09f4eff8 Binary files /dev/null and b/Base/res/icons/pixelpaint/brush.png differ