|
|
|
@ -5,6 +5,7 @@ |
|
|
|
using System.Buffers; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using SixLabors.ImageSharp.Formats.Tiff.Compression; |
|
|
|
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; |
|
|
|
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|
|
|
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; |
|
|
|
using SixLabors.ImageSharp.IO; |
|
|
|
@ -441,8 +442,14 @@ internal class TiffDecoderCore : ImageDecoderCore |
|
|
|
{ |
|
|
|
for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++) |
|
|
|
{ |
|
|
|
int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip, stripIndex); |
|
|
|
stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>(uncompressedStripSize); |
|
|
|
ulong uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip, stripIndex); |
|
|
|
|
|
|
|
if (uncompressedStripSize > int.MaxValue) |
|
|
|
{ |
|
|
|
TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
|
|
|
} |
|
|
|
|
|
|
|
stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>((int)uncompressedStripSize); |
|
|
|
} |
|
|
|
|
|
|
|
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel, frame.Metadata); |
|
|
|
@ -507,15 +514,83 @@ internal class TiffDecoderCore : ImageDecoderCore |
|
|
|
rowsPerStrip = height; |
|
|
|
} |
|
|
|
|
|
|
|
int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip); |
|
|
|
ulong uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip); |
|
|
|
int bitsPerPixel = this.BitsPerPixel; |
|
|
|
|
|
|
|
using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>(uncompressedStripSize, AllocationOptions.Clean); |
|
|
|
Span<byte> stripBufferSpan = stripBuffer.GetSpan(); |
|
|
|
Buffer2D<TPixel> pixels = frame.PixelBuffer; |
|
|
|
|
|
|
|
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel, frame.Metadata); |
|
|
|
TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>(); |
|
|
|
Buffer2D<TPixel> pixels = frame.PixelBuffer; |
|
|
|
|
|
|
|
// There exists in this world TIFF files with uncompressed strips larger than Int32.MaxValue.
|
|
|
|
// We can read them, but we cannot allocate a buffer that large to hold the uncompressed data.
|
|
|
|
// In this scenario we fall back to reading and decoding one row at a time.
|
|
|
|
//
|
|
|
|
// The NoneTiffCompression decompressor can be used to read individual rows since we have
|
|
|
|
// a guarantee that each row required the same number of bytes.
|
|
|
|
if (decompressor is NoneTiffCompression none && uncompressedStripSize > int.MaxValue) |
|
|
|
{ |
|
|
|
ulong bytesPerRowU = this.CalculateStripBufferSize(width, 1); |
|
|
|
|
|
|
|
// This should never happen, but we check just to be sure.
|
|
|
|
if (bytesPerRowU > int.MaxValue) |
|
|
|
{ |
|
|
|
TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
|
|
|
} |
|
|
|
|
|
|
|
int bytesPerRow = (int)bytesPerRowU; |
|
|
|
using IMemoryOwner<byte> rowBufferOwner = this.memoryAllocator.Allocate<byte>(bytesPerRow, AllocationOptions.Clean); |
|
|
|
Span<byte> rowBuffer = rowBufferOwner.GetSpan(); |
|
|
|
for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) |
|
|
|
{ |
|
|
|
cancellationToken.ThrowIfCancellationRequested(); |
|
|
|
|
|
|
|
int stripHeight = stripIndex < stripOffsets.Length - 1 || height % rowsPerStrip == 0 |
|
|
|
? rowsPerStrip |
|
|
|
: height % rowsPerStrip; |
|
|
|
|
|
|
|
int top = rowsPerStrip * stripIndex; |
|
|
|
if (top + stripHeight > height) |
|
|
|
{ |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
ulong baseOffset = stripOffsets[stripIndex]; |
|
|
|
ulong available = stripByteCounts[stripIndex]; |
|
|
|
ulong required = (ulong)bytesPerRow * (ulong)stripHeight; |
|
|
|
if (available < required) |
|
|
|
{ |
|
|
|
TiffThrowHelper.ThrowImageFormatException("StripByteCounts is smaller than required for uncompressed data."); |
|
|
|
} |
|
|
|
|
|
|
|
for (int r = 0; r < stripHeight; r++) |
|
|
|
{ |
|
|
|
cancellationToken.ThrowIfCancellationRequested(); |
|
|
|
|
|
|
|
ulong rowOffset = baseOffset + ((ulong)r * (ulong)bytesPerRow); |
|
|
|
|
|
|
|
// Use the NoneTiffCompression decompressor to read exactly one row.
|
|
|
|
none.Decompress( |
|
|
|
this.inputStream, |
|
|
|
rowOffset, |
|
|
|
(ulong)bytesPerRow, |
|
|
|
1, |
|
|
|
rowBuffer, |
|
|
|
cancellationToken); |
|
|
|
|
|
|
|
colorDecoder.Decode(rowBuffer, pixels, 0, top + r, width, 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (uncompressedStripSize > int.MaxValue) |
|
|
|
{ |
|
|
|
TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
|
|
|
} |
|
|
|
|
|
|
|
using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>((int)uncompressedStripSize, AllocationOptions.Clean); |
|
|
|
Span<byte> stripBufferSpan = stripBuffer.GetSpan(); |
|
|
|
|
|
|
|
for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) |
|
|
|
{ |
|
|
|
@ -808,7 +883,7 @@ internal class TiffDecoderCore : ImageDecoderCore |
|
|
|
/// <param name="height">The height for the desired pixel buffer.</param>
|
|
|
|
/// <param name="plane">The index of the plane for planar image configuration (or zero for chunky).</param>
|
|
|
|
/// <returns>The size (in bytes) of the required pixel buffer.</returns>
|
|
|
|
private int CalculateStripBufferSize(int width, int height, int plane = -1) |
|
|
|
private ulong CalculateStripBufferSize(int width, int height, int plane = -1) |
|
|
|
{ |
|
|
|
DebugGuard.MustBeLessThanOrEqualTo(plane, 3, nameof(plane)); |
|
|
|
|
|
|
|
@ -841,8 +916,8 @@ internal class TiffDecoderCore : ImageDecoderCore |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int bytesPerRow = ((width * bitsPerPixel) + 7) / 8; |
|
|
|
return bytesPerRow * height; |
|
|
|
ulong bytesPerRow = (((ulong)width * (ulong)bitsPerPixel) + 7) / 8; |
|
|
|
return bytesPerRow * (ulong)height; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
|
|