// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Reflection; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Tests.Formats; public class GeneralFormatTests { /// /// A collection made up of one file for each image format. /// public static readonly IEnumerable DefaultFiles = [ TestImages.Bmp.Car, TestImages.Jpeg.Baseline.Calliphora, TestImages.Png.Splash, TestImages.Gif.Trans ]; /// /// The collection of image files to test against. /// protected static readonly List Files = [ TestFile.Create(TestImages.Jpeg.Baseline.Calliphora), TestFile.Create(TestImages.Bmp.Car), TestFile.Create(TestImages.Png.Splash), TestFile.Create(TestImages.Gif.Rings) ]; [Theory] [WithFileCollection(nameof(DefaultFiles), PixelTypes.Rgba32)] public void ResolutionShouldChange(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); image.Metadata.VerticalResolution = 150; image.Metadata.HorizontalResolution = 150; image.DebugSave(provider); } [Fact] public void ChainedReadOriginIsRespectedForSeekableStreamsOnLoad() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using Image i = Image.Load(stream); long position1 = stream.Position; Assert.NotEqual(0, position1); using Image j = Image.Load(stream); long position2 = stream.Position; Assert.True(position2 > position1); Assert.NotEqual(i[5, 5], j[5, 5]); } [Fact] public void ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using NonSeekableStream wrapper = new(stream); using Image i = Image.Load(wrapper); Assert.Equal(stream.Length, stream.Position); Assert.Throws(() => { using Image j = Image.Load(wrapper); }); } [Fact] public async Task ChainedReadOriginIsRespectedForSeekableStreamsOnLoadAsync() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using Image i = await Image.LoadAsync(stream); long position1 = stream.Position; Assert.NotEqual(0, position1); using Image j = await Image.LoadAsync(stream); long position2 = stream.Position; Assert.True(position2 > position1); Assert.NotEqual(i[5, 5], j[5, 5]); } [Fact] public async Task ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException_Async() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using NonSeekableStream wrapper = new(stream); using Image i = await Image.LoadAsync(wrapper); Assert.Equal(stream.Length, stream.Position); await Assert.ThrowsAsync(async () => { using Image j = await Image.LoadAsync(wrapper); }); } [Fact] public void ImageCanEncodeToString() { string path = TestEnvironment.CreateOutputDirectory("ToString"); foreach (TestFile file in Files) { using Image image = file.CreateRgba32Image(); string filename = Path.Combine(path, $"{file.FileNameWithoutExtension}.txt"); File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); } } [Fact] public void DecodeThenEncodeImageFromStreamShouldSucceed() { string path = TestEnvironment.CreateOutputDirectory("Encode"); foreach (TestFile file in Files) { using Image image = file.CreateRgba32Image(); image.Save(Path.Combine(path, file.FileName)); } } public static readonly TheoryData QuantizerNames = new() { nameof(KnownQuantizers.Octree), nameof(KnownQuantizers.WebSafe), nameof(KnownQuantizers.Werner), nameof(KnownQuantizers.Wu) }; [Theory] [WithFile(TestImages.Png.CalliphoraPartial, nameof(QuantizerNames), PixelTypes.Rgba32)] [WithFile(TestImages.Png.Bike, nameof(QuantizerNames), PixelTypes.Rgba32)] public void QuantizeImageShouldPreserveMaximumColorPrecision(TestImageProvider provider, string quantizerName) where TPixel : unmanaged, IPixel { IQuantizer quantizer = GetQuantizer(quantizerName); using (Image image = provider.GetImage()) { image.DebugSave(provider, new PngEncoder { ColorType = PngColorType.Palette, Quantizer = quantizer }, testOutputDetails: quantizerName); } provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); } private static IQuantizer GetQuantizer(string name) { PropertyInfo property = typeof(KnownQuantizers).GetTypeInfo().GetProperty(name); return (IQuantizer)property.GetMethod.Invoke(null, []); } [Fact] public void ImageCanConvertFormat() { string path = TestEnvironment.CreateOutputDirectory("Format"); foreach (TestFile file in Files) { using Image image = file.CreateRgba32Image(); using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp"))) { image.SaveAsBmp(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg"))) { image.SaveAsJpeg(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm"))) { image.SaveAsPbm(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.png"))) { image.SaveAsPng(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif"))) { image.SaveAsGif(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga"))) { image.SaveAsTga(output); } using (FileStream output = File.Create(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff"))) { image.SaveAsTiff(output); } } } [Fact] public void ImageShouldPreservePixelByteOrderWhenSerialized() { string path = TestEnvironment.CreateOutputDirectory("Serialized"); foreach (TestFile file in Files) { byte[] serialized; using (Image image = Image.Load(file.Bytes)) using (MemoryStream memoryStream = new()) { image.Save(memoryStream, image.Metadata.DecodedImageFormat); memoryStream.Flush(); serialized = memoryStream.ToArray(); } using Image image2 = Image.Load(serialized); image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}"); } } [Theory] [InlineData(10, 10, "pbm")] [InlineData(100, 100, "pbm")] [InlineData(100, 10, "pbm")] [InlineData(10, 100, "pbm")] [InlineData(10, 10, "png")] [InlineData(100, 100, "png")] [InlineData(100, 10, "png")] [InlineData(10, 100, "png")] [InlineData(10, 10, "gif")] [InlineData(100, 100, "gif")] [InlineData(100, 10, "gif")] [InlineData(10, 100, "gif")] [InlineData(10, 10, "bmp")] [InlineData(100, 100, "bmp")] [InlineData(100, 10, "bmp")] [InlineData(10, 100, "bmp")] [InlineData(10, 10, "jpg")] [InlineData(100, 100, "jpg")] [InlineData(100, 10, "jpg")] [InlineData(10, 100, "jpg")] [InlineData(100, 100, "tga")] [InlineData(100, 10, "tga")] [InlineData(10, 100, "tga")] [InlineData(100, 100, "tiff")] [InlineData(100, 10, "tiff")] [InlineData(10, 100, "tiff")] public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension) { using Image image = Image.LoadPixelData(new Rgba32[width * height], width, height); using MemoryStream memoryStream = new(); IImageFormat format = GetFormat(extension); image.Save(memoryStream, format); memoryStream.Position = 0; ImageInfo imageInfo = Image.Identify(memoryStream); Assert.Equal(imageInfo.Width, width); Assert.Equal(imageInfo.Height, height); Assert.Equal(format, imageInfo.Metadata.DecodedImageFormat); } [Fact] public void Identify_UnknownImageFormatException_WithInvalidStream() { byte[] invalid = new byte[10]; using MemoryStream memoryStream = new(invalid); Assert.Throws(() => Image.Identify(invalid)); } private static IImageFormat GetFormat(string format) => Configuration.Default.ImageFormats .FirstOrDefault(x => x.FileExtensions.Contains(format)); }