diff --git a/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.cpp b/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.cpp index 287d7e08e8b..12b6897f786 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.cpp @@ -127,6 +127,46 @@ ErrorOr write_trailer(Stream& stream) return {}; } +class GIFAnimationWriter : public AnimationWriter { +public: + GIFAnimationWriter(SeekableStream& stream) + : m_stream(stream) + { + } + + virtual ErrorOr add_frame(Bitmap&, int, IntPoint) override; + +private: + SeekableStream& m_stream; + bool m_is_first_frame { true }; +}; + +ErrorOr GIFAnimationWriter::add_frame(Bitmap& bitmap, int duration_ms, IntPoint at = {}) +{ + // FIXME: Consider frame's duration and position + (void)duration_ms; + (void)at; + + // Let's get rid of the previously written trailer + if (!m_is_first_frame) + TRY(m_stream.seek(-1, SeekMode::FromEndPosition)); + + m_is_first_frame = false; + + // Write a Table-Based Image + BigEndianOutputBitStream bit_stream { MaybeOwned { m_stream } }; + TRY(write_image_descriptor(bit_stream, bitmap)); + + auto const palette = TRY(median_cut(bitmap, 256)); + TRY(write_color_table(m_stream, palette)); + TRY(write_image_data(m_stream, bitmap, palette)); + + // We always write a trailer to ensure that the file is valid. + TRY(write_trailer(m_stream)); + + return {}; +} + } ErrorOr GIFWriter::encode(Stream& stream, Bitmap const& bitmap) @@ -147,4 +187,13 @@ ErrorOr GIFWriter::encode(Stream& stream, Bitmap const& bitmap) return {}; } +ErrorOr> GIFWriter::start_encoding_animation(SeekableStream& stream, IntSize dimensions) +{ + TRY(write_header(stream)); + + BigEndianOutputBitStream bit_stream { MaybeOwned { stream } }; + TRY(write_logical_descriptor(bit_stream, dimensions)); + return make(stream); +} + } diff --git a/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.h b/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.h index d31c95c8d02..e959616f4d8 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.h +++ b/Userland/Libraries/LibGfx/ImageFormats/GIFWriter.h @@ -8,6 +8,7 @@ #include #include +#include namespace Gfx { @@ -16,6 +17,7 @@ namespace Gfx { class GIFWriter { public: static ErrorOr encode(Stream&, Bitmap const&); + static ErrorOr> start_encoding_animation(SeekableStream&, IntSize dimensions); }; } diff --git a/Userland/Utilities/animation.cpp b/Userland/Utilities/animation.cpp index b63ddbbeafa..ae7ca04830e 100644 --- a/Userland/Utilities/animation.cpp +++ b/Userland/Utilities/animation.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,13 @@ ErrorOr serenity_main(Main::Arguments arguments) auto output_file = TRY(Core::File::open(options.out_path, Core::File::OpenMode::Write)); auto output_stream = TRY(Core::OutputBufferedFile::create(move(output_file))); - auto animation_writer = TRY(Gfx::WebPWriter::start_encoding_animation(*output_stream, decoder->size(), decoder->loop_count())); + auto animation_writer = TRY([&]() -> ErrorOr> { + if (options.out_path.ends_with(".webp"sv)) + return Gfx::WebPWriter::start_encoding_animation(*output_stream, decoder->size(), decoder->loop_count()); + if (options.out_path.ends_with(".gif"sv)) + return Gfx::GIFWriter::start_encoding_animation(*output_stream, decoder->size()); + return Error::from_string_literal("Unable to find a encoder for the requested extension."); + }()); RefPtr last_frame; for (size_t i = 0; i < decoder->frame_count(); ++i) {