@@ -377,66 +377,50 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
377377 {
378378 this . ReadImageDescriptor ( ) ;
379379
380- IMemoryOwner < byte > localColorTable = null ;
381380 Buffer2D < byte > indices = null ;
382- try
383- {
384- // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
385- if ( this . imageDescriptor . LocalColorTableFlag )
386- {
387- int length = this . imageDescriptor . LocalColorTableSize * 3 ;
388- localColorTable = this . Configuration . MemoryAllocator . Allocate < byte > ( length , AllocationOptions . Clean ) ;
389- this . stream . Read ( localColorTable . GetSpan ( ) ) ;
390- }
391-
392- indices = this . Configuration . MemoryAllocator . Allocate2D < byte > ( this . imageDescriptor . Width , this . imageDescriptor . Height , AllocationOptions . Clean ) ;
393- this . ReadFrameIndices ( indices ) ;
394-
395- Span < byte > rawColorTable = default ;
396- if ( localColorTable != null )
397- {
398- rawColorTable = localColorTable . GetSpan ( ) ;
399- }
400- else if ( this . globalColorTable != null )
401- {
402- rawColorTable = this . globalColorTable . GetSpan ( ) ;
403- }
404-
405- ReadOnlySpan < Rgb24 > colorTable = MemoryMarshal . Cast < byte , Rgb24 > ( rawColorTable ) ;
406- this . ReadFrameColors ( ref image , ref previousFrame , indices , colorTable , this . imageDescriptor ) ;
407-
408- // Skip any remaining blocks
409- this . SkipBlock ( ) ;
410- }
411- finally
412- {
413- localColorTable ? . Dispose ( ) ;
414- indices ? . Dispose ( ) ;
415- }
381+ // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
382+ bool hasLocalColorTable = this . imageDescriptor . LocalColorTableFlag ;
383+ if ( hasLocalColorTable )
384+ {
385+ // Read and store the local color table. We allocate the maximum possible size and slice to match.
386+ int length = this . currentLocalColorTableSize = this . imageDescriptor . LocalColorTableSize * 3 ;
387+ this . currentLocalColorTable ??= this . configuration . MemoryAllocator . Allocate < byte > ( 768 , AllocationOptions . Clean ) ;
388+ stream . Read ( this . currentLocalColorTable . GetSpan ( ) [ ..length ] ) ;
416389 }
417390
418- /// <summary>
419- /// Reads the frame indices marking the color to use for each pixel.
420- /// </summary>
421- /// <param name="indices">The 2D pixel buffer to write to.</param>
422- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
423- private void ReadFrameIndices ( Buffer2D < byte > indices )
391+ Span < byte > rawColorTable = default ;
392+ if ( hasLocalColorTable )
393+ {
394+ rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ .. this . currentLocalColorTableSize ] ;
395+ }
396+ else if ( this . globalColorTable != null )
424397 {
425- int minCodeSize = this . stream . ReadByte ( ) ;
426- using var lzwDecoder = new LzwDecoder ( this . Configuration . MemoryAllocator , this . stream ) ;
427- lzwDecoder . DecodePixels ( minCodeSize , indices ) ;
398+ rawColorTable = this . globalColorTable . GetSpan ( ) ;
428399 }
429400
401+ ReadOnlySpan < Rgb24 > colorTable = MemoryMarshal . Cast < byte , Rgb24 > ( rawColorTable ) ;
402+ this . ReadFrameColors ( stream , ref image , ref previousFrame , colorTable , this . imageDescriptor ) ;
403+
404+ // Skip any remaining blocks
405+ SkipBlock ( stream ) ;
406+ }
407+ localColorTable ? . Dispose ( ) ;
408+
430409 /// <summary>
431410 /// Reads the frames colors, mapping indices to colors.
432411 /// </summary>
433412 /// <typeparam name="TPixel">The pixel format.</typeparam>
413+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
434414 /// <param name="image">The image to decode the information to.</param>
435415 /// <param name="previousFrame">The previous frame.</param>
436- /// <param name="indices">The indexed pixels.</param>
437416 /// <param name="colorTable">The color table containing the available colors.</param>
438417 /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
439- private void ReadFrameColors < TPixel > ( ref Image < TPixel > image , ref ImageFrame < TPixel > previousFrame , Buffer2D < byte > indices , ReadOnlySpan < Rgb24 > colorTable , in GifImageDescriptor descriptor )
418+ private void ReadFrameColors < TPixel > (
419+ BufferedReadStream stream ,
420+ ref Image < TPixel > ? image ,
421+ ref ImageFrame < TPixel > ? previousFrame ,
422+ ReadOnlySpan < Rgb24 > colorTable ,
423+ in GifImageDescriptor descriptor )
440424 where TPixel : unmanaged, IPixel < TPixel >
441425 {
442426 int imageWidth = this . logicalScreenDescriptor . Width ;
@@ -494,10 +478,19 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
494478 byte transIndex = this . graphicsControlExtension . TransparencyIndex ;
495479 int colorTableMaxIdx = colorTable . Length - 1 ;
496480
497- for ( int y = descriptorTop ; y < descriptorBottom && y < imageHeight ; y ++ )
481+ // For a properly encoded gif the descriptor dimensions will never exceed the logical screen dimensions.
482+ // However we have images that exceed this that can be decoded by other libraries. #1530
483+ using IMemoryOwner < byte > indicesRowOwner = this . memoryAllocator . Allocate < byte > ( descriptor . Width ) ;
484+ Span < byte > indicesRow = indicesRowOwner . Memory . Span ;
485+ ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indicesRow ) ;
486+
487+ int minCodeSize = stream . ReadByte ( ) ;
488+ if ( LzwDecoder . IsValidMinCodeSize ( minCodeSize ) )
498489 {
499- ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indices . DangerousGetRowSpan ( y - descriptorTop ) ) ;
490+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
500491
492+ for ( int y = descriptorTop ; y < descriptorBottom && y < imageHeight ; y ++ )
493+ {
501494 // Check if this image is interlaced.
502495 int writeY ; // the target y offset to write to
503496 if ( descriptor . InterlaceFlag )
@@ -524,23 +517,24 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
524517 }
525518 }
526519
527- writeY = interlaceY + descriptor . Top ;
520+ writeY = Math . Min ( interlaceY + descriptor . Top , image . Height ) ;
528521 interlaceY += interlaceIncrement ;
529522 }
530523 else
531524 {
532525 writeY = y ;
533526 }
534527
528+ lzwDecoder . DecodePixelRow ( indicesRow ) ;
535529 ref TPixel rowRef = ref MemoryMarshal . GetReference ( imageFrame . PixelBuffer . DangerousGetRowSpan ( writeY ) ) ;
536530
537531 if ( ! transFlag )
538532 {
539533 // #403 The left + width value can be larger than the image width
540534 for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
541535 {
542- int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , x - descriptorLeft ) , 0 , colorTableMaxIdx ) ;
543- ref TPixel pixel = ref Unsafe . Add ( ref rowRef , x ) ;
536+ int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) , 0 , colorTableMaxIdx ) ;
537+ ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
544538 Rgb24 rgb = colorTable [ index ] ;
545539 pixel . FromRgb24 ( rgb ) ;
546540 }
@@ -549,16 +543,20 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
549543 {
550544 for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
551545 {
552- int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , x - descriptorLeft ) , 0 , colorTableMaxIdx ) ;
546+ int index = Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) ;
553547 if ( transIndex != index )
548+ // Treat any out of bounds values as transparent.
549+ if ( index > colorTableMaxIdx || index == transIndex )
554550 {
555- ref TPixel pixel = ref Unsafe . Add ( ref rowRef , x ) ;
556- Rgb24 rgb = colorTable [ index ] ;
557- pixel . FromRgb24 ( rgb ) ;
551+ continue ;
558552 }
553+ ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
554+ Rgb24 rgb = colorTable [ index ] ;
555+ pixel . FromRgb24 ( rgb ) ;
559556 }
560557 }
561558 }
559+ }
562560
563561 if ( prevFrame != null )
564562 {
@@ -575,6 +573,11 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
575573 }
576574
577575 /// <summary>
576+ if ( LzwDecoder . IsValidMinCodeSize ( minCodeSize ) )
577+ {
578+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
579+ lzwDecoder . SkipIndices ( this . imageDescriptor . Width * this . imageDescriptor . Height ) ;
580+ }
578581 /// Restores the current frame area to the background.
579582 /// </summary>
580583 /// <typeparam name="TPixel">The pixel format.</typeparam>
0 commit comments