diff --git a/Userland/Applications/PixelPaint/Image.cpp b/Userland/Applications/PixelPaint/Image.cpp index 7b317c1f6f..014d085f1a 100644 --- a/Userland/Applications/PixelPaint/Image.cpp +++ b/Userland/Applications/PixelPaint/Image.cpp @@ -55,7 +55,7 @@ Image::Image(Gfx::IntSize const& size) { } -void Image::paint_into(GUI::Painter& painter, Gfx::IntRect const& dest_rect) +void Image::paint_into(GUI::Painter& painter, Gfx::IntRect const& dest_rect) const { float scale = (float)dest_rect.width() / (float)rect().width(); Gfx::PainterStateSaver saver(painter); @@ -188,30 +188,52 @@ Result Image::write_to_file(const String& file_path) const return {}; } -void Image::export_bmp(String const& file_path) +RefPtr Image::try_compose_bitmap() const { auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, m_size); + if (!bitmap) + return nullptr; 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); + return bitmap; } -void Image::export_png(String const& file_path) +Result Image::export_bmp_to_file(String const& file_path) { - auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, m_size); - VERIFY(bitmap); - GUI::Painter painter(*bitmap); - paint_into(painter, { 0, 0, m_size.width(), m_size.height() }); + auto file_or_error = Core::File::open(file_path, (Core::OpenMode)(Core::OpenMode::WriteOnly | Core::OpenMode::Truncate)); + if (file_or_error.is_error()) + return file_or_error.error(); - auto png = Gfx::PNGWriter::encode(*bitmap); - auto file = fopen(file_path.characters(), "wb"); - fwrite(png.data(), sizeof(u8), png.size(), file); - fclose(file); + auto bitmap = try_compose_bitmap(); + if (!bitmap) + return String { "Failed to allocate bitmap for encoding"sv }; + + Gfx::BMPWriter dumper; + auto encoded_data = dumper.dump(bitmap); + + auto& file = *file_or_error.value(); + if (!file.write(encoded_data.data(), encoded_data.size())) + return String { "Failed to write encoded BMP data to file"sv }; + + return {}; +} + +Result Image::export_png_to_file(String const& file_path) +{ + auto file_or_error = Core::File::open(file_path, (Core::OpenMode)(Core::OpenMode::WriteOnly | Core::OpenMode::Truncate)); + if (file_or_error.is_error()) + return file_or_error.error(); + + auto bitmap = try_compose_bitmap(); + if (!bitmap) + return String { "Failed to allocate bitmap for encoding"sv }; + + auto encoded_data = Gfx::PNGWriter::encode(*bitmap); + auto& file = *file_or_error.value(); + if (!file.write(encoded_data.data(), encoded_data.size())) + return String { "Failed to write encoded PNG data to file"sv }; + + return {}; } void Image::add_layer(NonnullRefPtr layer) diff --git a/Userland/Applications/PixelPaint/Image.h b/Userland/Applications/PixelPaint/Image.h index 1029558e95..2bcf87be7f 100644 --- a/Userland/Applications/PixelPaint/Image.h +++ b/Userland/Applications/PixelPaint/Image.h @@ -41,6 +41,9 @@ public: static Result, String> try_create_from_file(String const& file_path); static RefPtr try_create_from_bitmap(NonnullRefPtr); + // This generates a new Bitmap with the final image (all layers composed according to their attributes.) + RefPtr try_compose_bitmap() const; + size_t layer_count() const { return m_layers.size(); } Layer const& layer(size_t index) const { return m_layers.at(index); } Layer& layer(size_t index) { return m_layers.at(index); } @@ -52,10 +55,10 @@ public: RefPtr take_snapshot() const; void restore_snapshot(Image const&); - void paint_into(GUI::Painter&, Gfx::IntRect const& dest_rect); + void paint_into(GUI::Painter&, Gfx::IntRect const& dest_rect) const; Result write_to_file(String const& file_path) const; - void export_bmp(String const& file_path); - void export_png(String const& file_path); + Result export_bmp_to_file(String const& file_path); + Result export_png_to_file(String const& file_path); void move_layer_to_front(Layer&); void move_layer_to_back(Layer&); diff --git a/Userland/Applications/PixelPaint/main.cpp b/Userland/Applications/PixelPaint/main.cpp index 5b30c6bbf7..3790ab4856 100644 --- a/Userland/Applications/PixelPaint/main.cpp +++ b/Userland/Applications/PixelPaint/main.cpp @@ -138,10 +138,12 @@ int main(int argc, char** argv) "As &BMP", [&](auto&) { if (!image_editor.image()) return; - Optional save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "bmp"); + auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "bmp"); if (!save_path.has_value()) return; - image_editor.image()->export_bmp(save_path.value()); + auto result = image_editor.image()->export_bmp_to_file(save_path.value()); + if (result.is_error()) + GUI::MessageBox::show_error(window, String::formatted("Export to BMP failed: {}", result.error())); }, window)); export_submenu.add_action( @@ -149,13 +151,12 @@ int main(int argc, char** argv) "As &PNG", [&](auto&) { if (!image_editor.image()) return; - - Optional save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "png"); - + auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "png"); if (!save_path.has_value()) return; - - image_editor.image()->export_png(save_path.value()); + auto result = image_editor.image()->export_bmp_to_file(save_path.value()); + if (result.is_error()) + GUI::MessageBox::show_error(window, String::formatted("Export to PNG failed: {}", result.error())); }, window));