|
5 | 5 | using System.Buffers; |
6 | 6 | using System.Runtime.CompilerServices; |
7 | 7 | using SixLabors.ImageSharp.Formats.Tiff.Compression; |
| 8 | +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; |
8 | 9 | using SixLabors.ImageSharp.Formats.Tiff.Constants; |
9 | 10 | using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; |
10 | 11 | using SixLabors.ImageSharp.IO; |
@@ -441,8 +442,14 @@ private void DecodeStripsPlanar<TPixel>( |
441 | 442 | { |
442 | 443 | for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++) |
443 | 444 | { |
444 | | - int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip, stripIndex); |
445 | | - stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>(uncompressedStripSize); |
| 445 | + ulong uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip, stripIndex); |
| 446 | + |
| 447 | + if (uncompressedStripSize > int.MaxValue) |
| 448 | + { |
| 449 | + TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
| 450 | + } |
| 451 | + |
| 452 | + stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>((int)uncompressedStripSize); |
446 | 453 | } |
447 | 454 |
|
448 | 455 | using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel, frame.Metadata); |
@@ -507,15 +514,83 @@ private void DecodeStripsChunky<TPixel>( |
507 | 514 | rowsPerStrip = height; |
508 | 515 | } |
509 | 516 |
|
510 | | - int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip); |
| 517 | + ulong uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip); |
511 | 518 | int bitsPerPixel = this.BitsPerPixel; |
512 | 519 |
|
513 | | - using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>(uncompressedStripSize, AllocationOptions.Clean); |
514 | | - Span<byte> stripBufferSpan = stripBuffer.GetSpan(); |
515 | | - Buffer2D<TPixel> pixels = frame.PixelBuffer; |
516 | | - |
517 | 520 | using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel, frame.Metadata); |
518 | 521 | TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>(); |
| 522 | + Buffer2D<TPixel> pixels = frame.PixelBuffer; |
| 523 | + |
| 524 | + // There exists in this world TIFF files with uncompressed strips larger than Int32.MaxValue. |
| 525 | + // We can read them, but we cannot allocate a buffer that large to hold the uncompressed data. |
| 526 | + // In this scenario we fall back to reading and decoding one row at a time. |
| 527 | + // |
| 528 | + // The NoneTiffCompression decompressor can be used to read individual rows since we have |
| 529 | + // a guarantee that each row required the same number of bytes. |
| 530 | + if (decompressor is NoneTiffCompression none && uncompressedStripSize > int.MaxValue) |
| 531 | + { |
| 532 | + ulong bytesPerRowU = this.CalculateStripBufferSize(width, 1); |
| 533 | + |
| 534 | + // This should never happen, but we check just to be sure. |
| 535 | + if (bytesPerRowU > int.MaxValue) |
| 536 | + { |
| 537 | + TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
| 538 | + } |
| 539 | + |
| 540 | + int bytesPerRow = (int)bytesPerRowU; |
| 541 | + using IMemoryOwner<byte> rowBufferOwner = this.memoryAllocator.Allocate<byte>(bytesPerRow, AllocationOptions.Clean); |
| 542 | + Span<byte> rowBuffer = rowBufferOwner.GetSpan(); |
| 543 | + for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) |
| 544 | + { |
| 545 | + cancellationToken.ThrowIfCancellationRequested(); |
| 546 | + |
| 547 | + int stripHeight = stripIndex < stripOffsets.Length - 1 || height % rowsPerStrip == 0 |
| 548 | + ? rowsPerStrip |
| 549 | + : height % rowsPerStrip; |
| 550 | + |
| 551 | + int top = rowsPerStrip * stripIndex; |
| 552 | + if (top + stripHeight > height) |
| 553 | + { |
| 554 | + break; |
| 555 | + } |
| 556 | + |
| 557 | + ulong baseOffset = stripOffsets[stripIndex]; |
| 558 | + ulong available = stripByteCounts[stripIndex]; |
| 559 | + ulong required = (ulong)bytesPerRow * (ulong)stripHeight; |
| 560 | + if (available < required) |
| 561 | + { |
| 562 | + break; |
| 563 | + } |
| 564 | + |
| 565 | + for (int r = 0; r < stripHeight; r++) |
| 566 | + { |
| 567 | + cancellationToken.ThrowIfCancellationRequested(); |
| 568 | + |
| 569 | + ulong rowOffset = baseOffset + ((ulong)r * (ulong)bytesPerRow); |
| 570 | + |
| 571 | + // Use the NoneTiffCompression decompressor to read exactly one row. |
| 572 | + none.Decompress( |
| 573 | + this.inputStream, |
| 574 | + rowOffset, |
| 575 | + (ulong)bytesPerRow, |
| 576 | + 1, |
| 577 | + rowBuffer, |
| 578 | + cancellationToken); |
| 579 | + |
| 580 | + colorDecoder.Decode(rowBuffer, pixels, 0, top + r, width, 1); |
| 581 | + } |
| 582 | + } |
| 583 | + |
| 584 | + return; |
| 585 | + } |
| 586 | + |
| 587 | + if (uncompressedStripSize > int.MaxValue) |
| 588 | + { |
| 589 | + TiffThrowHelper.ThrowNotSupported("Strips larger than Int32.MaxValue bytes are not supported for compressed images."); |
| 590 | + } |
| 591 | + |
| 592 | + using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>((int)uncompressedStripSize, AllocationOptions.Clean); |
| 593 | + Span<byte> stripBufferSpan = stripBuffer.GetSpan(); |
519 | 594 |
|
520 | 595 | for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) |
521 | 596 | { |
@@ -808,7 +883,7 @@ private IMemoryOwner<ulong> ConvertNumbers(Array array, out Span<ulong> span) |
808 | 883 | /// <param name="height">The height for the desired pixel buffer.</param> |
809 | 884 | /// <param name="plane">The index of the plane for planar image configuration (or zero for chunky).</param> |
810 | 885 | /// <returns>The size (in bytes) of the required pixel buffer.</returns> |
811 | | - private int CalculateStripBufferSize(int width, int height, int plane = -1) |
| 886 | + private ulong CalculateStripBufferSize(int width, int height, int plane = -1) |
812 | 887 | { |
813 | 888 | DebugGuard.MustBeLessThanOrEqualTo(plane, 3, nameof(plane)); |
814 | 889 |
|
@@ -841,8 +916,8 @@ private int CalculateStripBufferSize(int width, int height, int plane = -1) |
841 | 916 | } |
842 | 917 | } |
843 | 918 |
|
844 | | - int bytesPerRow = ((width * bitsPerPixel) + 7) / 8; |
845 | | - return bytesPerRow * height; |
| 919 | + ulong bytesPerRow = (((ulong)width * (ulong)bitsPerPixel) + 7) / 8; |
| 920 | + return bytesPerRow * (ulong)height; |
846 | 921 | } |
847 | 922 |
|
848 | 923 | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
0 commit comments