@@ -377,66 +377,50 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
377
377
{
378
378
this . ReadImageDescriptor ( ) ;
379
379
380
- IMemoryOwner < byte > localColorTable = null ;
381
380
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 ] ) ;
416
389
}
417
390
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 )
424
397
{
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 ( ) ;
428
399
}
429
400
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
+
430
409
/// <summary>
431
410
/// Reads the frames colors, mapping indices to colors.
432
411
/// </summary>
433
412
/// <typeparam name="TPixel">The pixel format.</typeparam>
413
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
434
414
/// <param name="image">The image to decode the information to.</param>
435
415
/// <param name="previousFrame">The previous frame.</param>
436
- /// <param name="indices">The indexed pixels.</param>
437
416
/// <param name="colorTable">The color table containing the available colors.</param>
438
417
/// <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 )
440
424
where TPixel : unmanaged, IPixel < TPixel >
441
425
{
442
426
int imageWidth = this . logicalScreenDescriptor . Width ;
@@ -494,10 +478,19 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
494
478
byte transIndex = this . graphicsControlExtension . TransparencyIndex ;
495
479
int colorTableMaxIdx = colorTable . Length - 1 ;
496
480
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 ) )
498
489
{
499
- ref byte indicesRowRef = ref MemoryMarshal . GetReference ( indices . DangerousGetRowSpan ( y - descriptorTop ) ) ;
490
+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream , minCodeSize ) ;
500
491
492
+ for ( int y = descriptorTop ; y < descriptorBottom && y < imageHeight ; y ++ )
493
+ {
501
494
// Check if this image is interlaced.
502
495
int writeY ; // the target y offset to write to
503
496
if ( descriptor . InterlaceFlag )
@@ -524,23 +517,24 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
524
517
}
525
518
}
526
519
527
- writeY = interlaceY + descriptor . Top ;
520
+ writeY = Math . Min ( interlaceY + descriptor . Top , image . Height ) ;
528
521
interlaceY += interlaceIncrement ;
529
522
}
530
523
else
531
524
{
532
525
writeY = y ;
533
526
}
534
527
528
+ lzwDecoder . DecodePixelRow ( indicesRow ) ;
535
529
ref TPixel rowRef = ref MemoryMarshal . GetReference ( imageFrame . PixelBuffer . DangerousGetRowSpan ( writeY ) ) ;
536
530
537
531
if ( ! transFlag )
538
532
{
539
533
// #403 The left + width value can be larger than the image width
540
534
for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
541
535
{
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 ) ;
544
538
Rgb24 rgb = colorTable [ index ] ;
545
539
pixel . FromRgb24 ( rgb ) ;
546
540
}
@@ -549,16 +543,20 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
549
543
{
550
544
for ( int x = descriptorLeft ; x < descriptorRight && x < imageWidth ; x ++ )
551
545
{
552
- int index = Numerics . Clamp ( Unsafe . Add ( ref indicesRowRef , x - descriptorLeft ) , 0 , colorTableMaxIdx ) ;
546
+ int index = Unsafe . Add ( ref indicesRowRef , ( uint ) ( x - descriptorLeft ) ) ;
553
547
if ( transIndex != index )
548
+ // Treat any out of bounds values as transparent.
549
+ if ( index > colorTableMaxIdx || index == transIndex )
554
550
{
555
- ref TPixel pixel = ref Unsafe . Add ( ref rowRef , x ) ;
556
- Rgb24 rgb = colorTable [ index ] ;
557
- pixel . FromRgb24 ( rgb ) ;
551
+ continue ;
558
552
}
553
+ ref TPixel pixel = ref Unsafe . Add ( ref rowRef , ( uint ) x ) ;
554
+ Rgb24 rgb = colorTable [ index ] ;
555
+ pixel . FromRgb24 ( rgb ) ;
559
556
}
560
557
}
561
558
}
559
+ }
562
560
563
561
if ( prevFrame != null )
564
562
{
@@ -575,6 +573,11 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
575
573
}
576
574
577
575
/// <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
+ }
578
581
/// Restores the current frame area to the background.
579
582
/// </summary>
580
583
/// <typeparam name="TPixel">The pixel format.</typeparam>
0 commit comments