@@ -427,68 +427,49 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
427427 {
428428 this . ReadImageDescriptor ( stream ) ;
429429
430- Buffer2D < byte > ? indices = null ;
431- try
432- {
433- // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
434- bool hasLocalColorTable = this . imageDescriptor . LocalColorTableFlag ;
435-
436- if ( hasLocalColorTable )
437- {
438- // Read and store the local color table. We allocate the maximum possible size and slice to match.
439- int length = this . currentLocalColorTableSize = this . imageDescriptor . LocalColorTableSize * 3 ;
440- this . currentLocalColorTable ??= this . configuration . MemoryAllocator . Allocate < byte > ( 768 , AllocationOptions . Clean ) ;
441- stream . Read ( this . currentLocalColorTable . GetSpan ( ) [ ..length ] ) ;
442- }
443-
444- indices = this . configuration . MemoryAllocator . Allocate2D < byte > ( this . imageDescriptor . Width , this . imageDescriptor . Height , AllocationOptions . Clean ) ;
445- this . ReadFrameIndices ( stream , indices ) ;
430+ // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
431+ bool hasLocalColorTable = this . imageDescriptor . LocalColorTableFlag ;
446432
447- Span < byte > rawColorTable = default ;
448- if ( hasLocalColorTable )
449- {
450- rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ ..this . currentLocalColorTableSize ] ;
451- }
452- else if ( this . globalColorTable != null )
453- {
454- rawColorTable = this . globalColorTable . GetSpan ( ) ;
455- }
456-
457- ReadOnlySpan < Rgb24 > colorTable = MemoryMarshal . Cast < byte , Rgb24 > ( rawColorTable ) ;
458- this . ReadFrameColors ( ref image , ref previousFrame , indices , colorTable , this . imageDescriptor ) ;
433+ if ( hasLocalColorTable )
434+ {
435+ // Read and store the local color table. We allocate the maximum possible size and slice to match.
436+ int length = this . currentLocalColorTableSize = this . imageDescriptor . LocalColorTableSize * 3 ;
437+ this . currentLocalColorTable ??= this . configuration . MemoryAllocator . Allocate < byte > ( 768 , AllocationOptions . Clean ) ;
438+ stream . Read ( this . currentLocalColorTable . GetSpan ( ) [ ..length ] ) ;
439+ }
459440
460- // Skip any remaining blocks
461- SkipBlock ( stream ) ;
441+ Span < byte > rawColorTable = default ;
442+ if ( hasLocalColorTable )
443+ {
444+ rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ ..this . currentLocalColorTableSize ] ;
462445 }
463- finally
446+ else if ( this . globalColorTable != null )
464447 {
465- indices ? . Dispose ( ) ;
448+ rawColorTable = this . globalColorTable . GetSpan ( ) ;
466449 }
467- }
468450
469- /// <summary>
470- /// Reads the frame indices marking the color to use for each pixel.
471- /// </summary>
472- /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
473- /// <param name="indices">The 2D pixel buffer to write to.</param>
474- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
475- private void ReadFrameIndices ( BufferedReadStream stream , Buffer2D < byte > indices )
476- {
477- int minCodeSize = stream . ReadByte ( ) ;
478- using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream ) ;
479- lzwDecoder . DecodePixels ( minCodeSize , indices ) ;
451+ ReadOnlySpan < Rgb24 > colorTable = MemoryMarshal . Cast < byte , Rgb24 > ( rawColorTable ) ;
452+ this . ReadFrameColors ( stream , ref image , ref previousFrame , colorTable , this . imageDescriptor ) ;
453+
454+ // Skip any remaining blocks
455+ SkipBlock ( stream ) ;
480456 }
481457
482458 /// <summary>
483459 /// Reads the frames colors, mapping indices to colors.
484460 /// </summary>
485461 /// <typeparam name="TPixel">The pixel format.</typeparam>
462+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
486463 /// <param name="image">The image to decode the information to.</param>
487464 /// <param name="previousFrame">The previous frame.</param>
488- /// <param name="indices">The indexed pixels.</param>
489465 /// <param name="colorTable">The color table containing the available colors.</param>
490466 /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
491- private void ReadFrameColors < TPixel > ( ref Image < TPixel > ? image , ref ImageFrame < TPixel > ? previousFrame , Buffer2D < byte > indices , ReadOnlySpan < Rgb24 > colorTable , in GifImageDescriptor descriptor )
467+ private void ReadFrameColors < TPixel > (
468+ BufferedReadStream stream ,
469+ ref Image < TPixel > ? image ,
470+ ref ImageFrame < TPixel > ? previousFrame ,
471+ ReadOnlySpan < Rgb24 > colorTable ,
472+ in GifImageDescriptor descriptor )
492473 where TPixel : unmanaged, IPixel < TPixel >
493474 {
494475 int imageWidth = this . logicalScreenDescriptor . Width ;
@@ -549,73 +530,83 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
549530 byte transIndex = this . graphicsControlExtension . TransparencyIndex ;
550531 int colorTableMaxIdx = colorTable . Length - 1 ;
551532
552- for ( int y = descriptorTop ; y < descriptorBottom && y < imageHeight ; y ++ )
533+ // For a properly encoded gif the descriptor dimensions will never exceed the logical screen dimensions.
534+ // However we have images that exceed this that can be decoded by other libraries. #1530
535+ using IMemoryOwner < byte > indicesRowOwner = this . memoryAllocator . Allocate < byte > ( descriptor . Width ) ;
536+ Span < byte > indicesRow = indicesRowOwner . Memory . Span ;
537+ ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indicesRow ) ;
538+
539+ int minCodeSize = stream . ReadByte ( ) ;
540+ if ( LzwDecoder . IsValidMinCodeSize ( minCodeSize ) )
553541 {
554- ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indices . DangerousGetRowSpan ( y - descriptorTop ) ) ;
542+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
555543
556- // Check if this image is interlaced.
557- int writeY ; // the target y offset to write to
558- if ( descriptor . InterlaceFlag )
544+ for ( int y = descriptorTop ; y < descriptorBottom && y < imageHeight ; y ++ )
559545 {
560- // If so then we read lines at predetermined offsets.
561- // When an entire image height worth of offset lines has been read we consider this a pass.
562- // With each pass the number of offset lines changes and the starting line changes.
563- if ( interlaceY >= descriptor . Height )
546+ // Check if this image is interlaced.
547+ int writeY ; // the target y offset to write to
548+ if ( descriptor . InterlaceFlag )
564549 {
565- interlacePass ++ ;
566- switch ( interlacePass )
550+ // If so then we read lines at predetermined offsets.
551+ // When an entire image height worth of offset lines has been read we consider this a pass.
552+ // With each pass the number of offset lines changes and the starting line changes.
553+ if ( interlaceY >= descriptor . Height )
567554 {
568- case 1 :
569- interlaceY = 4 ;
570- break ;
571- case 2 :
572- interlaceY = 2 ;
573- interlaceIncrement = 4 ;
574- break ;
575- case 3 :
576- interlaceY = 1 ;
577- interlaceIncrement = 2 ;
578- break ;
555+ interlacePass ++ ;
556+ switch ( interlacePass )
557+ {
558+ case 1 :
559+ interlaceY = 4 ;
560+ break ;
561+ case 2 :
562+ interlaceY = 2 ;
563+ interlaceIncrement = 4 ;
564+ break ;
565+ case 3 :
566+ interlaceY = 1 ;
567+ interlaceIncrement = 2 ;
568+ break ;
569+ }
579570 }
580- }
581571
582- writeY = interlaceY + descriptor . Top ;
583- interlaceY += interlaceIncrement ;
584- }
585- else
586- {
587- writeY = y ;
588- }
572+ writeY = Math . Min ( interlaceY + descriptor . Top , image . Height ) ;
573+ interlaceY += interlaceIncrement ;
574+ }
575+ else
576+ {
577+ writeY = y ;
578+ }
589579
590- ref TPixel rowRef = ref MemoryMarshal . GetReference ( imageFrame . PixelBuffer . DangerousGetRowSpan ( writeY ) ) ;
580+ lzwDecoder . DecodePixelRow ( indicesRow ) ;
581+ ref TPixel rowRef = ref MemoryMarshal . GetReference ( imageFrame . PixelBuffer . DangerousGetRowSpan ( writeY ) ) ;
591582
592- if ( ! transFlag )
593- {
594- // #403 The left + width value can be larger than the image width
595- for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
583+ if ( ! transFlag )
596584 {
597- int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) , 0 , colorTableMaxIdx ) ;
598- ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
599- Rgb24 rgb = colorTable [ index ] ;
600- pixel . FromRgb24 ( rgb ) ;
585+ // #403 The left + width value can be larger than the image width
586+ for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
587+ {
588+ int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) , 0 , colorTableMaxIdx ) ;
589+ ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
590+ Rgb24 rgb = colorTable [ index ] ;
591+ pixel . FromRgb24 ( rgb ) ;
592+ }
601593 }
602- }
603- else
604- {
605- for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
594+ else
606595 {
607- int rawIndex = Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) ;
608-
609- // Treat any out of bounds values as transparent.
610- if ( rawIndex > colorTableMaxIdx || rawIndex == transIndex )
596+ for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
611597 {
612- continue ;
613- }
598+ int index = Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) ;
599+
600+ // Treat any out of bounds values as transparent.
601+ if ( index > colorTableMaxIdx || index == transIndex )
602+ {
603+ continue ;
604+ }
614605
615- int index = Numerics . Clamp ( rawIndex , 0 , colorTableMaxIdx ) ;
616- ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
617- Rgb24 rgb = colorTable [ index ] ;
618- pixel . FromRgb24 ( rgb ) ;
606+ ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
607+ Rgb24 rgb = colorTable [ index ] ;
608+ pixel . FromRgb24 ( rgb ) ;
609+ }
619610 }
620611 }
621612 }
@@ -656,8 +647,11 @@ private void ReadFrameMetadata(BufferedReadStream stream, List<ImageFrameMetadat
656647 // Skip the frame indices. Pixels length + mincode size.
657648 // The gif format does not tell us the length of the compressed data beforehand.
658649 int minCodeSize = stream . ReadByte ( ) ;
659- using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream ) ;
660- lzwDecoder . SkipIndices ( minCodeSize , this . imageDescriptor . Width * this . imageDescriptor . Height ) ;
650+ if ( LzwDecoder . IsValidMinCodeSize ( minCodeSize ) )
651+ {
652+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
653+ lzwDecoder . SkipIndices ( this . imageDescriptor . Width * this . imageDescriptor . Height ) ;
654+ }
661655
662656 ImageFrameMetadata currentFrame = new ( ) ;
663657 frameMetadata . Add ( currentFrame ) ;
0 commit comments