From 05f093765206f636b0a631f0f22bd0dadda7d3f2 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Fri, 14 Apr 2017 19:32:12 +0100 Subject: [PATCH] Add support for WhiteIsZero bilevel & 4-bit images --- .../TiffColorType.cs | 10 ++ .../WhiteIsZero1TiffColor.cs | 53 ++++++ .../WhiteIsZero4TiffColor.cs | 61 +++++++ .../WhiteIsZero8TiffColor.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 44 ++++- .../PhotometricInterpretationTestBase.cs | 3 +- .../WhiteIsZero8TiffColorTests.cs | 51 ------ .../WhiteIsZeroTiffColorTests.cs | 152 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 26 +++ 9 files changed, 342 insertions(+), 60 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs delete mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index da7d6af265..be9e14df40 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -10,6 +10,16 @@ namespace ImageSharp.Formats.Tiff /// internal enum TiffColorType { + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for bilevel images. + /// + WhiteIsZero1, + + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 4-bit images. + /// + WhiteIsZero4, + /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 8-bit images. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs new file mode 100644 index 0000000000..5e486c7fef --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Runtime.CompilerServices; + using ImageSharp; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). + /// + internal static class WhiteIsZero1TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + TColor color = default(TColor); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x += 8) + { + byte b = data[offset++]; + int maxShift = Math.Min(left + width - x, 8); + + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + byte intensity = (bit == 1) ? (byte)0 : (byte)255; + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x + shift, y] = color; + } + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs new file mode 100644 index 0000000000..98f74dca0e --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System.Runtime.CompilerServices; + using ImageSharp; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). + /// + internal static class WhiteIsZero4TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + TColor color = default(TColor); + + uint offset = 0; + bool isOddWidth = (width & 1) == 1; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width - 1; x += 2) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[x, y] = color; + + byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); + color.PackFromBytes(intensity2, intensity2, intensity2, 255); + pixels[x + 1, y] = color; + } + + if (isOddWidth) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[left + width - 1, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 97d7edaca6..8ddafd9833 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Formats.Tiff using ImageSharp; /// - /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// internal static class WhiteIsZero8TiffColor { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d9086c95a7..cc2d0f8b7d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -299,18 +299,38 @@ namespace ImageSharp.Formats { uint[] bitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); - if (bitsPerSample.Length == 1 && bitsPerSample[0] == 8) + if (bitsPerSample.Length == 1) { - this.ColorType = TiffColorType.WhiteIsZero8; - } - else - { - throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + switch (bitsPerSample[0]) + { + case 8: + { + this.ColorType = TiffColorType.WhiteIsZero8; + break; + } + + case 4: + { + this.ColorType = TiffColorType.WhiteIsZero4; + break; + } + + case 1: + { + this.ColorType = TiffColorType.WhiteIsZero1; + break; + } + + default: + { + throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + } + } } } else { - throw new NotSupportedException("TIFF bilevel images are not supported."); + this.ColorType = TiffColorType.WhiteIsZero1; } break; @@ -331,6 +351,10 @@ namespace ImageSharp.Formats { switch (this.ColorType) { + case TiffColorType.WhiteIsZero1: + return ((width + 7) / 8) * height; + case TiffColorType.WhiteIsZero4: + return ((width + 1) / 2) * height; case TiffColorType.WhiteIsZero8: return width * height; default: @@ -373,6 +397,12 @@ namespace ImageSharp.Formats { switch (this.ColorType) { + case TiffColorType.WhiteIsZero1: + WhiteIsZero1TiffColor.Decode(data, pixels, left, top, width, height); + break; + case TiffColorType.WhiteIsZero4: + WhiteIsZero4TiffColor.Decode(data, pixels, left, top, width, height); + break; case TiffColorType.WhiteIsZero8: WhiteIsZero8TiffColor.Decode(data, pixels, left, top, width, height); break; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 7fdb121772..3c245855d3 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -50,7 +50,8 @@ namespace ImageSharp.Tests { for (int x = 0; x < resultWidth; x++) { - Assert.Equal(expectedResult[y][x], pixels[x, y]); + Assert.True(expectedResult[y][x] == pixels[x, y], + $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x,y]}"); } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs deleted file mode 100644 index 7b2513ce5c..0000000000 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - - public class WhiteIsZero8TiffColorTests : PhotometricInterpretationTestBase - { - private static Color Gray000 = new Color(255, 255, 255, 255); - private static Color Gray128 = new Color(127, 127, 127, 255); - private static Color Gray255 = new Color(0, 0, 0, 255); - - private static byte[] GrayscaleBytes4x4 = new byte[] { 128, 255, 000, 255, - 255, 255, 255, 255, - 000, 128, 128, 255, - 255, 000, 255, 128 }; - - private static Color[][] GrayscaleResult4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 }}; - - public static IEnumerable DecodeData - { - get - { - yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, GrayscaleResult4x4 }; - yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, Offset(GrayscaleResult4x4, 0, 0, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 1, 0, 4, 4, Offset(GrayscaleResult4x4, 1, 0, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 0, 1, 4, 4, Offset(GrayscaleResult4x4, 0, 1, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 1, 1, 4, 4, Offset(GrayscaleResult4x4, 1, 1, 6, 6) }; - } - } - - [Theory] - [MemberData(nameof(DecodeData))] - public void Decode_WritesPixelData(byte[] inputData, int left, int top, int width, int height, Color[][] expectedResult) - { - AssertDecode(expectedResult, pixels => - { - WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); - }); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs new file mode 100644 index 0000000000..8769d472ba --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -0,0 +1,152 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase + { + private static Color Gray000 = new Color(255, 255, 255, 255); + private static Color Gray128 = new Color(127, 127, 127, 255); + private static Color Gray255 = new Color(0, 0, 0, 255); + private static Color Gray0 = new Color(255, 255, 255, 255); + private static Color Gray8 = new Color(119, 119, 119, 255); + private static Color GrayF = new Color(0, 0, 0, 255); + private static Color Bit0 = new Color(255, 255, 255, 255); + private static Color Bit1 = new Color(0, 0, 0, 255); + + private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, + 0b11110000, + 0b01110000, + 0b10010000 }; + + private static Color[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit1 }, + new[] { Bit1, Bit0, Bit0, Bit1 }}; + + private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, + 0b11111111, 0b11111111, + 0b01101001, 0b10100000, + 0b10010000, 0b01100000}; + + private static Color[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, + new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; + + private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, + 0xFF, 0xFF, + 0x08, 0x8F, + 0xF0, 0xF8 }; + + private static Color[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + new[] { GrayF, GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8, GrayF }, + new[] { GrayF, Gray0, GrayF, Gray8 }}; + + private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, + 0xFF, 0xF0, + 0x08, 0x80, + 0xF0, 0xF0 }; + + private static Color[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + new[] { GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8 }, + new[] { GrayF, Gray0, GrayF }}; + + private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 }; + + private static Color[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + new[] { Gray255, Gray255, Gray255, Gray255 }, + new[] { Gray000, Gray128, Gray128, Gray255 }, + new[] { Gray255, Gray000, Gray255, Gray128 }}; + + public static IEnumerable Bilevel_Data + { + get + { + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Bilevel_Result4x4 }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Offset(Bilevel_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 0, 4, 4, Offset(Bilevel_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 1, 4, 4, Offset(Bilevel_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 1, 4, 4, Offset(Bilevel_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Bilevel_Result12x4 }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Offset(Bilevel_Result12x4, 0, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 0, 12, 4, Offset(Bilevel_Result12x4, 1, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 1, 12, 4, Offset(Bilevel_Result12x4, 0, 1, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 1, 12, 4, Offset(Bilevel_Result12x4, 1, 1, 18, 6) }; + } + } + + public static IEnumerable Grayscale4_Data + { + get + { + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Grayscale4_Result4x4 }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Offset(Grayscale4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 0, 4, 4, Offset(Grayscale4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 1, 4, 4, Offset(Grayscale4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 1, 4, 4, Offset(Grayscale4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Grayscale4_Result3x4 }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Offset(Grayscale4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 0, 3, 4, Offset(Grayscale4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 1, 3, 4, Offset(Grayscale4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 1, 3, 4, Offset(Grayscale4_Result3x4, 1, 1, 6, 6) }; + } + } + + public static IEnumerable Grayscale8_Data + { + get + { + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Grayscale8_Result4x4 }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Offset(Grayscale8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 0, 4, 4, Offset(Grayscale8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 1, 4, 4, Offset(Grayscale8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 1, 4, 4, Offset(Grayscale8_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Bilevel_Data))] + public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale4_Data))] + public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale8_Data))] + public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index b949318d91..f302c17b30 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -178,6 +178,10 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -192,6 +196,23 @@ namespace ImageSharp.Tests Assert.Equal((TiffColorType)colorType, decoder.ColorType); } + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] + public void ReadImageFormat_DeterminesCorrectColorImplementation_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation, int colorType) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithoutEntry(TiffTags.BitsPerSample) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffColorType)colorType, decoder.ColorType); + } + // [Theory] // [InlineData(false, new[] { 8 }, TiffColorType.WhiteIsZero8)] // [InlineData(true, new[] { 8 }, TiffColorType.WhiteIsZero8)] @@ -285,9 +306,14 @@ namespace ImageSharp.Tests [Theory] [InlineData(TiffColorType.WhiteIsZero8, 100, 80, 100 * 80)] + [InlineData(TiffColorType.WhiteIsZero4, 100, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero4, 99, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero1, 160, 80, 20 * 80)] + [InlineData(TiffColorType.WhiteIsZero1, 153, 80, 20 * 80)] public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); + decoder.ColorType = (TiffColorType)colorType; int bufferSize = decoder.CalculateImageBufferSize(width, height);