mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-17 23:12:22 +00:00
LibGfx+animation: Support writing animated GIF files
This commit is contained in:
parent
6a5f8b5163
commit
777e84b09b
Notes:
sideshowbarker
2024-07-17 06:29:49 +09:00
Author: https://github.com/LucasChollet
Commit: 777e84b09b
Pull-request: https://github.com/SerenityOS/serenity/pull/24380
Reviewed-by: https://github.com/nico ✅
3 changed files with 59 additions and 1 deletions
|
@ -127,6 +127,46 @@ ErrorOr<void> write_trailer(Stream& stream)
|
||||||
return {};
|
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)
|
ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
|
||||||
|
@ -147,4 +187,13 @@ ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
|
||||||
return {};
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
|
#include <LibGfx/ImageFormats/AnimationWriter.h>
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ namespace Gfx {
|
||||||
class GIFWriter {
|
class GIFWriter {
|
||||||
public:
|
public:
|
||||||
static ErrorOr<void> encode(Stream&, Bitmap const&);
|
static ErrorOr<void> encode(Stream&, Bitmap const&);
|
||||||
|
static ErrorOr<NonnullOwnPtr<AnimationWriter>> start_encoding_animation(SeekableStream&, IntSize dimensions);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/MappedFile.h>
|
#include <LibCore/MappedFile.h>
|
||||||
#include <LibGfx/ImageFormats/AnimationWriter.h>
|
#include <LibGfx/ImageFormats/AnimationWriter.h>
|
||||||
|
#include <LibGfx/ImageFormats/GIFWriter.h>
|
||||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||||
#include <LibGfx/ImageFormats/WebPWriter.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_file = TRY(Core::File::open(options.out_path, Core::File::OpenMode::Write));
|
||||||
auto output_stream = TRY(Core::OutputBufferedFile::create(move(output_file)));
|
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;
|
RefPtr<Gfx::Bitmap> last_frame;
|
||||||
for (size_t i = 0; i < decoder->frame_count(); ++i) {
|
for (size_t i = 0; i < decoder->frame_count(); ++i) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue