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