From f9700ffb416df629654267727929d4f2b4418f73 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 2 Oct 2020 15:57:23 -0600 Subject: [PATCH] PixelPaint: Move GUI logic and filter parameters out of filters --- Applications/PixelPaint/FilterParams.h | 191 ++++++++++++++++++ .../PixelPaint/Filters/BoxBlurFilter.cpp | 17 -- .../PixelPaint/Filters/BoxBlurFilter.h | 2 - .../Filters/GenericConvolutionFilter.cpp | 78 ------- .../Filters/GenericConvolutionFilter.h | 16 -- .../PixelPaint/Filters/LaplacianFilter.cpp | 8 - .../PixelPaint/Filters/LaplacianFilter.h | 2 - .../PixelPaint/Filters/SharpenFilter.cpp | 5 - .../PixelPaint/Filters/SharpenFilter.h | 2 - .../Filters/SpatialGaussianBlurFilter.cpp | 20 -- .../Filters/SpatialGaussianBlurFilter.h | 2 - Applications/PixelPaint/main.cpp | 22 +- 12 files changed, 200 insertions(+), 165 deletions(-) create mode 100644 Applications/PixelPaint/FilterParams.h diff --git a/Applications/PixelPaint/FilterParams.h b/Applications/PixelPaint/FilterParams.h new file mode 100644 index 0000000000..21c25d5eff --- /dev/null +++ b/Applications/PixelPaint/FilterParams.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace PixelPaint { + +template +struct FilterParameters { +}; + +template +class GenericConvolutionFilterInputDialog : public GUI::Dialog { + C_OBJECT(GenericConvolutionFilterInputDialog); + +public: + const Matrix& matrix() const { return m_matrix; } + bool should_wrap() const { return m_should_wrap; } + +private: + explicit GenericConvolutionFilterInputDialog(GUI::Window* parent_window) + : Dialog(parent_window) + { + // FIXME: Help! Make this GUI less ugly. + StringBuilder builder; + builder.appendf("%zux%zu", N, N); + builder.append(" Convolution"); + set_title(builder.string_view()); + + resize(200, 250); + auto& main_widget = set_main_widget(); + main_widget.set_frame_shape(Gfx::FrameShape::Container); + main_widget.set_frame_shadow(Gfx::FrameShadow::Raised); + main_widget.set_fill_with_background_color(true); + auto& layout = main_widget.template set_layout(); + layout.set_margins({ 4, 4, 4, 4 }); + + size_t index = 0; + size_t columns = N; + size_t rows = N; + + for (size_t row = 0; row < rows; ++row) { + auto& horizontal_container = main_widget.template add(); + horizontal_container.template set_layout(); + for (size_t column = 0; column < columns; ++column) { + if (index < columns * rows) { + auto& textbox = horizontal_container.template add(); + textbox.set_preferred_size({ 30, 50 }); + textbox.on_change = [&, row = row, column = column] { + auto& element = m_matrix.elements()[row][column]; + char* endptr = nullptr; + auto value = strtof(textbox.text().characters(), &endptr); + if (endptr != nullptr) + element = value; + else + textbox.set_text(""); + }; + } else { + horizontal_container.template add(); + } + } + } + + auto& norm_checkbox = main_widget.template add("Normalize"); + norm_checkbox.set_checked(false); + + auto& wrap_checkbox = main_widget.template add("Wrap"); + wrap_checkbox.set_checked(m_should_wrap); + + auto& button = main_widget.template add("Done"); + button.on_click = [&](auto) { + m_should_wrap = wrap_checkbox.is_checked(); + if (norm_checkbox.is_checked()) + normalize(m_matrix); + done(ExecOK); + }; + } + + Matrix m_matrix {}; + bool m_should_wrap { false }; +}; + +template +struct FilterParameters> { + static OwnPtr::Parameters> get(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) + { + Matrix kernel; + auto sigma = 1.0f; + auto s = 2.0f * sigma * sigma; + + for (auto x = -(ssize_t)N / 2; x <= (ssize_t)N / 2; x++) { + for (auto y = -(ssize_t)N / 2; y <= (ssize_t)N / 2; y++) { + auto r = sqrt(x * x + y * y); + kernel.elements()[x + 2][y + 2] = (exp(-(r * r) / s)) / (M_PI * s); + } + } + + normalize(kernel); + + return make::Parameters>(bitmap, rect, kernel); + } +}; + +template<> +struct FilterParameters { + static OwnPtr::Parameters> get(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) + { + return make::Parameters>(bitmap, rect, Matrix<3, float>(0, -1, 0, -1, 5, -1, 0, -1, 0)); + } +}; + +template<> +struct FilterParameters { + static OwnPtr::Parameters> get(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect, bool diagonal) + { + if (diagonal) + return make::Parameters>(bitmap, rect, Matrix<3, float>(-1, -1, -1, -1, 8, -1, -1, -1, -1)); + + return make::Parameters>(bitmap, rect, Matrix<3, float>(0, -1, 0, -1, 4, -1, 0, -1, 0)); + } +}; + +template +struct FilterParameters> { + static OwnPtr::Parameters> get(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect, GUI::Window* parent_window) + { + auto input = GenericConvolutionFilterInputDialog::construct(parent_window); + input->exec(); + if (input->result() == GUI::Dialog::ExecOK) + return make::Parameters>(bitmap, rect, input->matrix(), input->should_wrap()); + + return {}; + } +}; + +template +struct FilterParameters> { + static OwnPtr::Parameters> get(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) + { + Matrix kernel; + + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < N; ++j) { + kernel.elements()[i][j] = 1; + } + } + + normalize(kernel); + + return make::Parameters>(bitmap, rect, kernel); + } +}; + +} diff --git a/Applications/PixelPaint/Filters/BoxBlurFilter.cpp b/Applications/PixelPaint/Filters/BoxBlurFilter.cpp index f6f6a6e2ce..ad86dd91b6 100644 --- a/Applications/PixelPaint/Filters/BoxBlurFilter.cpp +++ b/Applications/PixelPaint/Filters/BoxBlurFilter.cpp @@ -42,23 +42,6 @@ BoxBlurFilter::~BoxBlurFilter() { } -template -OwnPtr::Parameters> -BoxBlurFilter::get_parameters(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) -{ - Matrix kernel; - - for (size_t i = 0; i < N; ++i) { - for (size_t j = 0; j < N; ++j) { - kernel.elements()[i][j] = 1; - } - } - - normalize(kernel); - - return make::Parameters>(bitmap, rect, kernel); -} - } template class PixelPaint::BoxBlurFilter<3>; diff --git a/Applications/PixelPaint/Filters/BoxBlurFilter.h b/Applications/PixelPaint/Filters/BoxBlurFilter.h index e96b00dd88..9e243c3ef8 100644 --- a/Applications/PixelPaint/Filters/BoxBlurFilter.h +++ b/Applications/PixelPaint/Filters/BoxBlurFilter.h @@ -37,8 +37,6 @@ public: virtual ~BoxBlurFilter(); virtual const char* class_name() const override { return "BoxBlurFilter"; } - - OwnPtr::Parameters> get_parameters(Gfx::Bitmap&, const Gfx::IntRect&); }; } diff --git a/Applications/PixelPaint/Filters/GenericConvolutionFilter.cpp b/Applications/PixelPaint/Filters/GenericConvolutionFilter.cpp index bfd8c4a503..af5a2bf299 100644 --- a/Applications/PixelPaint/Filters/GenericConvolutionFilter.cpp +++ b/Applications/PixelPaint/Filters/GenericConvolutionFilter.cpp @@ -26,14 +26,6 @@ #include "GenericConvolutionFilter.h" #include -#include -#include -#include -#include -#include -#include -#include -#include #include namespace PixelPaint { @@ -105,76 +97,6 @@ void GenericConvolutionFilter::apply(const Filter::Parameters& parameters) } } -template -OwnPtr::Parameters> -GenericConvolutionFilter::get_parameters(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect, GUI::Window* parent_window) -{ - auto input = GenericConvolutionFilterInputDialog::construct(parent_window); - input->exec(); - if (input->result() == GUI::Dialog::ExecOK) - return make(bitmap, rect, input->matrix(), input->should_wrap()); - - return {}; -} - -template -GenericConvolutionFilterInputDialog::GenericConvolutionFilterInputDialog(Window* parent_window) - : Dialog(parent_window) -{ - // FIXME: Help! Make this GUI less ugly. - StringBuilder builder; - builder.appendf("%zux%zu", N, N); - builder.append(" Convolution"); - set_title(builder.string_view()); - - resize(200, 250); - auto& main_widget = set_main_widget(); - main_widget.set_frame_shape(Gfx::FrameShape::Container); - main_widget.set_frame_shadow(Gfx::FrameShadow::Raised); - main_widget.set_fill_with_background_color(true); - auto& layout = main_widget.template set_layout(); - layout.set_margins({ 4, 4, 4, 4 }); - - size_t index = 0; - size_t columns = N; - size_t rows = N; - - for (size_t row = 0; row < rows; ++row) { - auto& horizontal_container = main_widget.template add(); - horizontal_container.template set_layout(); - for (size_t column = 0; column < columns; ++column) { - if (index < columns * rows) { - auto& textbox = horizontal_container.template add(); - textbox.set_preferred_size({ 30, 50 }); - textbox.on_change = [&, row = row, column = column] { - auto& element = m_matrix.elements()[row][column]; - char* endptr = nullptr; - auto value = strtof(textbox.text().characters(), &endptr); - if (endptr != nullptr) - element = value; - else - textbox.set_text(""); - }; - } else { - horizontal_container.template add(); - } - } - } - - auto& norm_checkbox = main_widget.template add("Normalize"); - norm_checkbox.set_checked(false); - - auto& wrap_checkbox = main_widget.template add("Wrap"); - wrap_checkbox.set_checked(m_should_wrap); - - auto& button = main_widget.template add("Done"); - button.on_click = [&](auto) { - m_should_wrap = wrap_checkbox.is_checked(); - if (norm_checkbox.is_checked()) - normalize(m_matrix); - done(ExecOK); - }; -} } template class PixelPaint::GenericConvolutionFilter<3>; diff --git a/Applications/PixelPaint/Filters/GenericConvolutionFilter.h b/Applications/PixelPaint/Filters/GenericConvolutionFilter.h index a6ff6f1d67..bd538f7d43 100644 --- a/Applications/PixelPaint/Filters/GenericConvolutionFilter.h +++ b/Applications/PixelPaint/Filters/GenericConvolutionFilter.h @@ -78,22 +78,6 @@ public: virtual const char* class_name() const override { return "GenericConvolutionFilter"; } virtual void apply(const Filter::Parameters&) override; - - OwnPtr get_parameters(Gfx::Bitmap&, const Gfx::IntRect&, GUI::Window* parent_window); -}; - -template -class GenericConvolutionFilterInputDialog : public GUI::Dialog { - C_OBJECT(GenericConvolutionFilterInputDialog); - -public: - const Matrix& matrix() const { return m_matrix; } - bool should_wrap() const { return m_should_wrap; } - -private: - explicit GenericConvolutionFilterInputDialog(GUI::Window*); - Matrix m_matrix {}; - bool m_should_wrap { false }; }; } diff --git a/Applications/PixelPaint/Filters/LaplacianFilter.cpp b/Applications/PixelPaint/Filters/LaplacianFilter.cpp index 808fafd818..6a1af5c768 100644 --- a/Applications/PixelPaint/Filters/LaplacianFilter.cpp +++ b/Applications/PixelPaint/Filters/LaplacianFilter.cpp @@ -36,12 +36,4 @@ LaplacianFilter::~LaplacianFilter() { } -OwnPtr::Parameters> LaplacianFilter::get_parameters(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect, bool diagonal) -{ - if (diagonal) - return make::Parameters>(bitmap, rect, Matrix<3, float>(-1, -1, -1, -1, 8, -1, -1, -1, -1)); - - return make::Parameters>(bitmap, rect, Matrix<3, float>(0, -1, 0, -1, 4, -1, 0, -1, 0)); -} - } diff --git a/Applications/PixelPaint/Filters/LaplacianFilter.h b/Applications/PixelPaint/Filters/LaplacianFilter.h index 0691e60a26..b0d838caeb 100644 --- a/Applications/PixelPaint/Filters/LaplacianFilter.h +++ b/Applications/PixelPaint/Filters/LaplacianFilter.h @@ -36,8 +36,6 @@ public: virtual ~LaplacianFilter(); virtual const char* class_name() const override { return "LaplacianFilter"; } - - OwnPtr get_parameters(Gfx::Bitmap&, const Gfx::IntRect&, bool diagonal); }; } diff --git a/Applications/PixelPaint/Filters/SharpenFilter.cpp b/Applications/PixelPaint/Filters/SharpenFilter.cpp index c6152c085d..7ee17b1cd3 100644 --- a/Applications/PixelPaint/Filters/SharpenFilter.cpp +++ b/Applications/PixelPaint/Filters/SharpenFilter.cpp @@ -37,9 +37,4 @@ SharpenFilter::~SharpenFilter() { } -OwnPtr::Parameters> SharpenFilter::get_parameters(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) -{ - return make::Parameters>(bitmap, rect, Matrix<3, float>(0, -1, 0, -1, 5, -1, 0, -1, 0)); -} - } diff --git a/Applications/PixelPaint/Filters/SharpenFilter.h b/Applications/PixelPaint/Filters/SharpenFilter.h index d1bdb836fa..7581e4c2ee 100644 --- a/Applications/PixelPaint/Filters/SharpenFilter.h +++ b/Applications/PixelPaint/Filters/SharpenFilter.h @@ -36,8 +36,6 @@ public: virtual ~SharpenFilter(); virtual const char* class_name() const override { return "SharpenFilter"; } - - OwnPtr get_parameters(Gfx::Bitmap&, const Gfx::IntRect&); }; } diff --git a/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.cpp b/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.cpp index fd3c2478fd..b4743951aa 100644 --- a/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.cpp +++ b/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.cpp @@ -38,26 +38,6 @@ SpatialGaussianBlurFilter::~SpatialGaussianBlurFilter() { } -template -OwnPtr::Parameters> -SpatialGaussianBlurFilter::get_parameters(Gfx::Bitmap& bitmap, const Gfx::IntRect& rect) -{ - Matrix kernel; - auto sigma = 1.0f; - auto s = 2.0f * sigma * sigma; - - for (auto x = -(ssize_t)N / 2; x <= (ssize_t)N / 2; x++) { - for (auto y = -(ssize_t)N / 2; y <= (ssize_t)N / 2; y++) { - auto r = sqrt(x * x + y * y); - kernel.elements()[x + 2][y + 2] = (exp(-(r * r) / s)) / (M_PI * s); - } - } - - normalize(kernel); - - return make::Parameters>(bitmap, rect, kernel); -} - } template class PixelPaint::SpatialGaussianBlurFilter<3>; diff --git a/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.h b/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.h index aed206d345..bb774cc75e 100644 --- a/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.h +++ b/Applications/PixelPaint/Filters/SpatialGaussianBlurFilter.h @@ -38,8 +38,6 @@ public: virtual ~SpatialGaussianBlurFilter(); virtual const char* class_name() const override { return "SpatialGaussianBlurFilter"; } - - OwnPtr::Parameters> get_parameters(Gfx::Bitmap&, const Gfx::IntRect&); }; } diff --git a/Applications/PixelPaint/main.cpp b/Applications/PixelPaint/main.cpp index d4d30f80cd..15b1227c27 100644 --- a/Applications/PixelPaint/main.cpp +++ b/Applications/PixelPaint/main.cpp @@ -25,11 +25,7 @@ */ #include "CreateNewLayerDialog.h" -#include "Filters/BoxBlurFilter.h" -#include "Filters/GenericConvolutionFilter.h" -#include "Filters/LaplacianFilter.h" -#include "Filters/SharpenFilter.h" -#include "Filters/SpatialGaussianBlurFilter.h" +#include "FilterParams.h" #include "Image.h" #include "ImageEditor.h" #include "Layer.h" @@ -223,14 +219,14 @@ int main(int argc, char** argv) edge_detect_submenu.add_action(GUI::Action::create("Laplacian (cardinal)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::LaplacianFilter filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect(), false)) + if (auto parameters = PixelPaint::FilterParameters::get(layer->bitmap(), layer->rect(), false)) filter.apply(*parameters); } })); edge_detect_submenu.add_action(GUI::Action::create("Laplacian (diagonal)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::LaplacianFilter filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect(), true)) + if (auto parameters = PixelPaint::FilterParameters::get(layer->bitmap(), layer->rect(), true)) filter.apply(*parameters); } })); @@ -238,35 +234,35 @@ int main(int argc, char** argv) blur_submenu.add_action(GUI::Action::create("Gaussian Blur (3x3)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::SpatialGaussianBlurFilter<3> filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect())) + if (auto parameters = PixelPaint::FilterParameters>::get(layer->bitmap(), layer->rect())) filter.apply(*parameters); } })); blur_submenu.add_action(GUI::Action::create("Gaussian Blur (5x5)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::SpatialGaussianBlurFilter<5> filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect())) + if (auto parameters = PixelPaint::FilterParameters>::get(layer->bitmap(), layer->rect())) filter.apply(*parameters); } })); blur_submenu.add_action(GUI::Action::create("Box Blur (3x3)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::BoxBlurFilter<3> filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect())) + if (auto parameters = PixelPaint::FilterParameters>::get(layer->bitmap(), layer->rect())) filter.apply(*parameters); } })); blur_submenu.add_action(GUI::Action::create("Box Blur (5x5)", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::BoxBlurFilter<5> filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect())) + if (auto parameters = PixelPaint::FilterParameters>::get(layer->bitmap(), layer->rect())) filter.apply(*parameters); } })); blur_submenu.add_action(GUI::Action::create("Sharpen", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::SharpenFilter filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect())) + if (auto parameters = PixelPaint::FilterParameters::get(layer->bitmap(), layer->rect())) filter.apply(*parameters); } })); @@ -275,7 +271,7 @@ int main(int argc, char** argv) spatial_filters_menu.add_action(GUI::Action::create("Generic 5x5 Convolution", [&](auto&) { if (auto* layer = image_editor.active_layer()) { PixelPaint::GenericConvolutionFilter<5> filter; - if (auto parameters = filter.get_parameters(layer->bitmap(), layer->rect(), window)) + if (auto parameters = PixelPaint::FilterParameters>::get(layer->bitmap(), layer->rect(), window)) filter.apply(*parameters); } }));