diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 4f45ba5d7a..d127fd8703 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (entries.Predictor != TiffPredictor.None) { - TiffThrowHelper.ThrowNotSupported("At the moment support only None Predictor."); + TiffThrowHelper.ThrowNotSupported("At the moment we support only None Predictor images."); } if (entries.SampleFormat != null) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 34b713d6e6..09938582ad 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -76,23 +76,59 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); + where TPixel : unmanaged, IPixel + { + // Because a quantizer is used to create the palette (and therefore changes to the original are expected), + // we do not compare the encoded image against the original: + // Instead we load the encoded image with a reference decoder and compare against that image. + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel + { + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel + { + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs new file mode 100644 index 0000000000..b4ebd088e8 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using ImageMagick; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public static class TiffTestUtils + { + public static void CompareWithReferenceDecoder( + TestImageProvider provider, + Image image, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); + if (useExactComparer) + { + ImageComparer.Exact.VerifySimilarity(magickImage, image); + } + else + { + ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image); + } + } + + public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + using var magickImage = new MagickImage(fileInfo); + magickImage.AutoOrient(); + var result = new Image(configuration, magickImage.Width, magickImage.Height); + + Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels)); + + using IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe(); + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels, + resultPixels.Length); + + return result; + } + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 099aa2d5a1..6bce48eb28 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -505,10 +505,12 @@ namespace SixLabors.ImageSharp.Tests public const string Benchmark_RgbUncompressed = "Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff"; public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; + public const string Calliphora_GrayscaleDeflate_Predictor = "Tiff/Calliphora_gray_deflate_predictor.tiff"; + public const string Calliphora_GrayscaleDeflate = "Tiff/Calliphora_gray_deflate.tiff"; public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; - public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate.tiff"; + public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate_predictor.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw_predictor.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; @@ -516,7 +518,9 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_BiColor = "Tiff/Calliphora_bicolor_uncompressed.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; - public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tiff"; + public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff"; + public const string HuffmanRleAllTermCodes = "Tiff/huffman_rle_all_terminating_codes.tiff"; + public const string HuffmanRleAllMakeupCodes = "Tiff/huffman_rle_all_makeup_codes.tiff"; public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; @@ -549,8 +553,9 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] All = { - Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, - Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, + Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, + Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, + HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, @@ -560,7 +565,7 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff index e50ee6f2a5..df6f89b42a 100644 --- a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff +++ b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a45c92b187e7a59247ccc50f418379e91fd169b39fa7fc8a6dcda9b092fc3013 +oid sha256:c35abf4ea204b130c9c70590581c0bb88566630fbc0fe5bd2dabaa90379dc4f1 size 125776 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff new file mode 100644 index 0000000000..0cf2c2136f --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a019baef7da23cb937a65131ee34b59988ca5ace5d26fe36139d6e6b12e8d59 +size 557710 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff new file mode 100644 index 0000000000..1e316caa48 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f57e87714dca75ce414b01e9a47e4fd0f0ecfd50bc408eb80f3a53cf758e148 +size 630942 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff deleted file mode 100644 index c2ebed3649..0000000000 --- a/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da6e6a35a0bb0f5f2d49e3c5f0eb2deb7118718dd08844f66a6cb72f48b5c489 -size 1476294 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff new file mode 100644 index 0000000000..0784d8875a --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f614a127d6741b0ed335c5fc5e5a2917dd737a4db6afb40ff71b0346e691097a +size 1476268 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff deleted file mode 100644 index 3a37054ccc..0000000000 --- a/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:36b828df14ffda9b64f8eed99714e7af9d6324efe2349a972003af7166fc4629 -size 1792988 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff new file mode 100644 index 0000000000..c0ab53c1d2 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bbfce6af3942b2dc3edaaaac7de64956b1a532c48b43a7b0ba887b2dd98fcc8 +size 1792960 diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff b/tests/Images/Input/Tiff/ccitt_fax3_all_makeup_codes.tiff similarity index 100% rename from tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff rename to tests/Images/Input/Tiff/ccitt_fax3_all_makeup_codes.tiff diff --git a/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff b/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff new file mode 100644 index 0000000000..643805ca77 --- /dev/null +++ b/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8dfeb96763b2b35b5f06f37021d7e33551485105ad4a3a704d76b3aecf039d +size 518 diff --git a/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff b/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff new file mode 100644 index 0000000000..2ab78c71ec --- /dev/null +++ b/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99a488957403e3c35f7dfbbcbe7f187a74e6cab61b233c91f4892079c04984fd +size 550