summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenJilks <benjyjilks@gmail.com>2020-10-18 14:44:33 +0000
committerAndreas Kling <kling@serenityos.org>2020-11-22 16:07:00 +0100
commitd8474d80f200e8fe3d78464f12087a78389d6488 (patch)
tree94306ee2a04f6c39053a77401718743007944155
parent216385084bb04d796af216ee1f1b44386a03f3c9 (diff)
downloadserenity-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.cpp78
-rw-r--r--Applications/PixelPaint/Image.h2
-rw-r--r--Applications/PixelPaint/ImageEditor.cpp1
-rw-r--r--Applications/PixelPaint/main.cpp17
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&) {