@@ -427,68 +427,49 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
427
427
{
428
428
this . ReadImageDescriptor ( stream ) ;
429
429
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 ;
446
432
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
+ }
459
440
460
- // Skip any remaining blocks
461
- SkipBlock ( stream ) ;
441
+ Span < byte > rawColorTable = default ;
442
+ if ( hasLocalColorTable )
443
+ {
444
+ rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ ..this . currentLocalColorTableSize ] ;
462
445
}
463
- finally
446
+ else if ( this . globalColorTable != null )
464
447
{
465
- indices ? . Dispose ( ) ;
448
+ rawColorTable = this . globalColorTable . GetSpan ( ) ;
466
449
}
467
- }
468
450
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 ) ;
480
456
}
481
457
482
458
/// <summary>
483
459
/// Reads the frames colors, mapping indices to colors.
484
460
/// </summary>
485
461
/// <typeparam name="TPixel">The pixel format.</typeparam>
462
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
486
463
/// <param name="image">The image to decode the information to.</param>
487
464
/// <param name="previousFrame">The previous frame.</param>
488
- /// <param name="indices">The indexed pixels.</param>
489
465
/// <param name="colorTable">The color table containing the available colors.</param>
490
466
/// <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 )
492
473
where TPixel : unmanaged, IPixel < TPixel >
493
474
{
494
475
int imageWidth = this . logicalScreenDescriptor . Width ;
@@ -549,73 +530,83 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
549
530
byte transIndex = this . graphicsControlExtension . TransparencyIndex ;
550
531
int colorTableMaxIdx = colorTable . Length - 1 ;
551
532
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 ) )
553
541
{
554
- ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indices . DangerousGetRowSpan ( y - descriptorTop ) ) ;
542
+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
555
543
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 ++ )
559
545
{
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 )
564
549
{
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 )
567
554
{
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
+ }
579
570
}
580
- }
581
571
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
+ }
589
579
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 ) ) ;
591
582
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 )
596
584
{
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
+ }
601
593
}
602
- }
603
- else
604
- {
605
- for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
594
+ else
606
595
{
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 ++ )
611
597
{
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
+ }
614
605
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
+ }
619
610
}
620
611
}
621
612
}
@@ -656,8 +647,11 @@ private void ReadFrameMetadata(BufferedReadStream stream, List<ImageFrameMetadat
656
647
// Skip the frame indices. Pixels length + mincode size.
657
648
// The gif format does not tell us the length of the compressed data beforehand.
658
649
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
+ }
661
655
662
656
ImageFrameMetadata currentFrame = new ( ) ;
663
657
frameMetadata . Add ( currentFrame ) ;
0 commit comments