diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs
index 28459d0c5..f1faa6c67 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs
@@ -35,17 +35,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
/// The number of bytes to read from the input stream.
/// The height of the strip.
/// The output buffer for uncompressed data.
- public void Decompress(BufferedReadStream stream, uint stripOffset, uint stripByteCount, int stripHeight, Span buffer)
+ public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer)
{
- if (stripByteCount > int.MaxValue)
+ if (stripOffset > long.MaxValue || stripByteCount > long.MaxValue)
{
- TiffThrowHelper.ThrowImageFormatException("The StripByteCount value is too big.");
+ TiffThrowHelper.ThrowImageFormatException("The StripOffset or StripByteCount value is too big.");
}
- stream.Seek(stripOffset, SeekOrigin.Begin);
+ stream.Seek((long)stripOffset, SeekOrigin.Begin);
this.Decompress(stream, (int)stripByteCount, stripHeight, buffer);
- if (stripOffset + stripByteCount < stream.Position)
+ if ((long)stripOffset + (long)stripByteCount < stream.Position)
{
TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip.");
}
diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
index b54545141..b5548c707 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
@@ -35,6 +35,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
///
public const ushort HeaderMagicNumber = 42;
+ ///
+ /// The big tiff header magic number
+ ///
+ public const ushort BigTiffHeaderMagicNumber = 43;
+
+ ///
+ /// The big tiff bytesize of offsets value.
+ ///
+ public const ushort BigTiffBytesize = 8;
+
///
/// RowsPerStrip default value, which is effectively infinity.
///
diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
index 8b2c6bd3a..bd673cf3a 100644
--- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs
@@ -16,11 +16,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
private readonly Stream stream;
- private uint nextIfdOffset;
+ private ulong nextIfdOffset;
// used for sequential read big values (actual for multiframe big files)
// todo: different tags can link to the same data (stream offset) - investigate
- private readonly SortedList lazyLoaders = new SortedList(new DuplicateKeyComparer());
+ private readonly SortedList lazyLoaders = new SortedList(new DuplicateKeyComparer());
public DirectoryReader(Stream stream) => this.stream = stream;
@@ -36,13 +36,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public IEnumerable Read()
{
this.ByteOrder = ReadByteOrder(this.stream);
- this.nextIfdOffset = new HeaderReader(this.stream, this.ByteOrder).ReadFileHeader();
- return this.ReadIfds();
+ var headerReader = new HeaderReader(this.stream, this.ByteOrder);
+ headerReader.ReadFileHeader();
+
+ this.nextIfdOffset = headerReader.FirstIfdOffset;
+
+ return this.ReadIfds(headerReader.IsBigTiff);
}
private static ByteOrder ReadByteOrder(Stream stream)
{
- var headerBytes = new byte[2];
+ byte[] headerBytes = new byte[2];
stream.Read(headerBytes, 0, 2);
if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian)
{
@@ -56,13 +60,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
throw TiffThrowHelper.ThrowInvalidHeader();
}
- private IEnumerable ReadIfds()
+ private IEnumerable ReadIfds(bool isBigTiff)
{
var readers = new List();
- while (this.nextIfdOffset != 0 && this.nextIfdOffset < this.stream.Length)
+ while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length)
{
- var reader = new EntryReader(this.stream, this.ByteOrder, this.nextIfdOffset, this.lazyLoaders);
- reader.ReadTags();
+ var reader = new EntryReader(this.stream, this.ByteOrder, this.lazyLoaders);
+ reader.ReadTags(isBigTiff, this.nextIfdOffset);
this.nextIfdOffset = reader.NextIfdOffset;
@@ -75,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
loader();
}
- var list = new List();
+ var list = new List(readers.Count);
foreach (EntryReader reader in readers)
{
var profile = new ExifProfile(reader.Values, reader.InvalidTags);
diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
index 123a64cc1..7884242ef 100644
--- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
@@ -12,31 +12,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
internal class EntryReader : BaseExifReader
{
- private readonly uint startOffset;
+ private readonly SortedList lazyLoaders;
- private readonly SortedList lazyLoaders;
-
- public EntryReader(Stream stream, ByteOrder byteOrder, uint ifdOffset, SortedList lazyLoaders)
+ public EntryReader(Stream stream, ByteOrder byteOrder, SortedList lazyLoaders)
: base(stream)
{
this.IsBigEndian = byteOrder == ByteOrder.BigEndian;
- this.startOffset = ifdOffset;
this.lazyLoaders = lazyLoaders;
}
public List Values { get; } = new List();
- public uint NextIfdOffset { get; private set; }
+ public ulong NextIfdOffset { get; private set; }
- public void ReadTags()
+ public void ReadTags(bool isBigTiff, ulong ifdOffset)
{
- this.ReadValues(this.Values, this.startOffset);
- this.NextIfdOffset = this.ReadUInt32();
+ if (!isBigTiff)
+ {
+ this.ReadValues(this.Values, (uint)ifdOffset);
+ this.NextIfdOffset = this.ReadUInt32();
+
+ this.ReadSubIfd(this.Values);
+ }
+ else
+ {
+ this.ReadValues64(this.Values, ifdOffset);
+ this.NextIfdOffset = this.ReadUInt64();
- this.ReadSubIfd(this.Values);
+ this.ReadSubIfd64(this.Values);
+ }
}
- protected override void RegisterExtLoader(uint offset, Action reader) =>
+ protected override void RegisterExtLoader(ulong offset, Action reader) =>
this.lazyLoaders.Add(offset, reader);
}
@@ -46,20 +53,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff
: base(stream) =>
this.IsBigEndian = byteOrder == ByteOrder.BigEndian;
- public uint FirstIfdOffset { get; private set; }
+ public bool IsBigTiff { get; private set; }
+
+ public ulong FirstIfdOffset { get; private set; }
- public uint ReadFileHeader()
+ public void ReadFileHeader()
{
ushort magic = this.ReadUInt16();
- if (magic != TiffConstants.HeaderMagicNumber)
+ if (magic == TiffConstants.HeaderMagicNumber)
{
- TiffThrowHelper.ThrowInvalidHeader();
+ this.IsBigTiff = false;
+ this.FirstIfdOffset = this.ReadUInt32();
+ return;
+ }
+ else if (magic == TiffConstants.BigTiffHeaderMagicNumber)
+ {
+ this.IsBigTiff = true;
+
+ ushort bytesize = this.ReadUInt16();
+ ushort reserve = this.ReadUInt16();
+ if (bytesize == TiffConstants.BigTiffBytesize && reserve == 0)
+ {
+ this.FirstIfdOffset = this.ReadUInt64();
+ return;
+ }
}
- this.FirstIfdOffset = this.ReadUInt32();
- return this.FirstIfdOffset;
+ TiffThrowHelper.ThrowInvalidHeader();
}
- protected override void RegisterExtLoader(uint offset, Action reader) => throw new NotSupportedException();
+ protected override void RegisterExtLoader(ulong offset, Action reader) => throw new NotSupportedException();
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 55af87005..922c1f174 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -432,6 +432,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth");
}
+ if (((ulong)width.Value) > int.MaxValue)
+ {
+ TiffThrowHelper.ThrowImageFormatException("Too big ImageWidth value");
+ }
+
return (int)width.Value;
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index a8420fabb..a187d444a 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -24,12 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The IFD entries container to read the image format information for current frame.
public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
{
- if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null)
+ if (exifProfile.GetValueInternal(ExifTag.TileOffsets) is not null || exifProfile.GetValueInternal(ExifTag.TileByteCounts) is not null)
{
TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
}
- if (exifProfile.GetValue(ExifTag.ExtraSamples)?.Value != null)
+ if (exifProfile.GetValueInternal(ExifTag.ExtraSamples) is not null)
{
TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported.");
}
@@ -95,12 +95,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
{
- if (exifProfile.GetValue(ExifTag.StripOffsets) == null)
+ if (exifProfile.GetValueInternal(ExifTag.StripOffsets) is null)
{
TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!");
}
- if (exifProfile.GetValue(ExifTag.StripByteCounts) == null)
+ if (exifProfile.GetValueInternal(ExifTag.StripByteCounts) is null)
{
TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!");
}
@@ -384,7 +384,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile)
{
- switch (compression)
+ // Default 1 (No compression) https://www.awaresystems.be/imaging/tiff/tifftags/compression.html
+ switch (compression ?? TiffCompression.None)
{
case TiffCompression.None:
{
@@ -441,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
default:
{
- TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format {compression} is not supported");
+ TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported");
break;
}
}