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