From ad1b0e6dadba957fa924d500900a7213bee717d3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Dec 2023 22:01:55 +1000 Subject: [PATCH] Fix handling gif encoding for global palettes. (#2614) * Handle global ani with 256 palette and no trans * Bump diff for windows --- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 19 ++++++++++++------- .../Formats/Gif/MetadataExtensions.cs | 3 +++ .../Formats/Png/PngEncoderTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 4 +++- .../Images/Input/Gif/global-256-no-trans.gif | 3 +++ 5 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Gif/global-256-no-trans.gif diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 9988848d11..1215768e4b 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -103,7 +103,14 @@ public void Encode(Image image, Stream stream, CancellationToken { // We avoid dithering by default to preserve the original colors. int transparencyIndex = GetTransparentIndex(quantized, frameMetadata); - this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null }, transparencyIndex); + if (transparencyIndex >= 0 || gifMetadata.GlobalColorTable.Value.Length < 256) + { + this.quantizer = new PaletteQuantizer(gifMetadata.GlobalColorTable.Value, new() { Dither = null }, transparencyIndex); + } + else + { + this.quantizer = KnownQuantizers.Octree; + } } else { @@ -198,19 +205,17 @@ private static GifMetadata GetGifMetadata(Image image) private static GifFrameMetadata GetGifFrameMetadata(ImageFrame frame, int transparencyIndex) where TPixel : unmanaged, IPixel { + GifFrameMetadata? metadata = null; if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) { - return (GifFrameMetadata)gif.DeepClone(); + metadata = (GifFrameMetadata)gif.DeepClone(); } - - GifFrameMetadata? metadata = null; - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) + else if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) { AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); metadata = GifFrameMetadata.FromAnimatedMetadata(ani); } - - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) + else if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) { AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); metadata = GifFrameMetadata.FromAnimatedMetadata(ani); diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index 42602f2c78..ad06462e77 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -82,6 +82,9 @@ internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this Gif // has a local palette with 256 colors and is not transparent we should use 'Source'. bool blendSource = source.DisposalMethod == GifDisposalMethod.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); + // If the color table is global and frame has no transparency. Consider it 'Source' also. + blendSource |= source.ColorTableMode == GifColorTableMode.Global && !source.HasTransparency; + return new() { ColorTable = source.LocalColorTable, diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index c81b7eb6cd..e70854b082 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -499,7 +499,7 @@ public void Encode_AnimatedFormatTransform_FromGif(TestImageProvider