diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
index 84b90e69fe..858a1ca4bc 100644
--- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
@@ -51,5 +51,15 @@ namespace ImageSharp.Formats
/// Size (in bytes) of the Rational and SRational data types
///
public const int SizeOfRational = 8;
+
+ ///
+ /// Size (in bytes) of the Float data type
+ ///
+ public const int SizeOfFloat = 4;
+
+ ///
+ /// Size (in bytes) of the Double data type
+ ///
+ public const int SizeOfDouble = 8;
}
}
diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
index 7d5bcf5198..1c2703ed57 100644
--- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
@@ -316,6 +316,53 @@ namespace ImageSharp.Formats
return result;
}
+ public float ReadFloat(ref TiffIfdEntry entry)
+ {
+ if (entry.Count != 1)
+ throw new ImageFormatException($"Cannot read a single value from an array of multiple items.");
+
+ if (entry.Type != TiffType.Float)
+ throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float.");
+
+ return ToSingle(entry.Value, 0);
+ }
+
+ public double ReadDouble(ref TiffIfdEntry entry)
+ {
+ if (entry.Count != 1)
+ throw new ImageFormatException($"Cannot read a single value from an array of multiple items.");
+
+ return ReadDoubleArray(ref entry)[0];
+ }
+
+ public float[] ReadFloatArray(ref TiffIfdEntry entry)
+ {
+ if (entry.Type != TiffType.Float)
+ throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float.");
+
+ byte[] bytes = ReadBytes(ref entry);
+ float[] result = new float[entry.Count];
+
+ for (int i = 0 ; i < result.Length ; i++)
+ result[i] = ToSingle(bytes, i * TiffConstants.SizeOfFloat);
+
+ return result;
+ }
+
+ public double[] ReadDoubleArray(ref TiffIfdEntry entry)
+ {
+ if (entry.Type != TiffType.Double)
+ throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a double.");
+
+ byte[] bytes = ReadBytes(ref entry);
+ double[] result = new double[entry.Count];
+
+ for (int i = 0 ; i < result.Length ; i++)
+ result[i] = ToDouble(bytes, i * TiffConstants.SizeOfDouble);
+
+ return result;
+ }
+
private SByte ToSByte(byte[] bytes, int offset)
{
return (sbyte)bytes[offset];
@@ -352,6 +399,28 @@ namespace ImageSharp.Formats
return (ushort)ToInt16(bytes, offset);
}
+ private Single ToSingle(byte[] bytes, int offset)
+ {
+ byte[] buffer = new byte[4];
+ Array.Copy(bytes, offset, buffer, 0, 4);
+
+ if (BitConverter.IsLittleEndian != IsLittleEndian)
+ Array.Reverse(buffer);
+
+ return BitConverter.ToSingle(buffer, 0);
+ }
+
+ private Double ToDouble(byte[] bytes, int offset)
+ {
+ byte[] buffer = new byte[8];
+ Array.Copy(bytes, offset, buffer, 0, 8);
+
+ if (BitConverter.IsLittleEndian != IsLittleEndian)
+ Array.Reverse(buffer);
+
+ return BitConverter.ToDouble(buffer, 0);
+ }
+
public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count;
private static uint SizeOfDataType(TiffType type)
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
index b810182c98..369fc61de1 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
@@ -607,6 +607,223 @@ namespace ImageSharp.Tests
Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message);
}
+ [Theory]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)]
+ [InlineDataAttribute(false, new byte[] { 0x3F, 0x80, 0x00, 0x00 }, 1.0F)]
+ [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00 }, -2.0F)]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0x7F, 0xFF, 0xFF }, float.MaxValue)]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0x80, 0x00, 0x00 }, float.PositiveInfinity)]
+ [InlineDataAttribute(false, new byte[] { 0xFF, 0x80, 0x00, 0x00 }, float.NegativeInfinity)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x3F }, 1.0F)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0xC0 }, -2.0F)]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, float.MaxValue)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x7F }, float.PositiveInfinity)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0xFF }, float.NegativeInfinity)]
+ public void ReadFloat_ReturnsValue(bool isLittleEndian, byte[] bytes, float expectedValue)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, 1, bytes), isLittleEndian);
+
+ float result = decoder.ReadFloat(ref entry);
+
+ Assert.Equal(expectedValue, result);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte)]
+ [InlineDataAttribute(TiffType.Ascii)]
+ [InlineDataAttribute(TiffType.Short)]
+ [InlineDataAttribute(TiffType.Long)]
+ [InlineDataAttribute(TiffType.Rational)]
+ [InlineDataAttribute(TiffType.SByte)]
+ [InlineDataAttribute(TiffType.Undefined)]
+ [InlineDataAttribute(TiffType.SShort)]
+ [InlineDataAttribute(TiffType.SLong)]
+ [InlineDataAttribute(TiffType.SRational)]
+ [InlineDataAttribute(TiffType.Double)]
+ [InlineDataAttribute(TiffType.Ifd)]
+ [InlineDataAttribute((TiffType)99)]
+ public void ReadFloat_ThrowsExceptionIfInvalidType(ushort type)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true);
+
+ var e = Assert.Throws(() => decoder.ReadFloat(ref entry));
+
+ Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a float.", e.Message);
+ }
+
+ [Theory]
+ [InlineDataAttribute(false)]
+ [InlineDataAttribute(true)]
+ public void ReadFloat_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, 2, new byte[4]), isLittleEndian);
+
+ var e = Assert.Throws(() => decoder.ReadFloat(ref entry));
+
+ Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message);
+ }
+
+ [Theory]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })]
+ [InlineDataAttribute(false, new byte[] { 0x3F, 0x80, 0x00, 0x00 }, new float[] { 1.0F })]
+ [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00 }, new float[] { -2.0F })]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0x7F, 0xFF, 0xFF }, new float[] { float.MaxValue })]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0x80, 0x00, 0x00 }, new float[] { float.PositiveInfinity })]
+ [InlineDataAttribute(false, new byte[] { 0xFF, 0x80, 0x00, 0x00 }, new float[] { float.NegativeInfinity })]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00 }, new float[] { 0.0F, 1.0F, -2.0F })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x3F }, new float[] { 1.0F })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0xC0 }, new float[] { -2.0F })]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, new float[] { float.MaxValue })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x7F }, new float[] { float.PositiveInfinity })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0xFF }, new float[] { float.NegativeInfinity })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xC0 }, new float[] { 0.0F, 1.0F, -2.0F })]
+
+ public void ReadFloatArray_ReturnsValue(bool isLittleEndian, byte[] bytes, float[] expectedValue)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, (uint)expectedValue.Length, bytes), isLittleEndian);
+
+ float[] result = decoder.ReadFloatArray(ref entry);
+
+ Assert.Equal(expectedValue, result);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte)]
+ [InlineDataAttribute(TiffType.Ascii)]
+ [InlineDataAttribute(TiffType.Short)]
+ [InlineDataAttribute(TiffType.Long)]
+ [InlineDataAttribute(TiffType.Rational)]
+ [InlineDataAttribute(TiffType.SByte)]
+ [InlineDataAttribute(TiffType.Undefined)]
+ [InlineDataAttribute(TiffType.SShort)]
+ [InlineDataAttribute(TiffType.SLong)]
+ [InlineDataAttribute(TiffType.SRational)]
+ [InlineDataAttribute(TiffType.Double)]
+ [InlineDataAttribute(TiffType.Ifd)]
+ [InlineDataAttribute((TiffType)99)]
+ public void ReadFloatArray_ThrowsExceptionIfInvalidType(ushort type)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true);
+
+ var e = Assert.Throws(() => decoder.ReadFloatArray(ref entry));
+
+ Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a float.", e.Message);
+ }
+
+ [Theory]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)]
+ [InlineDataAttribute(false, new byte[] { 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 1.0)]
+ [InlineDataAttribute(false, new byte[] { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 2.0)]
+ [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -2.0)]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, double.MaxValue)]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, double.PositiveInfinity)]
+ [InlineDataAttribute(false, new byte[] { 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, double.NegativeInfinity)]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, double.NaN)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, 1.0)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, 2.0)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, -2.0)]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, double.MaxValue)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, double.PositiveInfinity)]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, double.NegativeInfinity)]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, double.NaN)]
+ public void ReadDouble_ReturnsValue(bool isLittleEndian, byte[] bytes, double expectedValue)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, 1, bytes), isLittleEndian);
+
+ double result = decoder.ReadDouble(ref entry);
+
+ Assert.Equal(expectedValue, result);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte)]
+ [InlineDataAttribute(TiffType.Ascii)]
+ [InlineDataAttribute(TiffType.Short)]
+ [InlineDataAttribute(TiffType.Long)]
+ [InlineDataAttribute(TiffType.Rational)]
+ [InlineDataAttribute(TiffType.SByte)]
+ [InlineDataAttribute(TiffType.Undefined)]
+ [InlineDataAttribute(TiffType.SShort)]
+ [InlineDataAttribute(TiffType.SLong)]
+ [InlineDataAttribute(TiffType.SRational)]
+ [InlineDataAttribute(TiffType.Float)]
+ [InlineDataAttribute(TiffType.Ifd)]
+ [InlineDataAttribute((TiffType)99)]
+ public void ReadDouble_ThrowsExceptionIfInvalidType(ushort type)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true);
+
+ var e = Assert.Throws(() => decoder.ReadDouble(ref entry));
+
+ Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a double.", e.Message);
+ }
+
+ [Theory]
+ [InlineDataAttribute(false)]
+ [InlineDataAttribute(true)]
+ public void ReadDouble_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, 2, new byte[4]), isLittleEndian);
+
+ var e = Assert.Throws(() => decoder.ReadDouble(ref entry));
+
+ Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message);
+ }
+
+ [Theory]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })]
+ [InlineDataAttribute(false, new byte[] { 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 1.0 })]
+ [InlineDataAttribute(false, new byte[] { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 2.0 })]
+ [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { -2.0 })]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, new double[] { double.MaxValue })]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { double.PositiveInfinity })]
+ [InlineDataAttribute(false, new byte[] { 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { double.NegativeInfinity })]
+ [InlineDataAttribute(false, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, new double[] { double.NaN })]
+ [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0, 1.0, -2.0 })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, new double[] { 1.0 })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, new double[] { 2.0 })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { -2.0 })]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, new double[] { double.MaxValue })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, new double[] { double.PositiveInfinity })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, new double[] { double.NegativeInfinity })]
+ [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, new double[] { double.NaN })]
+ [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { 0.0, 1.0, -2.0 })]
+ public void ReadDoubleArray_ReturnsValue(bool isLittleEndian, byte[] bytes, double[] expectedValue)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, (uint)expectedValue.Length, bytes), isLittleEndian);
+
+ double[] result = decoder.ReadDoubleArray(ref entry);
+
+ Assert.Equal(expectedValue, result);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte)]
+ [InlineDataAttribute(TiffType.Ascii)]
+ [InlineDataAttribute(TiffType.Short)]
+ [InlineDataAttribute(TiffType.Long)]
+ [InlineDataAttribute(TiffType.Rational)]
+ [InlineDataAttribute(TiffType.SByte)]
+ [InlineDataAttribute(TiffType.Undefined)]
+ [InlineDataAttribute(TiffType.SShort)]
+ [InlineDataAttribute(TiffType.SLong)]
+ [InlineDataAttribute(TiffType.SRational)]
+ [InlineDataAttribute(TiffType.Float)]
+ [InlineDataAttribute(TiffType.Ifd)]
+ [InlineDataAttribute((TiffType)99)]
+ public void ReadDoubleArray_ThrowsExceptionIfInvalidType(ushort type)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true);
+
+ var e = Assert.Throws(() => decoder.ReadDoubleArray(ref entry));
+
+ Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a double.", e.Message);
+ }
+
private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian)
{
Stream stream = new TiffGenIfd()