LibGfx/GIF: Write the netscape extension block

This allows us to encode the required number of loops in the file.
This commit is contained in:
Lucas CHOLLET 2024-05-18 21:05:33 -04:00 committed by Tim Flynn
parent ebb3d8025c
commit bee8dd76ee
Notes: sideshowbarker 2024-07-17 05:13:53 +09:00
3 changed files with 37 additions and 3 deletions

View file

@ -167,6 +167,36 @@ ErrorOr<void> GIFAnimationWriter::add_frame(Bitmap& bitmap, int duration_ms, Int
return {};
}
ErrorOr<void> write_netscape_extension(BigEndianOutputBitStream& stream, u16 loop_count)
{
// This is a vendor extension, its sole usage is to provide the loop count.
// I used this link as a source: https://web.archive.org/web/19990418091037/http://www6.uniovi.es/gifanim/gifabout.htm
// Extension Introducer
TRY(stream.write_value<u8>(0x21));
// Application Extension Label
TRY(stream.write_value<u8>(0xFF));
// Block Size
constexpr auto netscape_signature = "NETSCAPE2.0"sv;
TRY(stream.write_value<u8>(netscape_signature.length()));
TRY(stream.write_until_depleted(netscape_signature));
// Length of Data Sub-Block
TRY(stream.write_value<u8>(3));
// Undocumented
TRY(stream.write_value<u8>(1));
// Number of loops, 0 means infinite
TRY(stream.write_value<u16>(loop_count));
// Block Terminator
TRY(stream.write_value<u8>(0));
return {};
}
}
ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
@ -187,12 +217,16 @@ ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
return {};
}
ErrorOr<NonnullOwnPtr<AnimationWriter>> GIFWriter::start_encoding_animation(SeekableStream& stream, IntSize dimensions)
ErrorOr<NonnullOwnPtr<AnimationWriter>> GIFWriter::start_encoding_animation(SeekableStream& stream, IntSize dimensions, u16 loop_count)
{
TRY(write_header(stream));
BigEndianOutputBitStream bit_stream { MaybeOwned<Stream> { stream } };
TRY(write_logical_descriptor(bit_stream, dimensions));
// Vendor extension to support looping
TRY(write_netscape_extension(bit_stream, loop_count));
return make<GIFAnimationWriter>(stream);
}

View file

@ -17,7 +17,7 @@ namespace Gfx {
class GIFWriter {
public:
static ErrorOr<void> encode(Stream&, Bitmap const&);
static ErrorOr<NonnullOwnPtr<AnimationWriter>> start_encoding_animation(SeekableStream&, IntSize dimensions);
static ErrorOr<NonnullOwnPtr<AnimationWriter>> start_encoding_animation(SeekableStream&, IntSize dimensions, u16 loop_count);
};
}

View file

@ -51,7 +51,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
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 Gfx::GIFWriter::start_encoding_animation(*output_stream, decoder->size(), decoder->loop_count());
return Error::from_string_literal("Unable to find a encoder for the requested extension.");
}());