@@ -29,6 +29,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
2929 /// </summary>
3030 private IMemoryOwner < byte > ? globalColorTable ;
3131
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+
3242 /// <summary>
3343 /// The area to restore.
3444 /// </summary>
@@ -159,6 +169,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
159169 finally
160170 {
161171 this . globalColorTable ? . Dispose ( ) ;
172+ this . currentLocalColorTable ? . Dispose ( ) ;
162173 }
163174
164175 if ( image is null )
@@ -229,6 +240,7 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
229240 finally
230241 {
231242 this . globalColorTable ? . Dispose ( ) ;
243+ this . currentLocalColorTable ? . Dispose ( ) ;
232244 }
233245
234246 if ( this . logicalScreenDescriptor . Width == 0 && this . logicalScreenDescriptor . Height == 0 )
@@ -332,7 +344,7 @@ private void ReadApplicationExtension(BufferedReadStream stream)
332344 if ( subBlockSize == GifConstants . NetscapeLoopingSubBlockSize )
333345 {
334346 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 ;
336348 stream . Skip ( 1 ) ; // Skip the terminator.
337349 return ;
338350 }
@@ -415,25 +427,27 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
415427 {
416428 this . ReadImageDescriptor ( stream ) ;
417429
418- IMemoryOwner < byte > ? localColorTable = null ;
419430 Buffer2D < byte > ? indices = null ;
420431 try
421432 {
422433 // 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 )
424437 {
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 ] ) ;
428442 }
429443
430444 indices = this . configuration . MemoryAllocator . Allocate2D < byte > ( this . imageDescriptor . Width , this . imageDescriptor . Height , AllocationOptions . Clean ) ;
431445 this . ReadFrameIndices ( stream , indices ) ;
432446
433447 Span < byte > rawColorTable = default ;
434- if ( localColorTable != null )
448+ if ( hasLocalColorTable )
435449 {
436- rawColorTable = localColorTable . GetSpan ( ) ;
450+ rawColorTable = this . currentLocalColorTable ! . GetSpan ( ) [ .. this . currentLocalColorTableSize ] ;
437451 }
438452 else if ( this . globalColorTable != null )
439453 {
@@ -448,7 +462,6 @@ private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
448462 }
449463 finally
450464 {
451- localColorTable ? . Dispose ( ) ;
452465 indices ? . Dispose ( ) ;
453466 }
454467 }
@@ -509,7 +522,10 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
509522 prevFrame = previousFrame ;
510523 }
511524
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 ) ;
513529
514530 this . SetFrameMetadata ( currentFrame . Metadata ) ;
515531
@@ -631,7 +647,10 @@ private void ReadFrameMetadata(BufferedReadStream stream, List<ImageFrameMetadat
631647 // Skip the color table for this frame if local.
632648 if ( this . imageDescriptor . LocalColorTableFlag )
633649 {
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 ] ) ;
635654 }
636655
637656 // Skip the frame indices. Pixels length + mincode size.
@@ -682,21 +701,30 @@ private void SetFrameMetadata(ImageFrameMetadata metadata)
682701 {
683702 GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
684703 gifMeta . ColorTableMode = GifColorTableMode . Global ;
685- gifMeta . ColorTableLength = this . logicalScreenDescriptor . GlobalColorTableSize ;
686704 }
687705
688706 if ( this . imageDescriptor . LocalColorTableFlag
689707 && this . imageDescriptor . LocalColorTableSize > 0 )
690708 {
691709 GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
692710 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 ;
694720 }
695721
696722 // Graphics control extensions is optional.
697723 if ( this . graphicsControlExtension != default )
698724 {
699725 GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
726+ gifMeta . HasTransparency = this . graphicsControlExtension . TransparencyFlag ;
727+ gifMeta . TransparencyIndex = this . graphicsControlExtension . TransparencyIndex ;
700728 gifMeta . FrameDelay = this . graphicsControlExtension . DelayTime ;
701729 gifMeta . DisposalMethod = this . graphicsControlExtension . DisposalMethod ;
702730 }
@@ -751,14 +779,22 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s
751779 if ( this . logicalScreenDescriptor . GlobalColorTableFlag )
752780 {
753781 int globalColorTableLength = this . logicalScreenDescriptor . GlobalColorTableSize * 3 ;
754- this . gifMetadata . GlobalColorTableLength = globalColorTableLength ;
755-
756782 if ( globalColorTableLength > 0 )
757783 {
758784 this . globalColorTable = this . memoryAllocator . Allocate < byte > ( globalColorTableLength , AllocationOptions . Clean ) ;
759785
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 ;
762798 }
763799 }
764800 }
0 commit comments