mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
PixelPaint: Export image as BMP
You can now export your masterpieces as fresh, crisp BMP files.
This commit is contained in:
parent
d8474d80f2
commit
58c30959b1
Notes:
sideshowbarker
2024-07-19 01:19:12 +09:00
Author: https://github.com/BenJilks Commit: https://github.com/SerenityOS/serenity/commit/58c30959b1e Pull-request: https://github.com/SerenityOS/serenity/pull/3792 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/linusg
7 changed files with 192 additions and 3 deletions
|
@ -1,6 +1,7 @@
|
|||
set(SOURCES
|
||||
BrushTool.cpp
|
||||
BucketTool.cpp
|
||||
CreateNewImageDialog.cpp
|
||||
CreateNewLayerDialog.cpp
|
||||
EllipseTool.cpp
|
||||
EraseTool.cpp
|
||||
|
|
91
Applications/PixelPaint/CreateNewImageDialog.cpp
Normal file
91
Applications/PixelPaint/CreateNewImageDialog.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
49
Applications/PixelPaint/CreateNewImageDialog.h
Normal file
49
Applications/PixelPaint/CreateNewImageDialog.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue