diff options
author | BenJilks <benjyjilks@gmail.com> | 2020-10-18 14:44:33 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-11-22 16:07:00 +0100 |
commit | d8474d80f200e8fe3d78464f12087a78389d6488 (patch) | |
tree | 94306ee2a04f6c39053a77401718743007944155 | |
parent | 216385084bb04d796af216ee1f1b44386a03f3c9 (diff) | |
download | serenity-d8474d80f200e8fe3d78464f12087a78389d6488.zip |
PixelPaint: Save and load to and from disk
Store a PixelPaint project in a .pp file (as there doesn't seem to
be any real standard on this). It's a very simple json file that
contains the bitmap as a base64 encoded bmp.
-rw-r--r-- | Applications/PixelPaint/Image.cpp | 78 | ||||
-rw-r--r-- | Applications/PixelPaint/Image.h | 2 | ||||
-rw-r--r-- | Applications/PixelPaint/ImageEditor.cpp | 1 | ||||
-rw-r--r-- | Applications/PixelPaint/main.cpp | 17 |
4 files changed, 94 insertions, 4 deletions
diff --git a/Applications/PixelPaint/Image.cpp b/Applications/PixelPaint/Image.cpp index 470bbbf985..57f83d7a9a 100644 --- a/Applications/PixelPaint/Image.cpp +++ b/Applications/PixelPaint/Image.cpp @@ -26,7 +26,15 @@ #include "Image.h" #include "Layer.h" +#include <AK/Base64.h> +#include <AK/JsonObject.h> +#include <AK/JsonObjectSerializer.h> +#include <AK/JsonValue.h> +#include <AK/StringBuilder.h> #include <LibGUI/Painter.h> +#include <LibGfx/BMPDumper.h> +#include <LibGfx/ImageDecoder.h> +#include <stdio.h> //#define PAINT_DEBUG @@ -62,6 +70,76 @@ void Image::paint_into(GUI::Painter& painter, const Gfx::IntRect& dest_rect) } } +RefPtr<Image> Image::create_from_file(const String& file_path) +{ + auto file = fopen(file_path.characters(), "r"); + fseek(file, 0L, SEEK_END); + auto length = ftell(file); + rewind(file); + + auto buffer = ByteBuffer::create_uninitialized(length); + fread(buffer.data(), sizeof(u8), length, file); + fclose(file); + + auto json_or_error = JsonValue::from_string(String::copy(buffer)); + if (!json_or_error.has_value()) + return nullptr; + + auto json = json_or_error.value().as_object(); + auto image = create_with_size({ json.get("width").to_i32(), json.get("height").to_i32() }); + json.get("layers").as_array().for_each([&](JsonValue json_layer) { + auto json_layer_object = json_layer.as_object(); + auto width = json_layer_object.get("width").to_i32(); + auto height = json_layer_object.get("height").to_i32(); + auto name = json_layer_object.get("name").as_string(); + auto layer = Layer::create_with_size(*image, { width, height }, name); + layer->set_location({ json_layer_object.get("locationx").to_i32(), json_layer_object.get("locationy").to_i32() }); + layer->set_opacity_percent(json_layer_object.get("opacity_percent").to_i32()); + layer->set_visible(json_layer_object.get("visable").as_bool()); + layer->set_selected(json_layer_object.get("selected").as_bool()); + + auto bitmap_base64_encoded = json_layer_object.get("bitmap").as_string(); + auto bitmap_data = decode_base64(bitmap_base64_encoded); + auto image_decoder = Gfx::ImageDecoder::create(bitmap_data); + layer->set_bitmap(*image_decoder->bitmap()); + image->add_layer(*layer); + }); + + return image; +} + +void Image::save(const String& file_path) const +{ + // Build json file + StringBuilder builder; + JsonObjectSerializer json(builder); + json.add("width", m_size.width()); + json.add("height", m_size.height()); + { + auto json_layers = json.add_array("layers"); + for (const auto& layer : m_layers) { + Gfx::BMPDumper bmp_dumber; + auto json_layer = json_layers.add_object(); + json_layer.add("width", layer.size().width()); + json_layer.add("height", layer.size().height()); + json_layer.add("name", layer.name()); + json_layer.add("locationx", layer.location().x()); + json_layer.add("locationy", layer.location().y()); + json_layer.add("opacity_percent", layer.opacity_percent()); + json_layer.add("visable", layer.is_visible()); + json_layer.add("selected", layer.is_selected()); + json_layer.add("bitmap", encode_base64(bmp_dumber.dump(layer.bitmap()))); + } + } + json.finish(); + + // Write json to disk + auto file = fopen(file_path.characters(), "w"); + auto byte_buffer = builder.to_byte_buffer(); + fwrite(byte_buffer.data(), sizeof(u8), byte_buffer.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 fc944c17d2..432c8ae229 100644 --- a/Applications/PixelPaint/Image.h +++ b/Applications/PixelPaint/Image.h @@ -53,6 +53,7 @@ public: class Image : public RefCounted<Image> { public: static RefPtr<Image> create_with_size(const Gfx::IntSize&); + static RefPtr<Image> create_from_file(const String& file_path); size_t layer_count() const { return m_layers.size(); } const Layer& layer(size_t index) const { return m_layers.at(index); } @@ -66,6 +67,7 @@ public: void restore_snapshot(const Image&); void paint_into(GUI::Painter&, const Gfx::IntRect& dest_rect); + void save(const String& file_path) const; 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 1e8d87184e..e930658028 100644 --- a/Applications/PixelPaint/ImageEditor.cpp +++ b/Applications/PixelPaint/ImageEditor.cpp @@ -53,6 +53,7 @@ void ImageEditor::set_image(RefPtr<Image> image) m_image = move(image); m_history.reset(*m_image); update(); + image_did_modify_layer_stack(); if (m_image) m_image->add_client(*this); diff --git a/Applications/PixelPaint/main.cpp b/Applications/PixelPaint/main.cpp index e979f3d740..93b64d61be 100644 --- a/Applications/PixelPaint/main.cpp +++ b/Applications/PixelPaint/main.cpp @@ -115,11 +115,20 @@ int main(int argc, char** argv) if (!open_path.has_value()) return; - auto bitmap = Gfx::Bitmap::load_from_file(open_path.value()); - if (!bitmap) { - GUI::MessageBox::show(window, String::formatted("Failed to load '{}'", open_path.value()), "Open failed", GUI::MessageBox::Type::Error); + auto image = PixelPaint::Image::create_from_file(open_path.value()); + image_editor.set_image(image); + layer_list_widget.set_image(image); + })); + app_menu.add_action(GUI::CommonActions::make_save_as_action([&](auto&) { + if (!image_editor.image()) return; - } + + Optional<String> save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "pp"); + + if (!save_path.has_value()) + return; + + image_editor.image()->save(save_path.value()); })); app_menu.add_separator(); app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { |