55using System . Buffers ;
66using System . Runtime . CompilerServices ;
77using SixLabors . ImageSharp . Formats . Tiff . Compression ;
8+ using SixLabors . ImageSharp . Formats . Tiff . Compression . Decompressors ;
89using SixLabors . ImageSharp . Formats . Tiff . Constants ;
910using SixLabors . ImageSharp . Formats . Tiff . PhotometricInterpretation ;
1011using SixLabors . ImageSharp . IO ;
@@ -403,8 +404,14 @@ private void DecodeStripsPlanar<TPixel>(ImageFrame<TPixel> frame, int rowsPerStr
403404 {
404405 for ( int stripIndex = 0 ; stripIndex < stripBuffers . Length ; stripIndex ++ )
405406 {
406- int uncompressedStripSize = this . CalculateStripBufferSize ( frame . Width , rowsPerStrip , stripIndex ) ;
407- stripBuffers [ stripIndex ] = this . memoryAllocator . Allocate < byte > ( uncompressedStripSize ) ;
407+ ulong uncompressedStripSize = this . CalculateStripBufferSize ( frame . Width , rowsPerStrip , stripIndex ) ;
408+
409+ if ( uncompressedStripSize > int . MaxValue )
410+ {
411+ TiffThrowHelper . ThrowNotSupported ( "Strips larger than Int32.MaxValue bytes are not supported for compressed images." ) ;
412+ }
413+
414+ stripBuffers [ stripIndex ] = this . memoryAllocator . Allocate < byte > ( ( int ) uncompressedStripSize ) ;
408415 }
409416
410417 using TiffBaseDecompressor decompressor = this . CreateDecompressor < TPixel > ( frame . Width , bitsPerPixel ) ;
@@ -460,26 +467,97 @@ private void DecodeStripsChunky<TPixel>(ImageFrame<TPixel> frame, int rowsPerStr
460467 rowsPerStrip = frame . Height ;
461468 }
462469
463- int uncompressedStripSize = this . CalculateStripBufferSize ( frame . Width , rowsPerStrip ) ;
470+ ulong uncompressedStripSize = this . CalculateStripBufferSize ( frame . Width , rowsPerStrip ) ;
464471 int bitsPerPixel = this . BitsPerPixel ;
465-
466- using IMemoryOwner < byte > stripBuffer = this . memoryAllocator . Allocate < byte > ( uncompressedStripSize , AllocationOptions . Clean ) ;
467- Span < byte > stripBufferSpan = stripBuffer . GetSpan ( ) ;
468472 Buffer2D < TPixel > pixels = frame . PixelBuffer ;
469473
470- using TiffBaseDecompressor decompressor = this . CreateDecompressor < TPixel > ( frame . Width , bitsPerPixel ) ;
474+ int width = frame . Width ;
475+ int height = frame . Height ;
476+
477+ using TiffBaseDecompressor decompressor = this . CreateDecompressor < TPixel > ( width , bitsPerPixel ) ;
471478 TiffBaseColorDecoder < TPixel > colorDecoder = this . CreateChunkyColorDecoder < TPixel > ( ) ;
472479
480+ // There exists in this world TIFF files with uncompressed strips larger than Int32.MaxValue.
481+ // We can read them, but we cannot allocate a buffer that large to hold the uncompressed data.
482+ // In this scenario we fall back to reading and decoding one row at a time.
483+ //
484+ // The NoneTiffCompression decompressor can be used to read individual rows since we have
485+ // a guarantee that each row required the same number of bytes.
486+ if ( decompressor is NoneTiffCompression none && uncompressedStripSize > int . MaxValue )
487+ {
488+ ulong bytesPerRowU = this . CalculateStripBufferSize ( frame . Width , 1 ) ;
489+
490+ // This should never happen, but we check just to be sure.
491+ if ( bytesPerRowU > int . MaxValue )
492+ {
493+ TiffThrowHelper . ThrowNotSupported ( "Strips larger than Int32.MaxValue bytes are not supported for compressed images." ) ;
494+ }
495+
496+ int bytesPerRow = ( int ) bytesPerRowU ;
497+ using IMemoryOwner < byte > rowBufferOwner = this . memoryAllocator . Allocate < byte > ( bytesPerRow , AllocationOptions . Clean ) ;
498+ Span < byte > rowBuffer = rowBufferOwner . GetSpan ( ) ;
499+ for ( int stripIndex = 0 ; stripIndex < stripOffsets . Length ; stripIndex ++ )
500+ {
501+ cancellationToken . ThrowIfCancellationRequested ( ) ;
502+
503+ int stripHeight = stripIndex < stripOffsets . Length - 1 || height % rowsPerStrip == 0
504+ ? rowsPerStrip
505+ : height % rowsPerStrip ;
506+
507+ int top = rowsPerStrip * stripIndex ;
508+ if ( top + stripHeight > height )
509+ {
510+ break ;
511+ }
512+
513+ ulong baseOffset = stripOffsets [ stripIndex ] ;
514+ ulong available = stripByteCounts [ stripIndex ] ;
515+ ulong required = ( ulong ) bytesPerRow * ( ulong ) stripHeight ;
516+ if ( available < required )
517+ {
518+ break ;
519+ }
520+
521+ for ( int r = 0 ; r < stripHeight ; r ++ )
522+ {
523+ cancellationToken . ThrowIfCancellationRequested ( ) ;
524+
525+ ulong rowOffset = baseOffset + ( ( ulong ) r * ( ulong ) bytesPerRow ) ;
526+
527+ // Use the NoneTiffCompression decompressor to read exactly one row.
528+ none . Decompress (
529+ this . inputStream ,
530+ rowOffset ,
531+ ( ulong ) bytesPerRow ,
532+ 1 ,
533+ rowBuffer ,
534+ cancellationToken ) ;
535+
536+ colorDecoder . Decode ( rowBuffer , pixels , 0 , top + r , width , 1 ) ;
537+ }
538+ }
539+
540+ return ;
541+ }
542+
543+ if ( uncompressedStripSize > int . MaxValue )
544+ {
545+ TiffThrowHelper . ThrowNotSupported ( "Strips larger than Int32.MaxValue bytes are not supported for compressed images." ) ;
546+ }
547+
548+ using IMemoryOwner < byte > stripBuffer = this . memoryAllocator . Allocate < byte > ( ( int ) uncompressedStripSize , AllocationOptions . Clean ) ;
549+ Span < byte > stripBufferSpan = stripBuffer . GetSpan ( ) ;
550+
473551 for ( int stripIndex = 0 ; stripIndex < stripOffsets . Length ; stripIndex ++ )
474552 {
475553 cancellationToken . ThrowIfCancellationRequested ( ) ;
476554
477- int stripHeight = stripIndex < stripOffsets . Length - 1 || frame . Height % rowsPerStrip == 0
555+ int stripHeight = stripIndex < stripOffsets . Length - 1 || height % rowsPerStrip == 0
478556 ? rowsPerStrip
479- : frame . Height % rowsPerStrip ;
557+ : height % rowsPerStrip ;
480558
481559 int top = rowsPerStrip * stripIndex ;
482- if ( top + stripHeight > frame . Height )
560+ if ( top + stripHeight > height )
483561 {
484562 // Make sure we ignore any strips that are not needed for the image (if too many are present).
485563 break ;
@@ -493,7 +571,7 @@ private void DecodeStripsChunky<TPixel>(ImageFrame<TPixel> frame, int rowsPerStr
493571 stripBufferSpan ,
494572 cancellationToken ) ;
495573
496- colorDecoder . Decode ( stripBufferSpan , pixels , 0 , top , frame . Width , stripHeight ) ;
574+ colorDecoder . Decode ( stripBufferSpan , pixels , 0 , top , width , stripHeight ) ;
497575 }
498576 }
499577
@@ -753,7 +831,7 @@ private IMemoryOwner<ulong> ConvertNumbers(Array array, out Span<ulong> span)
753831 /// <param name="height">The height for the desired pixel buffer.</param>
754832 /// <param name="plane">The index of the plane for planar image configuration (or zero for chunky).</param>
755833 /// <returns>The size (in bytes) of the required pixel buffer.</returns>
756- private int CalculateStripBufferSize ( int width , int height , int plane = - 1 )
834+ private ulong CalculateStripBufferSize ( int width , int height , int plane = - 1 )
757835 {
758836 DebugGuard . MustBeLessThanOrEqualTo ( plane , 3 , nameof ( plane ) ) ;
759837
@@ -786,8 +864,8 @@ private int CalculateStripBufferSize(int width, int height, int plane = -1)
786864 }
787865 }
788866
789- int bytesPerRow = ( ( width * bitsPerPixel ) + 7 ) / 8 ;
790- return bytesPerRow * height ;
867+ ulong bytesPerRow = ( ( ( ulong ) width * ( ulong ) bitsPerPixel ) + 7 ) / 8 ;
868+ return bytesPerRow * ( ulong ) height ;
791869 }
792870
793871 /// <summary>
0 commit comments