// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Text; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; using Xunit; using System.IO; using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; public class GifDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; public static readonly string[] MultiFrameTestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Kumin }; public static readonly string[] BasicVerificationFiles = { TestImages.Gif.Cheers, TestImages.Gif.Rings, // previously DecodeBadApplicationExtensionLength: TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2, // previously DecodeBadDescriptorDimensionsLength: TestImages.Gif.Issues.BadDescriptorWidth }; private static readonly Dictionary BasicVerificationFrameCount = new Dictionary { [TestImages.Gif.Cheers] = 93, [TestImages.Gif.Issues.BadDescriptorWidth] = 36, }; public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; [Theory] [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] public void Decode_VerifyAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSaveMultiFrame(provider); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } } [Theory] [WithFile(TestImages.Gif.Trans, TestPixelTypes)] public void GifDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } [Theory] [WithFileCollection(nameof(BasicVerificationFiles), PixelTypes.Rgba32)] public void Decode_VerifyRootFrameAndFrameCount(TestImageProvider provider) where TPixel : struct, IPixel { if (!BasicVerificationFrameCount.TryGetValue(provider.SourceFileOrDescription, out int expectedFrameCount)) { expectedFrameCount = 1; } using (Image image = provider.GetImage()) { Assert.Equal(expectedFrameCount, image.Frames.Count); image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { var options = new GifDecoder { IgnoreMetadata = false }; var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("Comments", image.MetaData.Properties[0].Name); Assert.Equal("ImageSharp", image.MetaData.Properties[0].Value); } } [Fact] public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() { var options = new GifDecoder { IgnoreMetadata = true }; var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { Assert.Equal(0, image.MetaData.Properties.Count); } } [Fact] public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() { var options = new GifDecoder { TextEncoding = Encoding.Unicode }; var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); } } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void CanDecodeJustOneFrame(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.First })) { Assert.Equal(1, image.Frames.Count); } } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void CanDecodeAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.All })) { Assert.True(image.Frames.Count > 1); } } [Theory] [InlineData(TestImages.Gif.Cheers, 8)] [InlineData(TestImages.Gif.Giphy, 8)] [InlineData(TestImages.Gif.Rings, 8)] [InlineData(TestImages.Gif.Trans, 8)] public void DetectPixelSize(string imagePath, int expectedPixelSize) { TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } [Fact] public void CanDecodeIntermingledImages() { using (var kumin1 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) using (var icon = Image.Load(TestFile.Create(TestImages.Png.Icon).Bytes)) using (var kumin2 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) { for (int i = 0; i < kumin1.Frames.Count; i++) { ImageFrame first = kumin1.Frames[i]; ImageFrame second = kumin2.Frames[i]; first.ComparePixelBufferTo(second.GetPixelSpan()); } } } } }