@@ -29,6 +29,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
29
29
/// </summary>
30
30
private IMemoryOwner < byte > ? globalColorTable ;
31
31
32
+ /// <summary>
33
+ /// The current local color table.
34
+ /// </summary>
35
+ private IMemoryOwner < byte > ? currentLocalColorTable ;
36
+
37
+ /// <summary>
38
+ /// Gets the size in bytes of the current local color table.
39
+ /// </summary>
40
+ private int currentLocalColorTableSize ;
41
+
32
42
/// <summary>
33
43
/// The area to restore.
34
44
/// </summary>
@@ -159,6 +169,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
159
169
finally
160
170
{
161
171
this . globalColorTable ? . Dispose ( ) ;
172
+ this . currentLocalColorTable ? . Dispose ( ) ;
162
173
}
163
174
164
175
if ( image is null )
@@ -229,6 +240,7 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
229
240
finally
230
241
{
231
242
this . globalColorTable ? . Dispose ( ) ;
243
+ this . currentLocalColorTable ? . Dispose ( ) ;
232
244
}
233
245
234
246
if ( this . logicalScreenDescriptor . Width == 0 && this . logicalScreenDescriptor . Height == 0 )
@@ -332,7 +344,7 @@ private void ReadApplicationExtension(BufferedReadStream stream)
332
344
if ( subBlockSize == GifConstants . NetscapeLoopingSubBlockSize )
333
345
{
334
346
stream . Read ( this . buffer . Span , 0 , GifConstants . NetscapeLoopingSubBlockSize ) ;
335
- this . gifMetadata ! . RepeatCount = GifNetscapeLoopingApplicationExtension . Parse ( this . buffer . Span . Slice ( 1 ) ) . RepeatCount ;
347
+ this . gifMetadata ! . RepeatCount = GifNetscapeLoopingApplicationExtension . Parse ( this . buffer . Span [ 1 .. ] ) . RepeatCount ;
336
348
stream . Skip ( 1 ) ; // Skip the terminator.
337
349
return ;
338
350
}
@@ -415,25 +427,27 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
415
427
{
416
428
this . ReadImageDescriptor ( stream ) ;
417
429
418
- IMemoryOwner < byte > ? localColorTable = null ;
419
430
Buffer2D < byte > ? indices = null ;
420
431
try
421
432
{
422
433
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
423
- if ( this . imageDescriptor . LocalColorTableFlag )
434
+ bool hasLocalColorTable = this . imageDescriptor . LocalColorTableFlag ;
435
+
436
+ if ( hasLocalColorTable )
424
437
{
425
- int length = this . imageDescriptor . LocalColorTableSize * 3 ;
426
- localColorTable = this . configuration . MemoryAllocator . Allocate < byte > ( length , AllocationOptions . Clean ) ;
427
- stream . Read ( localColorTable . GetSpan ( ) ) ;
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 ] ) ;
428
442
}
429
443
430
444
indices = this . configuration . MemoryAllocator . Allocate2D < byte > ( this . imageDescriptor . Width , this . imageDescriptor . Height , AllocationOptions . Clean ) ;
431
445
this . ReadFrameIndices ( stream , indices ) ;
432
446
433
447
Span < byte > rawColorTable = default ;
434
- if ( localColorTable != null )
448
+ if ( hasLocalColorTable )
435
449
{
436
- rawColorTable = localColorTable . GetSpan ( ) ;
450
+ rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ .. this . currentLocalColorTableSize ] ;
437
451
}
438
452
else if ( this . globalColorTable != null )
439
453
{
@@ -448,7 +462,6 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
448
462
}
449
463
finally
450
464
{
451
- localColorTable ? . Dispose ( ) ;
452
465
indices ? . Dispose ( ) ;
453
466
}
454
467
}
@@ -509,7 +522,10 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
509
522
prevFrame = previousFrame ;
510
523
}
511
524
512
- currentFrame = image ! . Frames . CreateFrame ( ) ;
525
+ // We create a clone of the frame and add it.
526
+ // We will overpaint the difference of pixels on the current frame to create a complete image.
527
+ // This ensures that we have enough pixel data to process without distortion. #2450
528
+ currentFrame = image ! . Frames . AddFrame ( previousFrame ) ;
513
529
514
530
this . SetFrameMetadata ( currentFrame . Metadata ) ;
515
531
@@ -631,7 +647,10 @@ private void ReadFrameMetadata(BufferedReadStream stream, List<ImageFrameMetadat
631
647
// Skip the color table for this frame if local.
632
648
if ( this . imageDescriptor . LocalColorTableFlag )
633
649
{
634
- stream . Skip ( this . imageDescriptor . LocalColorTableSize * 3 ) ;
650
+ // Read and store the local color table. We allocate the maximum possible size and slice to match.
651
+ int length = this . currentLocalColorTableSize = this . imageDescriptor . LocalColorTableSize * 3 ;
652
+ this . currentLocalColorTable ??= this . configuration . MemoryAllocator . Allocate < byte > ( 768 , AllocationOptions . Clean ) ;
653
+ stream . Read ( this . currentLocalColorTable . GetSpan ( ) [ ..length ] ) ;
635
654
}
636
655
637
656
// Skip the frame indices. Pixels length + mincode size.
@@ -682,21 +701,30 @@ private void SetFrameMetadata(ImageFrameMetadata metadata)
682
701
{
683
702
GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
684
703
gifMeta . ColorTableMode = GifColorTableMode . Global ;
685
- gifMeta . ColorTableLength = this . logicalScreenDescriptor . GlobalColorTableSize ;
686
704
}
687
705
688
706
if ( this . imageDescriptor . LocalColorTableFlag
689
707
&& this . imageDescriptor . LocalColorTableSize > 0 )
690
708
{
691
709
GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
692
710
gifMeta . ColorTableMode = GifColorTableMode . Local ;
693
- gifMeta . ColorTableLength = this . imageDescriptor . LocalColorTableSize ;
711
+
712
+ Color [ ] colorTable = new Color [ this . imageDescriptor . LocalColorTableSize ] ;
713
+ ReadOnlySpan < Rgb24 > rgbTable = MemoryMarshal . Cast < byte , Rgb24 > ( this . currentLocalColorTable ! . GetSpan ( ) [ ..this . currentLocalColorTableSize ] ) ;
714
+ for ( int i = 0 ; i < colorTable . Length ; i ++ )
715
+ {
716
+ colorTable [ i ] = new Color ( rgbTable [ i ] ) ;
717
+ }
718
+
719
+ gifMeta . LocalColorTable = colorTable ;
694
720
}
695
721
696
722
// Graphics control extensions is optional.
697
723
if ( this . graphicsControlExtension != default )
698
724
{
699
725
GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
726
+ gifMeta . HasTransparency = this . graphicsControlExtension . TransparencyFlag ;
727
+ gifMeta . TransparencyIndex = this . graphicsControlExtension . TransparencyIndex ;
700
728
gifMeta . FrameDelay = this . graphicsControlExtension . DelayTime ;
701
729
gifMeta . DisposalMethod = this . graphicsControlExtension . DisposalMethod ;
702
730
}
@@ -751,14 +779,22 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s
751
779
if ( this . logicalScreenDescriptor . GlobalColorTableFlag )
752
780
{
753
781
int globalColorTableLength = this . logicalScreenDescriptor . GlobalColorTableSize * 3 ;
754
- this . gifMetadata . GlobalColorTableLength = globalColorTableLength ;
755
-
756
782
if ( globalColorTableLength > 0 )
757
783
{
758
784
this . globalColorTable = this . memoryAllocator . Allocate < byte > ( globalColorTableLength , AllocationOptions . Clean ) ;
759
785
760
- // Read the global color table data from the stream
761
- stream . Read ( this . globalColorTable . GetSpan ( ) ) ;
786
+ // Read the global color table data from the stream and preserve it in the gif metadata
787
+ Span < byte > globalColorTableSpan = this . globalColorTable . GetSpan ( ) ;
788
+ stream . Read ( globalColorTableSpan ) ;
789
+
790
+ Color [ ] colorTable = new Color [ this . logicalScreenDescriptor . GlobalColorTableSize ] ;
791
+ ReadOnlySpan < Rgb24 > rgbTable = MemoryMarshal . Cast < byte , Rgb24 > ( globalColorTableSpan ) ;
792
+ for ( int i = 0 ; i < colorTable . Length ; i ++ )
793
+ {
794
+ colorTable [ i ] = new Color ( rgbTable [ i ] ) ;
795
+ }
796
+
797
+ this . gifMetadata . GlobalColorTable = colorTable ;
762
798
}
763
799
}
764
800
}
0 commit comments