diff options
Diffstat (limited to 'Applications/PixelPaint')
-rw-r--r-- | Applications/PixelPaint/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Applications/PixelPaint/CreateNewImageDialog.cpp | 91 | ||||
-rw-r--r-- | Applications/PixelPaint/CreateNewImageDialog.h | 49 | ||||
-rw-r--r-- | Applications/PixelPaint/Image.cpp | 17 | ||||
-rw-r--r-- | Applications/PixelPaint/Image.h | 1 | ||||
-rw-r--r-- | Applications/PixelPaint/ImageEditor.cpp | 3 | ||||
-rw-r--r-- | Applications/PixelPaint/main.cpp | 33 |
7 files changed, 192 insertions, 3 deletions
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 <benjyjilks@gmail.com> + * 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 <LibGUI/BoxLayout.h> +#include <LibGUI/Button.h> +#include <LibGUI/Label.h> +#include <LibGUI/SpinBox.h> +#include <LibGUI/TextBox.h> + +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<GUI::Widget>(); + main_widget.set_fill_with_background_color(true); + + auto& layout = main_widget.set_layout<GUI::VerticalBoxLayout>(); + layout.set_margins({ 4, 4, 4, 4 }); + + auto& name_label = main_widget.add<GUI::Label>("Name:"); + name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + m_name_textbox = main_widget.add<GUI::TextBox>(); + m_name_textbox->on_change = [this] { + m_image_name = m_name_textbox->text(); + }; + + auto& width_label = main_widget.add<GUI::Label>("Width:"); + width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto& width_spinbox = main_widget.add<GUI::SpinBox>(); + + auto& height_label = main_widget.add<GUI::Label>("Height:"); + height_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto& height_spinbox = main_widget.add<GUI::SpinBox>(); + + auto& button_container = main_widget.add<GUI::Widget>(); + button_container.set_layout<GUI::HorizontalBoxLayout>(); + + auto& ok_button = button_container.add<GUI::Button>("OK"); + ok_button.on_click = [this](auto) { + done(ExecOK); + }; + + auto& cancel_button = button_container.add<GUI::Button>("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 <benjyjilks@gmail.com> + * 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 <LibGUI/Dialog.h> + +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<GUI::TextBox> 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 <AK/JsonValue.h> #include <AK/StringBuilder.h> #include <LibGUI/Painter.h> -#include <LibGfx/BMPDumper.h> +#include <LibGfx/BMPWriter.h> #include <LibGfx/ImageDecoder.h> #include <stdio.h> @@ -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> 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> 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<String> 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<String> 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(); |