Bläddra i källkod

LibGfx+animation: Support writing animated GIF files

Lucas CHOLLET 1 år sedan
förälder
incheckning
777e84b09b

+ 49 - 0
Userland/Libraries/LibGfx/ImageFormats/GIFWriter.cpp

@@ -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);
+}
+
 }
 }

+ 2 - 0
Userland/Libraries/LibGfx/ImageFormats/GIFWriter.h

@@ -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 - 1
Userland/Utilities/animation.cpp

@@ -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) {