From 58c30959b1e8e9e9521e9022bb478b0579d0554e Mon Sep 17 00:00:00 2001 From: BenJilks Date: Sun, 18 Oct 2020 17:17:49 +0000 Subject: PixelPaint: Export image as BMP You can now export your masterpieces as fresh, crisp BMP files. --- Applications/PixelPaint/CMakeLists.txt | 1 + Applications/PixelPaint/CreateNewImageDialog.cpp | 91 ++++++++++++++++++++++++ Applications/PixelPaint/CreateNewImageDialog.h | 49 +++++++++++++ Applications/PixelPaint/Image.cpp | 17 ++++- Applications/PixelPaint/Image.h | 1 + Applications/PixelPaint/ImageEditor.cpp | 3 +- Applications/PixelPaint/main.cpp | 33 +++++++++ 7 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 Applications/PixelPaint/CreateNewImageDialog.cpp create mode 100644 Applications/PixelPaint/CreateNewImageDialog.h (limited to 'Applications') diff --git a/Applications/PixelPaint/CMakeLists.txt b/Applications/PixelPaint/CMakeLists.txt index 4a5396d5d0..44df93980f 100644 --- a/Applications/PixelPaint/CMakeLists.txt +++ b/Applications/PixelPaint/CMakeLists.txt @@ -1,6 +1,7 @@ set(SOURCES BrushTool.cpp BucketTool.cpp + CreateNewImageDialog.cpp CreateNewLayerDialog.cpp EllipseTool.cpp EraseTool.cpp diff --git a/Applications/PixelPaint/CreateNewImageDialog.cpp b/Applications/PixelPaint/CreateNewImageDialog.cpp new file mode 100644 index 0000000000..0696b69ba8 --- /dev/null +++ b/Applications/PixelPaint/CreateNewImageDialog.cpp @@ -0,0 +1,91 @@ +/* + * 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 "CreateNewImageDialog.h" +#include +#include +#include +#include +#include + +namespace PixelPaint { + +CreateNewImageDialog::CreateNewImageDialog(GUI::Window* parent_window) + : Dialog(parent_window) +{ + set_title("Create new image"); + resize(200, 200); + + auto& main_widget = set_main_widget(); + main_widget.set_fill_with_background_color(true); + + auto& layout = main_widget.set_layout(); + layout.set_margins({ 4, 4, 4, 4 }); + + auto& name_label = main_widget.add("Name:"); + name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + m_name_textbox = main_widget.add(); + m_name_textbox->on_change = [this] { + m_image_name = m_name_textbox->text(); + }; + + auto& width_label = main_widget.add("Width:"); + width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto& width_spinbox = main_widget.add(); + + auto& height_label = main_widget.add("Height:"); + height_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto& height_spinbox = main_widget.add(); + + auto& button_container = main_widget.add(); + button_container.set_layout(); + + auto& ok_button = button_container.add("OK"); + ok_button.on_click = [this](auto) { + done(ExecOK); + }; + + auto& cancel_button = button_container.add("Cancel"); + cancel_button.on_click = [this](auto) { + done(ExecCancel); + }; + + width_spinbox.on_change = [this](int value) { + m_image_size.set_width(value); + }; + + height_spinbox.on_change = [this](int value) { + m_image_size.set_height(value); + }; + + width_spinbox.set_range(0, 16384); + height_spinbox.set_range(0, 16384); +} + +} diff --git a/Applications/PixelPaint/CreateNewImageDialog.h b/Applications/PixelPaint/CreateNewImageDialog.h new file mode 100644 index 0000000000..b46b65adab --- /dev/null +++ b/Applications/PixelPaint/CreateNewImageDialog.h @@ -0,0 +1,49 @@ +/* + * 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 + +namespace PixelPaint { + +class CreateNewImageDialog final : public GUI::Dialog { + C_OBJECT(CreateNewImageDialog) + +public: + const Gfx::IntSize& image_size() const { return m_image_size; } + const String& image_name() const { return m_image_name; } + +private: + CreateNewImageDialog(GUI::Window* parent_window); + + String m_image_name; + Gfx::IntSize m_image_size; + + RefPtr m_name_textbox; +}; + +} diff --git a/Applications/PixelPaint/Image.cpp b/Applications/PixelPaint/Image.cpp index 57f83d7a9a..23a7f89d7e 100644 --- a/Applications/PixelPaint/Image.cpp +++ b/Applications/PixelPaint/Image.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -118,7 +118,7 @@ void Image::save(const String& file_path) const { auto json_layers = json.add_array("layers"); for (const auto& layer : m_layers) { - Gfx::BMPDumper bmp_dumber; + Gfx::BMPWriter bmp_dumber; auto json_layer = json_layers.add_object(); json_layer.add("width", layer.size().width()); json_layer.add("height", layer.size().height()); @@ -140,6 +140,19 @@ void Image::save(const String& file_path) const fclose(file); } +void Image::export_bmp(const String& file_path) +{ + auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, m_size); + GUI::Painter painter(*bitmap); + paint_into(painter, { 0, 0, m_size.width(), m_size.height() }); + + Gfx::BMPWriter dumper; + auto bmp = dumper.dump(bitmap); + auto file = fopen(file_path.characters(), "wb"); + fwrite(bmp.data(), sizeof(u8), bmp.size(), file); + fclose(file); +} + void Image::add_layer(NonnullRefPtr layer) { for (auto& existing_layer : m_layers) { diff --git a/Applications/PixelPaint/Image.h b/Applications/PixelPaint/Image.h index 432c8ae229..c4a25b54b2 100644 --- a/Applications/PixelPaint/Image.h +++ b/Applications/PixelPaint/Image.h @@ -68,6 +68,7 @@ public: void paint_into(GUI::Painter&, const Gfx::IntRect& dest_rect); void save(const String& file_path) const; + void export_bmp(const String& file_path); void move_layer_to_front(Layer&); void move_layer_to_back(Layer&); diff --git a/Applications/PixelPaint/ImageEditor.cpp b/Applications/PixelPaint/ImageEditor.cpp index e930658028..3c769cbd8b 100644 --- a/Applications/PixelPaint/ImageEditor.cpp +++ b/Applications/PixelPaint/ImageEditor.cpp @@ -51,9 +51,10 @@ void ImageEditor::set_image(RefPtr image) m_image->remove_client(*this); m_image = move(image); + m_active_layer = nullptr; m_history.reset(*m_image); update(); - image_did_modify_layer_stack(); + relayout(); if (m_image) m_image->add_client(*this); diff --git a/Applications/PixelPaint/main.cpp b/Applications/PixelPaint/main.cpp index 93b64d61be..3089cb74ed 100644 --- a/Applications/PixelPaint/main.cpp +++ b/Applications/PixelPaint/main.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "CreateNewImageDialog.h" #include "CreateNewLayerDialog.h" #include "FilterParams.h" #include "Image.h" @@ -109,6 +110,22 @@ int main(int argc, char** argv) auto menubar = GUI::MenuBar::construct(); auto& app_menu = menubar->add_menu("PixelPaint"); + app_menu.add_action( + GUI::Action::create( + "New", [&](auto&) { + auto dialog = PixelPaint::CreateNewImageDialog::construct(window); + if (dialog->exec() == GUI::Dialog::ExecOK) { + auto image = PixelPaint::Image::create_with_size(dialog->image_size()); + auto bg_layer = PixelPaint::Layer::create_with_size(*image, image->size(), "Background"); + image->add_layer(*bg_layer); + bg_layer->bitmap().fill(Color::White); + + image_editor.set_image(image); + layer_list_widget.set_image(image); + image_editor.set_active_layer(bg_layer); + } + }, + window)); app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { Optional open_path = GUI::FilePicker::get_open_filepath(window); @@ -130,6 +147,22 @@ int main(int argc, char** argv) image_editor.image()->save(save_path.value()); })); + auto& export_submenu = app_menu.add_submenu("Export"); + export_submenu.add_action( + GUI::Action::create( + "As BMP", [&](auto&) { + if (!image_editor.image()) + return; + + Optional save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "bmp"); + + if (!save_path.has_value()) + return; + + image_editor.image()->export_bmp(save_path.value()); + }, + window)); + app_menu.add_separator(); app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { GUI::Application::the()->quit(); -- cgit v1.2.3