From 1d77c2e5a2234e82c2fb6a888c0382c8319a2108 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 27 Aug 2020 20:47:07 +0300 Subject: [PATCH] Improve decoders - performance and memory usage. Implement Identify methods and add tests. Report not supported Predictor and SampleFormat features. --- .../Tiff/Compression/CompressionFactory.cs | 42 ----- .../Compression/DeflateTiffCompression.cs | 23 ++- .../Tiff/Compression/LzwTiffCompression.cs | 22 +-- .../Tiff/Compression/NoneTiffCompression.cs | 20 +-- .../Compression/PackBitsTiffCompression.cs | 80 ++++----- .../Tiff/Compression/TiffBaseCompression.cs | 29 +++ .../Compression/TiffCompressionFactory.cs | 28 +++ .../Formats/Tiff/Constants/TiffPredictor.cs | 26 +++ .../Tiff/Constants/TiffSampleFormat.cs | 41 +++++ .../Formats/Tiff/Ifd/EntryReader.cs | 1 + .../BlackIsZero1TiffColor.cs | 20 +-- .../BlackIsZero4TiffColor.cs | 19 +- .../BlackIsZero8TiffColor.cs | 20 +-- .../BlackIsZeroTiffColor.cs | 22 +-- .../PaletteTiffColor.cs | 23 +-- .../Rgb888TiffColor.cs | 24 +-- .../RgbPlanarTiffColor.cs | 23 ++- .../PhotometricInterpretation/RgbTiffColor.cs | 31 ++-- ...olorDecoder.cs => TiffBaseColorDecoder.cs} | 20 +-- .../TiffColorDecoderFactory.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 20 +-- .../WhiteIsZero4TiffColor.cs | 19 +- .../WhiteIsZero8TiffColor.cs | 20 +-- .../WhiteIsZeroTiffColor.cs | 19 +- .../Formats/Tiff/Streams/TiffStreamFactory.cs | 10 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 29 +-- .../Formats/Tiff/TiffDecoderCore.cs | 168 +++++++++++------- .../Formats/Tiff/TiffDecoderHelpers.cs | 36 ++-- .../Formats/Tiff/TiffFrameMetadata.cs | 99 ++++++----- .../Formats/Tiff/Utils/BitReader.cs | 14 +- .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 6 +- .../Formats/Tiff/Utils/TiffUtils.cs | 7 +- .../Profiles/Exif/Tags/ExifTag.LongArray.cs | 1 - .../Codecs/DecodeTiff.cs | 2 +- tests/ImageSharp.Tests/FileTestBase.cs | 4 +- .../DeflateTiffCompressionTests.cs | 3 +- .../Compression/LzwTiffCompressionTests.cs | 2 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 3 +- .../RgbPlanarTiffColorTests.cs | 11 +- .../Formats/Tiff/TiffDecoderTests.cs | 23 +++ .../Formats/Tiff/TiffMetadataTests.cs | 20 ++- tests/ImageSharp.Tests/TestImages.cs | 21 ++- .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../net472/Decode_Rgba32_metadata_sample.png | 3 - .../net472/Decode_Rgba32_multipage_lzw.png | 3 - .../net472/Decode_Rgba32_rgb_deflate.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../net472/Decode_Rgba32_rgb_small_lzw.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../Decode_Rgba32_metadata_sample.png | 3 - .../Decode_Rgba32_multipage_lzw.png | 3 - .../Decode_Rgba32_rgb_deflate.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../Decode_Rgba32_rgb_small_deflate.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../Decode_Rgba32_metadata_sample.png | 3 - .../Decode_Rgba32_multipage_lzw.png | 3 - .../Decode_Rgba32_rgb_deflate.png | 3 - .../netcoreapp3.1/Decode_Rgba32_rgb_lzw.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../Decode_Rgba32_rgb_small_lzw.png | 3 + tests/Images/Input/Tiff/metadata_sample.tiff | 4 +- .../Images/Input/Tiff/rgb_small_deflate.tiff | 3 + tests/Images/Input/Tiff/rgb_small_lzw.tiff | 3 + 68 files changed, 583 insertions(+), 548 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{TiffColorDecoder.cs => TiffBaseColorDecoder.cs} (61%) delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png create mode 100644 tests/Images/Input/Tiff/rgb_small_deflate.tiff create mode 100644 tests/Images/Input/Tiff/rgb_small_lzw.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs deleted file mode 100644 index f65b3caf3c..0000000000 --- a/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - internal static class CompressionFactory - { - /// - /// Decompresses an image block from the input stream into the specified buffer. - /// - /// The input stream. - /// Type of the compression. - /// The offset within the file of the image block. - /// The size (in bytes) of the compressed data. - /// The buffer to write the uncompressed data. - public static void DecompressImageBlock(Stream stream, TiffCompressionType compressionType, uint offset, uint byteCount, byte[] buffer) - { - stream.Seek(offset, SeekOrigin.Begin); - - switch (compressionType) - { - case TiffCompressionType.None: - NoneTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.PackBits: - PackBitsTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.Deflate: - DeflateTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.Lzw: - LzwTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - default: - throw new InvalidOperationException(); - } - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index f5295de4a6..e10d8195b0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -4,7 +4,7 @@ using System; using System.IO; using System.IO.Compression; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type. /// - internal static class DeflateTiffCompression + internal class DeflateTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public DeflateTiffCompression(MemoryAllocator allocator) + : base(allocator) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) { // Read the 'zlib' header information int cmf = stream.ReadByte(); @@ -47,8 +46,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff // The subsequent data is the Deflate compressed data (except for the last four bytes of checksum) int headerLength = fdict ? 10 : 6; - SubStream subStream = new SubStream(stream, byteCount - headerLength); - using (DeflateStream deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) + var subStream = new SubStream(stream, byteCount - headerLength); + using (var deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) { deflateStream.ReadFull(buffer); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index 2c244b6064..bba9739e22 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -1,26 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. /// - internal static class LzwTiffCompression + internal class LzwTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public LzwTiffCompression(MemoryAllocator allocator) + : base(allocator) { - SubStream subStream = new SubStream(stream, byteCount); + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + var subStream = new SubStream(stream, byteCount); using (var decoder = new TiffLzwDecoder(subStream)) { decoder.DecodePixels(buffer.Length, 8, buffer); diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index c78e22b41e..ad6801c6a4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -1,24 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is not compressed. /// - internal static class NoneTiffCompression + internal class NoneTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public NoneTiffCompression(MemoryAllocator allocator) + : base(allocator) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) { stream.ReadFull(buffer, byteCount); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index 9d5f041bfd..9862fea744 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -4,69 +4,63 @@ using System; using System.Buffers; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. /// - internal static class PackBitsTiffCompression + internal class PackBitsTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public PackBitsTiffCompression(MemoryAllocator allocator) + : base(allocator) { - byte[] compressedData = ArrayPool.Shared.Rent(byteCount); + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + using IMemoryOwner compressedDataMemory = this.Allocator.Allocate(byteCount); - try + Span compressedData = compressedDataMemory.GetSpan(); + + stream.ReadFull(compressedData, byteCount); + int compressedOffset = 0; + int decompressedOffset = 0; + + while (compressedOffset < byteCount) { - stream.ReadFull(compressedData, byteCount); - int compressedOffset = 0; - int decompressedOffset = 0; + byte headerByte = compressedData[compressedOffset]; - while (compressedOffset < byteCount) + if (headerByte <= (byte)127) { - byte headerByte = compressedData[compressedOffset]; - - if (headerByte <= (byte)127) - { - int literalOffset = compressedOffset + 1; - int literalLength = compressedData[compressedOffset] + 1; + int literalOffset = compressedOffset + 1; + int literalLength = compressedData[compressedOffset] + 1; - Array.Copy(compressedData, literalOffset, buffer, decompressedOffset, literalLength); + compressedData.Slice(literalOffset, literalLength).CopyTo(buffer.Slice(decompressedOffset)); - compressedOffset += literalLength + 1; - decompressedOffset += literalLength; - } - else if (headerByte == (byte)0x80) - { - compressedOffset += 1; - } - else - { - byte repeatData = compressedData[compressedOffset + 1]; - int repeatLength = 257 - headerByte; + compressedOffset += literalLength + 1; + decompressedOffset += literalLength; + } + else if (headerByte == (byte)0x80) + { + compressedOffset += 1; + } + else + { + byte repeatData = compressedData[compressedOffset + 1]; + int repeatLength = 257 - headerByte; - ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); + ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); - compressedOffset += 2; - decompressedOffset += repeatLength; - } + compressedOffset += 2; + decompressedOffset += repeatLength; } } - finally - { - ArrayPool.Shared.Return(compressedData); - } } - private static void ArrayCopyRepeat(byte value, byte[] destinationArray, int destinationIndex, int length) + private static void ArrayCopyRepeat(byte value, Span destinationArray, int destinationIndex, int length) { for (int i = 0; i < length; i++) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs new file mode 100644 index 0000000000..33330beba4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Base tiff decompressor class. + /// + internal abstract class TiffBaseCompression + { + private readonly MemoryAllocator allocator; + + public TiffBaseCompression(MemoryAllocator allocator) => this.allocator = allocator; + + protected MemoryAllocator Allocator => this.allocator; + + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + public abstract void Decompress(Stream stream, int byteCount, Span buffer); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs new file mode 100644 index 0000000000..7e077983de --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal static class TiffCompressionFactory + { + public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator) + { + switch (compressionType) + { + case TiffCompressionType.None: + return new NoneTiffCompression(allocator); + case TiffCompressionType.PackBits: + return new PackBitsTiffCompression(allocator); + case TiffCompressionType.Deflate: + return new DeflateTiffCompression(allocator); + case TiffCompressionType.Lzw: + return new LzwTiffCompression(allocator); + default: + throw new InvalidOperationException(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs new file mode 100644 index 0000000000..03965c06f0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// A mathematical operator that is applied to the image data before an encoding scheme is applied. + /// + public enum TiffPredictor : ushort + { + /// + /// No prediction scheme used before coding + /// + None = 1, + + /// + /// Horizontal differencing. + /// + Horizontal = 2, + + /// + /// Floating point horizontal differencing. + /// + FloatingPoint = 3 + } +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs new file mode 100644 index 0000000000..cdd618224c --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Specifies how to interpret each data sample in a pixel. + /// + public enum TiffSampleFormat : ushort + { + /// + /// Unsigned integer data. Default value. + /// + UnsignedInteger = 1, + + /// + /// Signed integer data. + /// + SignedInteger = 2, + + /// + /// IEEE floating point data. + /// + Float = 3, + + /// + /// Undefined data format. + /// + Undefined = 4, + + /// + /// The complex int. + /// + ComplexInt = 5, + + /// + /// The complex float. + /// + ComplexFloat = 6 + } +} diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index fe0ab4ccb7..b605a8737c 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -105,6 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case ExifTagValue.TileByteCounts: case ExifTagValue.ColorMap: case ExifTagValue.ExtraSamples: + case ExifTagValue.SampleFormat: return true; default: diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index a19cd6d44b..902882c566 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -11,28 +11,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). /// /// The pixel format. - internal class BlackIsZero1TiffColor : TiffColorDecoder + internal class BlackIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero1TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { @@ -45,6 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)255 : (byte)0; + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 059de1a3e7..46e0e82bcf 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal class BlackIsZero4TiffColor : TiffColorDecoder + internal class BlackIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero4TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; bool isOddWidth = (width & 1) == 1; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 5d50600d9b..013dae688a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -10,34 +10,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal class BlackIsZero8TiffColor : TiffColorDecoder + internal class BlackIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero8TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { byte intensity = data[offset++]; + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 7de3035367..91518c6622 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// - internal class BlackIsZeroTiffColor : TiffColorDecoder + internal class BlackIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; @@ -19,26 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float factor; public BlackIsZeroTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSample0 = bitsPerSample[0]; - this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; + this.factor = (float)(1 << this.bitsPerSample0) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { @@ -46,6 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = ((float)value) / this.factor; + color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 7b4f50e806..0af9d86989 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,33 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// - internal class PaletteTiffColor : TiffColorDecoder + internal class PaletteTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; private readonly TPixel[] palette; + /// The number of bits per sample for each pixel. + /// The RGB color lookup table to use for decoding the image. public PaletteTiffColor(ushort[] bitsPerSample, ushort[] colorMap) - : base(bitsPerSample, colorMap) { this.bitsPerSample0 = bitsPerSample[0]; - int colorCount = (int)Math.Pow(2, this.bitsPerSample0); + int colorCount = 1 << this.bitsPerSample0; this.palette = GeneratePalette(colorMap, colorCount); } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { @@ -52,7 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) { var palette = new TPixel[colorCount]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index 352c1e26c9..b19028a972 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -10,38 +10,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). /// - internal class Rgb888TiffColor : TiffColorDecoder + internal class Rgb888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public Rgb888TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); + for (int x = left; x < left + width; x++) { byte r = data[offset++]; byte g = data[offset++]; byte b = data[offset++]; + color.FromRgba32(new Rgba32(r, g, b, 255)); - pixels[x, y] = color; + pixelRow[x] = color; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 23f05c35fc..3bd263ef3c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -20,11 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float bFactor; - private readonly uint bitsPerSampleR; + private readonly ushort bitsPerSampleR; - private readonly uint bitsPerSampleG; + private readonly ushort bitsPerSampleG; - private readonly uint bitsPerSampleB; + private readonly ushort bitsPerSampleB; public RgbPlanarTiffColor(ushort[] bitsPerSample) /* : base(bitsPerSample, null) */ @@ -33,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.bitsPerSampleG = bitsPerSample[1]; this.bitsPerSampleB = bitsPerSample[2]; - this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; - this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; - this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + this.rFactor = (float)(1 << this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)(1 << this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)(1 << this.bitsPerSampleB) - 1.0f; } /// @@ -47,13 +46,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - public void Decode(byte[][] data, Buffer2D pixels, int left, int top, int width, int height) + public void Decode(IManagedByteBuffer[] data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - var rBitReader = new BitReader(data[0]); - var gBitReader = new BitReader(data[1]); - var bBitReader = new BitReader(data[2]); + var rBitReader = new BitReader(data[0].GetSpan()); + var gBitReader = new BitReader(data[1].GetSpan()); + var bBitReader = new BitReader(data[2].GetSpan()); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 35b51fd95d..bcc303f178 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (for all bit depths). /// - internal class RgbTiffColor : TiffColorDecoder + internal class RgbTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; @@ -20,38 +20,29 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float bFactor; - private readonly uint bitsPerSampleR; + private readonly ushort bitsPerSampleR; - private readonly uint bitsPerSampleG; + private readonly ushort bitsPerSampleG; - private readonly uint bitsPerSampleB; + private readonly ushort bitsPerSampleB; public RgbTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSampleR = bitsPerSample[0]; this.bitsPerSampleG = bitsPerSample[1]; this.bitsPerSampleB = bitsPerSample[2]; - this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; - this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; - this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + this.rFactor = (float)(1 << this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)(1 << this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)(1 << this.bitsPerSampleB) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs similarity index 61% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs index 8c4f7e9b58..ad67c463f1 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,22 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The base class for photometric interpretation decoders. /// /// The pixel format. - internal abstract class TiffColorDecoder + internal abstract class TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - private readonly ushort[] bitsPerSample; - - private readonly ushort[] colorMap; - - /// - /// Initializes a new instance of the class. - /// - /// The number of bits per sample for each pixel. - /// The RGB color lookup table to use for decoding the image. - protected TiffColorDecoder(ushort[] bitsPerSample, ushort[] colorMap) + protected TiffBaseColorDecoder() { - this.bitsPerSample = bitsPerSample; - this.colorMap = colorMap; } /* @@ -46,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// 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 abstract void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height); + /// The height of the image block. + public abstract void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height); } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index 37bc96ef49..a01a25e8ba 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel { - public static TiffColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + public static TiffBaseColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) { switch (colorType) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 666d03f9cc..95ff7c6a7d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). /// - internal class WhiteIsZero1TiffColor : TiffColorDecoder + internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero1TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { @@ -44,6 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)0 : (byte)255; + color.FromRgba32(new Rgba32(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 index 9e5ce1f597..2720a1aa5b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal class WhiteIsZero4TiffColor : TiffColorDecoder + internal class WhiteIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero4TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; bool isOddWidth = (width & 1) == 1; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 4e3a0e4436..30d8ea1db7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -10,34 +10,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal class WhiteIsZero8TiffColor : TiffColorDecoder + internal class WhiteIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero8TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { byte intensity = (byte)(255 - data[offset++]); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 329454e9d0..dda338d3b0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// - internal class WhiteIsZeroTiffColor : TiffColorDecoder + internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; @@ -19,26 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float factor; public WhiteIsZeroTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSample0 = bitsPerSample[0]; this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// 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. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs index 7c97153809..f65062d597 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -11,12 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffStreamFactory { - public static TiffStream CreateBySignature(Stream stream) - { - TiffByteOrder order = ReadByteOrder(stream); - return Create(order, stream); - } - /// /// Creates the specified byte order. /// @@ -40,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Reads the byte order of stream. /// /// The stream. - private static TiffByteOrder ReadByteOrder(Stream stream) + public static TiffByteOrder ReadByteOrder(Stream stream) { byte[] headerBytes = new byte[2]; stream.Read(headerBytes, 0, 2); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 74d11f1d4d..aed21af56c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -24,10 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, "stream"); - using (var decoder = new TiffDecoderCore(stream, configuration, this)) - { - return decoder.Decode(); - } + using var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.Decode(configuration, stream); } /// @@ -37,25 +36,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.DecodeAsync(configuration, stream, cancellationToken); } /// - public Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) - { - throw new System.NotImplementedException(); - } + public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) + => await this.DecodeAsync(configuration, stream, cancellationToken) + .ConfigureAwait(false); /// public IImageInfo Identify(Configuration configuration, Stream stream) { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.Identify(configuration, stream); } /// public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.IdentifyAsync(configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 0d089287fd..9471101379 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -25,6 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private readonly Configuration configuration; + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// @@ -41,6 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = configuration ?? Configuration.Default; this.ignoreMetadata = options.IgnoreMetadata; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// @@ -52,26 +58,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffDecoderCore(Stream stream, Configuration configuration, ITiffDecoderOptions options) : this(configuration, options) { - this.Stream = TiffStreamFactory.CreateBySignature(stream); + this.ByteOrder = TiffStreamFactory.ReadByteOrder(stream); } /// - /// Initializes a new instance of the class. + /// Gets the byte order. /// - /// The byte order. - /// The input stream. - /// The configuration. - /// The decoder options. - public TiffDecoderCore(TiffByteOrder byteOrder, Stream stream, Configuration configuration, ITiffDecoderOptions options) - : this(configuration, options) - { - this.Stream = TiffStreamFactory.Create(byteOrder, stream); - } + public TiffByteOrder ByteOrder { get; } /// /// Gets the input stream. /// - public TiffStream Stream { get; } + public TiffStream Stream { get; private set; } /// /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. @@ -109,32 +107,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public Size Dimensions { get; private set; } - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The decoded image. - public Image Decode() + /// + public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); var reader = new DirectoryReader(this.Stream); + IEnumerable directories = reader.Read(); var frames = new List>(); + var framesMetadata = new List(); foreach (IExifValue[] ifd in directories) { - ImageFrame frame = this.DecodeFrame(ifd); + ImageFrame frame = this.DecodeFrame(ifd, out TiffFrameMetadata frameMetadata); frames.Add(frame); + framesMetadata.Add(frameMetadata); } - ImageMetadata metadata = frames.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); // todo: tiff frames can have different sizes { - var root = frames.First(); + ImageFrame root = frames.First(); this.Dimensions = root.Size(); - foreach (var frame in frames) + foreach (ImageFrame frame in frames) { if (frame.Size() != root.Size()) { @@ -148,9 +145,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff return image; } - /// - /// Dispose - /// + /// + public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); + var reader = new DirectoryReader(this.Stream); + + IEnumerable directories = reader.Read(); + + var framesMetadata = new List(); + foreach (IExifValue[] ifd in directories) + { + framesMetadata.Add(new TiffFrameMetadata() { Tags = ifd }); + } + + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + + TiffFrameMetadata root = framesMetadata.First(); + int bitsPerPixel = 0; + foreach (var bits in root.BitsPerSample) + { + bitsPerPixel += bits; + } + + return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, metadata); + } + + /// public void Dispose() { // nothing @@ -161,11 +182,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The pixel format. /// The IFD tags. - private ImageFrame DecodeFrame(IExifValue[] tags) + /// The frame metadata. + /// + /// The tiff frame. + /// + private ImageFrame DecodeFrame(IExifValue[] tags, out TiffFrameMetadata metadata) where TPixel : unmanaged, IPixel { var coreMetadata = new ImageFrameMetadata(); - TiffFrameMetadata metadata = coreMetadata.GetTiffMetadata(); + metadata = coreMetadata.GetTiffMetadata(); metadata.Tags = tags; this.VerifyAndParseOptions(metadata); @@ -178,7 +203,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff uint[] stripOffsets = metadata.StripOffsets; uint[] stripByteCounts = metadata.StripByteCounts; - this.DecodeImageStrips(frame, rowsPerStrip, stripOffsets, stripByteCounts); + if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) + { + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts); + } + else + { + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts); + } return frame; } @@ -190,12 +222,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height for the desired pixel buffer. /// The index of the plane for planar image configuration (or zero for chunky). /// The size (in bytes) of the required pixel buffer. - private int CalculateImageBufferSize(int width, int height, int plane) + private int CalculateStripBufferSize(int width, int height, int plane = -1) { uint bitsPerPixel = 0; if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { + DebugGuard.IsTrue(plane == -1, "Excepted Chunky planar."); for (int i = 0; i < this.BitsPerSample.Length; i++) { bitsPerPixel += this.BitsPerSample[i]; @@ -207,7 +240,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8; - return bytesPerRow * height; + int stripBytes = bytesPerRow * height; + + return stripBytes; } /// @@ -218,35 +253,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The number of rows per strip of data. /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). - private void DecodeImageStrips(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) where TPixel : unmanaged, IPixel { - int stripsPerPixel = this.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? 1 : this.BitsPerSample.Length; + int stripsPerPixel = this.BitsPerSample.Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; Buffer2D pixels = frame.PixelBuffer; - byte[][] stripBytes = new byte[stripsPerPixel][]; - - for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) - { - int uncompressedStripSize = this.CalculateImageBufferSize(frame.Width, rowsPerStrip, stripIndex); - stripBytes[stripIndex] = ArrayPool.Shared.Rent(uncompressedStripSize); - } + var stripBuffers = new IManagedByteBuffer[stripsPerPixel]; try { - TiffColorDecoder chunkyDecoder = null; - RgbPlanarTiffColor planarDecoder = null; - if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - chunkyDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); - } - else + for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++) { - planarDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip, stripIndex); + stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + + RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + for (int i = 0; i < stripsPerPlane; i++) { int stripHeight = i < stripsPerPlane - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; @@ -254,37 +282,45 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { int stripIndex = (i * stripsPerPixel) + planeIndex; - CompressionFactory.DecompressImageBlock(this.Stream.InputStream, this.CompressionType, stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); - } - if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - chunkyDecoder.Decode(stripBytes[0], pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); - } - else - { - planarDecoder.Decode(stripBytes, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); + this.Stream.Seek(stripOffsets[stripIndex]); + decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffers[planeIndex].GetSpan()); } + + colorDecoder.Decode(stripBuffers, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); } } finally { - for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) + foreach (IManagedByteBuffer buf in stripBuffers) { - ArrayPool.Shared.Return(stripBytes[stripIndex]); + buf?.Dispose(); } } } - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + where TPixel : unmanaged, IPixel { - throw new NotImplementedException(); - } + int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); - public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) - { - throw new NotImplementedException(); + using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); + + Buffer2D pixels = frame.PixelBuffer; + + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + + TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); + + for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) + { + int stripHeight = stripIndex < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; + + this.Stream.Seek(stripOffsets[stripIndex]); + decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffer.GetSpan()); + + colorDecoder.Decode(stripBuffer.GetSpan(), pixels, 0, rowsPerStrip * stripIndex, frame.Width, stripHeight); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 4aa24f24dc..ebfdf5df0a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -2,11 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Net.Http.Headers; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -20,14 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderHelpers { - public static ImageMetadata CreateMetadata(this IList> frames, bool ignoreMetadata, TiffByteOrder byteOrder) - where TPixel : unmanaged, IPixel + public static ImageMetadata CreateMetadata(this IList frames, bool ignoreMetadata, TiffByteOrder byteOrder) { var coreMetadata = new ImageMetadata(); TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; - TiffFrameMetadata rootFrameMetadata = frames.First().Metadata.GetTiffMetadata(); + TiffFrameMetadata rootFrameMetadata = frames.First(); switch (rootFrameMetadata.ResolutionUnit) { case TiffResolutionUnit.None: @@ -53,13 +49,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (!ignoreMetadata) { - foreach (ImageFrame frame in frames) + foreach (TiffFrameMetadata frame in frames) { - TiffFrameMetadata frameMetadata = frame.Metadata.GetTiffMetadata(); - if (tiffMetadata.XmpProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.XMP, true); + byte[] buf = frame.GetArray(ExifTag.XMP, true); if (buf != null) { tiffMetadata.XmpProfile = buf; @@ -68,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (coreMetadata.IptcProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.IPTC, true); + byte[] buf = frame.GetArray(ExifTag.IPTC, true); if (buf != null) { coreMetadata.IptcProfile = new IptcProfile(buf); @@ -77,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (coreMetadata.IccProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.IccProfile, true); + byte[] buf = frame.GetArray(ExifTag.IccProfile, true); if (buf != null) { coreMetadata.IccProfile = new IccProfile(buf); @@ -106,11 +100,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff throw new NotSupportedException("The lower-order bits of the byte FillOrder is not supported."); } - if (entries.GetArrayValue(ExifTag.TileOffsets, true) != null) + if (entries.GetArray(ExifTag.TileOffsets, true) != null) { throw new NotSupportedException("The Tile images is not supported."); } + if (entries.Predictor != TiffPredictor.None) + { + throw new NotSupportedException("At the moment support only None Predictor."); + } + + if (entries.SampleFormat != null) + { + foreach (TiffSampleFormat format in entries.SampleFormat) + { + if (format != TiffSampleFormat.UnsignedInteger) + { + throw new NotSupportedException("At the moment support only UnsignedInteger SampleFormat."); + } + } + } + ParseCompression(options, entries.Compression); options.PlanarConfiguration = entries.PlanarConfiguration; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index bffbd1818e..cfec448a44 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,6 +17,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; + private const TiffPredictor DefaultPredictor = TiffPredictor.None; + /// /// Initializes a new instance of the class. /// @@ -39,17 +42,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets the number of columns in the image, i.e., the number of pixels per row. /// - public uint Width => this.GetSingleUInt(ExifTag.ImageWidth); + public uint Width => this.GetSingle(ExifTag.ImageWidth); /// /// Gets the number of rows of pixels in the image. /// - public uint Height => this.GetSingleUInt(ExifTag.ImageLength); + public uint Height => this.GetSingle(ExifTag.ImageLength); /// /// Gets the number of bits per component. /// - public ushort[] BitsPerSample => this.GetArrayValue(ExifTag.BitsPerSample, true); + public ushort[] BitsPerSample => this.GetArray(ExifTag.BitsPerSample, true); /// Gets the compression scheme used on the image data. /// The compression scheme used on the image data. @@ -81,17 +84,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff public string Model => this.GetString(ExifTag.Model); /// Gets for each strip, the byte offset of that strip.. - public uint[] StripOffsets => this.GetArrayValue(ExifTag.StripOffsets); + public uint[] StripOffsets => this.GetArray(ExifTag.StripOffsets); + + /// + /// Gets the number of components per pixel. + /// + public ushort SamplesPerPixel => this.GetSingle(ExifTag.SamplesPerPixel); /// /// Gets the number of rows per strip. /// - public uint RowsPerStrip => this.GetSingleUInt(ExifTag.RowsPerStrip); + public uint RowsPerStrip => this.GetSingle(ExifTag.RowsPerStrip); /// /// Gets for each strip, the number of bytes in the strip after compression. /// - public uint[] StripByteCounts => this.GetArrayValue(ExifTag.StripByteCounts); + public uint[] StripByteCounts => this.GetArray(ExifTag.StripByteCounts); /// Gets the resolution of the image in x- direction. /// The density of the image in x- direction. @@ -168,22 +176,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets a color map for palette color images. /// - public ushort[] ColorMap => this.GetArrayValue(ExifTag.ColorMap, true); + public ushort[] ColorMap => this.GetArray(ExifTag.ColorMap, true); /// /// Gets the description of extra components. /// - public ushort[] ExtraSamples => this.GetArrayValue(ExifTag.ExtraSamples, true); + public ushort[] ExtraSamples => this.GetArray(ExifTag.ExtraSamples, true); /// /// Gets the copyright notice. /// public string Copyright => this.GetString(ExifTag.Copyright); - internal T[] GetArrayValue(ExifTag tag, bool optional = false) + /// + /// Gets a mathematical operator that is applied to the image data before an encoding scheme is applied. + /// + public TiffPredictor Predictor => this.GetSingleEnum(ExifTag.Predictor, DefaultPredictor); + + /// + /// Gets the specifies how to interpret each data sample in a pixel. + /// + /// + public TiffSampleFormat[] SampleFormat => this.GetEnumArray(ExifTag.SampleFormat, true); + + internal T[] GetArray(ExifTag tag, bool optional = false) where T : struct { - if (this.TryGetArrayValue(tag, out T[] result)) + if (this.TryGetArray(tag, out T[] result)) { return result; } @@ -196,7 +215,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return null; } - private bool TryGetArrayValue(ExifTag tag, out T[] result) + private bool TryGetArray(ExifTag tag, out T[] result) where T : struct { foreach (IExifValue entry in this.Tags) @@ -214,6 +233,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff return false; } + private TEnum[] GetEnumArray(ExifTag tag, bool optional = false) + where TEnum : struct + where TTagValue : struct + { + if (this.TryGetArray(tag, out TTagValue[] result)) + { + // todo: improve + return result.Select(a => (TEnum)(object)a).ToArray(); + } + + if (!optional) + { + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + return null; + } + private string GetString(ExifTag tag) { foreach (IExifValue entry in this.Tags) @@ -246,42 +283,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) where TEnum : struct where TTagValue : struct - { - if (!this.TryGetSingle(tag, out TTagValue value)) - { - if (defaultValue != null) - { - return defaultValue.Value; - } - - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); - } - - return (TEnum)(object)value; - } + => this.GetSingleEnumNullable(tag) ?? (defaultValue != null ? defaultValue.Value : throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag))); - /* - private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) - where TEnum : struct - where TEnumParent : struct - where TTagValue : struct - { - if (!this.TryGetSingle(tag, out TTagValue value)) - { - if (defaultValue != null) - { - return defaultValue.Value; - } - - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); - } - - return (TEnum)(object)(TEnumParent)Convert.ChangeType(value, typeof(TEnumParent)); - } */ - - private uint GetSingleUInt(ExifTag tag) + private T GetSingle(ExifTag tag) + where T : struct { - if (this.TryGetSingle(tag, out uint result)) + if (this.TryGetSingle(tag, out T result)) { return result; } @@ -290,7 +297,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } private bool TryGetSingle(ExifTag tag, out T result) - where T : struct + where T : struct { foreach (IExifValue entry in this.Tags) { diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index 2c1b25000f..0093f342a6 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -1,24 +1,28 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Utility class to read a sequence of bits from an array /// - internal class BitReader + internal ref struct BitReader { - private readonly byte[] array; + private readonly ReadOnlySpan array; private int offset; private int bitOffset; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The array to read data from. - public BitReader(byte[] array) + public BitReader(ReadOnlySpan array) { this.array = array; + this.offset = 0; + this.bitOffset = 0; } /// @@ -59,4 +63,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index 7e33f186bd..a490e7c0d9 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The length of the compressed data. /// Size of the data. /// The pixel array to decode to. - public void DecodePixels(int length, int dataSize, byte[] pixels) + public void DecodePixels(int length, int dataSize, Span pixels) { Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); @@ -268,4 +268,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.isDisposed = true; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 8237244091..5c68ca14d6 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The stream to read from. /// A buffer to store the retrieved data. /// The number of bytes to read. - public static void ReadFull(this Stream stream, byte[] buffer, int count) + public static void ReadFull(this Stream stream, Span buffer, int count) { int offset = 0; @@ -39,9 +40,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The stream to read from. /// A buffer to store the retrieved data. - public static void ReadFull(this Stream stream, byte[] buffer) + public static void ReadFull(this Stream stream, Span buffer) { ReadFull(stream, buffer, buffer.Length); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index 5d0a106ab6..e08a0b1e72 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -59,7 +59,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Gets the StripByteCounts exif tag. /// - /// public static ExifTag StripByteCounts { get; } = new ExifTag(ExifTagValue.StripByteCounts); /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index 7e42f1beea..09c0daa487 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Tiff.RgbLzw)] + [Params(TestImages.Tiff.RgbPackbits)] public string TestImage { get; set; } [GlobalSetup] diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index cf5f87e667..cead349d19 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests TestImages.Png.Splash, TestImages.Gif.Trans, TestImages.Tga.Bit24PalRleTopRight, - TestImages.Tiff.RgbLzw, + TestImages.Tiff.RgbPackbits, }; /// @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests // TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only TestFile.Create(TestImages.Tga.Bit24PalRleTopRight), - TestFile.Create(TestImages.Tiff.RgbLzw), + TestFile.Create(TestImages.Tiff.RgbPackbits), }; #pragma warning restore SA1515 // Single-line comment should be preceded by blank line } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 66868d3dc8..10f5819ac3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -4,6 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { byte[] buffer = new byte[data.Length]; - DeflateTiffCompression.Decompress(stream, (int)stream.Length, buffer); + new DeflateTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index de4d5f46d0..df9208434f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { byte[] buffer = new byte[data.Length]; - LzwTiffCompression.Decompress(stream, (int)stream.Length, buffer); + new LzwTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 8e0d81fa43..28fbce69fd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Stream stream = new MemoryStream(inputData); byte[] buffer = new byte[expectedResult.Length]; - NoneTiffCompression.Decompress(stream, byteCount, buffer); + new NoneTiffCompression(null).Decompress(stream, byteCount, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 3888f6bf97..b08648465b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff Stream stream = new MemoryStream(inputData); byte[] buffer = new byte[expectedResult.Length]; - PackBitsTiffCompression.Decompress(stream, inputData.Length, buffer); + new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()).Decompress(stream, inputData.Length, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index c0e328c62c..1982a8ebeb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -190,7 +192,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - new RgbPlanarTiffColor(bitsPerSample).Decode(inputData, pixels, left, top, width, height); + var buffers = new IManagedByteBuffer[inputData.Length]; + for (int i = 0; i < buffers.Length; i++) + { + buffers[i] = Configuration.Default.MemoryAllocator.AllocateManagedByteBuffer(inputData[i].Length); + ((Span)inputData[i]).CopyTo(buffers[i].GetSpan()); + } + + new RgbPlanarTiffColor(bitsPerSample).Decode(buffers, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 58b917937c..d9abc163a6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,7 +4,9 @@ // ReSharper disable InconsistentNaming using System; +using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -30,6 +32,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Throws(() => provider.GetImage(new TiffDecoder())); } + [Theory] + [InlineData(TestImages.Tiff.RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] + [InlineData(TestImages.Tiff.SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(TestImages.Tiff.Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)] + public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); + Assert.Equal(expectedWidth, info.Width); + Assert.Equal(expectedHeight, info.Height); + Assert.NotNull(info.Metadata); + Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution); + Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution); + Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits); + } + } + [Theory] [WithFileCollection(nameof(SingleTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 6c210eb1e6..0090a82229 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -8,7 +8,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff.BlackBox")] [Trait("Category", "Tiff")] public class TiffMetadataTests { @@ -31,6 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff else { Assert.NotNull(meta.XmpProfile); + Assert.Equal(2599, meta.XmpProfile.Length); } } } @@ -53,25 +53,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffFrameMetadata frame = image.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(32u, frame.Width); Assert.Equal(32u, frame.Height); - Assert.Equal(new ushort[] { 8, 8, 8 }, frame.BitsPerSample); + Assert.Equal(new ushort[] { 4 }, frame.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frame.Compression); - Assert.Equal(TiffPhotometricInterpretation.Rgb, frame.PhotometricInterpretation); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); Assert.Equal("This is Название", frame.ImageDescription); Assert.Equal("This is Изготовитель камеры", frame.Make); Assert.Equal("This is Модель камеры", frame.Model); Assert.Equal(new uint[] { 8 }, frame.StripOffsets); + Assert.Equal(1, frame.SamplesPerPixel); Assert.Equal(32u, frame.RowsPerStrip); - Assert.Equal(new uint[] { 750 }, frame.StripByteCounts); + Assert.Equal(new uint[] { 297 }, frame.StripByteCounts); Assert.Equal(10, frame.HorizontalResolution); Assert.Equal(10, frame.VerticalResolution); Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); Assert.Equal(TiffResolutionUnit.Inch, frame.ResolutionUnit); Assert.Equal("IrfanView", frame.Software); Assert.Equal(null, frame.DateTime); - Assert.Equal("This is;Автор", frame.Artist); + Assert.Equal("This is author1;Author2", frame.Artist); Assert.Equal(null, frame.HostComputer); - Assert.Equal(null, frame.ColorMap); + Assert.Equal(48, frame.ColorMap.Length); + Assert.Equal(10537, frame.ColorMap[0]); + Assert.Equal(14392, frame.ColorMap[1]); + Assert.Equal(58596, frame.ColorMap[46]); + Assert.Equal(3855, frame.ColorMap[47]); + Assert.Equal(null, frame.ExtraSamples); + Assert.Equal(TiffPredictor.None, frame.Predictor); + Assert.Equal(null, frame.SampleFormat); Assert.Equal("This is Авторские права", frame.Copyright); } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 74967f3ec7..f90324d64f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -502,9 +502,9 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; - public const string Calliphora_RgbDeflate = "Tiff/Calliphora_rgb_deflate.tiff"; + public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzw = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbLzwe_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; @@ -512,32 +512,35 @@ namespace SixLabors.ImageSharp.Tests public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; - public const string RgbDeflate = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflate_Predictor = "Tiff/rgb_deflate.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; - public const string RgbLzw = "Tiff/rgb_lzw.tiff"; - public const string RgbLzwMultistrip = "Tiff/rgb_lzw_multistrip.tiff"; + public const string RgbLzw_Predictor = "Tiff/rgb_lzw.tiff"; + public const string RgbLzwMultistrip_Predictor = "Tiff/rgb_lzw_multistrip.tiff"; public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; + public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; + public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; + public const string RgbUncompressedTiled = "Tiff/rgb_uncompressed_tiled.tiff"; public const string MultiframeDifferentSizeTiled = "Tiff/multipage_ withPreview_differentSize_tiled.tiff"; - public const string MultiframeLzw = "Tiff/multipage_lzw.tiff"; + public const string MultiframeLzw_Predictor = "Tiff/multipage_lzw.tiff"; public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff"; public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, Calliphora_RgbDeflate, Calliphora_RgbLzw, Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflate, RgbDeflateMultistrip, /*RgbJpeg,*/ RgbLzw, RgbLzwMultistrip, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, MultiframeLzw, /*MultiFrameDifferentVariants,*/ SampleMetadata, }; + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw }; - public static readonly string[] Multiframes = { MultiframeLzw, MultiframeDeflateWithPreview /*MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; + public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index e49bf1073d..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:087d7479ebe3bdd95281584cf4c9582603d90e157d136cf4233dcdefd909ba73 -size 1696927 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index 891a2ace6c..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14f7b8e8275b4488418e4403c31e1a5c7565bf062fbd962f09f7a665468e2481 -size 7358 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 9eb1808f21..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91c87bc3d75b1386b30990513fab2da26bad065b977108904e866c850d66a7e5 -size 197 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index a6642e7168..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:727356bf611957750a0427bda4582f9ecc0f8935884f2158e8a2d5e65c3469b4 -size 18278 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 9e95173bce..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b077fb63012967c39c5a0b1b515ada33ad5470160ad0fa1aa89581aa77238c82 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 9dcd861ecd..0000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 -size 91898 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png new file mode 100644 index 0000000000..a05e8248e3 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed +size 208 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index 415f73d87a..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c -size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index 36dcfd9e87..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c74fae7a00dbf00bc259851b1e9c774a10fac2bd8581397d8680ebc47a7d0340 -size 31982 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 96cbbca223..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5727c4007787bf0d6af78763c94125029255e41ebe570a6b8f3cbdb65e2a4d5f -size 1488 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index 575e6cd56a..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f -size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 82d582e772..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 9dcd861ecd..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 -size 91898 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png new file mode 100644 index 0000000000..6020d448ab --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7a31971806dd550f722bb519f90e194b1fbb7e1b42d54f78b5ad8ce9da094c4 +size 2660 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index 415f73d87a..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c -size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index a79ae60962..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4acb6da968aa5bfc7af57b41fe9aefe13e7b2d3ee4379867b83548209fbc94eb -size 8108 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 249f688319..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:977c552ee08788244fa4579c23ca59dfcb6e91df706774dc8167286a4ce5a536 -size 821 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index 575e6cd56a..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f -size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 82d582e772..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png deleted file mode 100644 index c0da2eb59a..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:24fe217e11cbc2944e7a8aba0411e37314822024e90fdd873cd9e3feb0e55898 -size 77527 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 61d88337b0..0000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82f30f2936880eacc3248fbc759f84adac7d625b974259d61aeed16bc00f01b9 -size 64056 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png new file mode 100644 index 0000000000..a05e8248e3 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed +size 208 diff --git a/tests/Images/Input/Tiff/metadata_sample.tiff b/tests/Images/Input/Tiff/metadata_sample.tiff index aac5fe2c47..d767352688 100644 --- a/tests/Images/Input/Tiff/metadata_sample.tiff +++ b/tests/Images/Input/Tiff/metadata_sample.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea7bc7404614a90da555637f6fed88defe5c6be8b5d1da2ff5980c39d249a01b -size 8833 +oid sha256:72a1c8022d699e0e7248940f0734d01d6ab9bf4a71022e8b5626b64d66a5f39d +size 8107 diff --git a/tests/Images/Input/Tiff/rgb_small_deflate.tiff b/tests/Images/Input/Tiff/rgb_small_deflate.tiff new file mode 100644 index 0000000000..cd78dfc883 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_small_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09c964c2f11806c2ff1e9fc7b06ddbecbc9e94cb7f4bd2b9841f0a3939d98eef +size 2575 diff --git a/tests/Images/Input/Tiff/rgb_small_lzw.tiff b/tests/Images/Input/Tiff/rgb_small_lzw.tiff new file mode 100644 index 0000000000..deaeda645d --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_small_lzw.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2630a452dcc86f557594fe29ae4244fbb29a276cdee53835157af17f966e1405 +size 3221