LibGfx+animation: Support writing animated GIF files

This commit is contained in:
Lucas CHOLLET 2024-05-18 20:26:48 -04:00 committed by Andreas Kling
parent 6a5f8b5163
commit 777e84b09b
Notes: sideshowbarker 2024-07-17 06:29:49 +09:00
3 changed files with 59 additions and 1 deletions

View file

@ -127,6 +127,46 @@ ErrorOr<void> write_trailer(Stream& stream)
return {};
}
class GIFAnimationWriter : public AnimationWriter {
public:
GIFAnimationWriter(SeekableStream& stream)
: m_stream(stream)
{
}
virtual ErrorOr<void> add_frame(Bitmap&, int, IntPoint) override;
private:
SeekableStream& m_stream;
bool m_is_first_frame { true };
};
ErrorOr<void> 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<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
@ -147,4 +187,13 @@ ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
return {};
}
ErrorOr<NonnullOwnPtr<AnimationWriter>> GIFWriter::start_encoding_animation(SeekableStream& stream, IntSize dimensions)
{
TRY(write_header(stream));
BigEndianOutputBitStream bit_stream { MaybeOwned<Stream> { stream } };
TRY(write_logical_descriptor(bit_stream, dimensions));
return make<GIFAnimationWriter>(stream);
}
}

View file

@ -8,6 +8,7 @@
#include <AK/Error.h>
#include <LibGfx/Forward.h>
#include <LibGfx/ImageFormats/AnimationWriter.h>
namespace Gfx {
@ -16,6 +17,7 @@ namespace Gfx {
class GIFWriter {
public:
static ErrorOr<void> encode(Stream&, Bitmap const&);
static ErrorOr<NonnullOwnPtr<AnimationWriter>> start_encoding_animation(SeekableStream&, IntSize dimensions);
};
}

View file

@ -8,6 +8,7 @@
#include <LibCore/File.h>
#include <LibCore/MappedFile.h>
#include <LibGfx/ImageFormats/AnimationWriter.h>
#include <LibGfx/ImageFormats/GIFWriter.h>
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ImageFormats/WebPWriter.h>
@ -46,7 +47,13 @@ ErrorOr<int> 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<NonnullOwnPtr<Gfx::AnimationWriter>> {
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<Gfx::Bitmap> last_frame;
for (size_t i = 0; i < decoder->frame_count(); ++i) {