@@ -172,6 +172,9 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
172
172
/// <inheritdoc />
173
173
public ImageInfo Identify ( BufferedReadStream stream , CancellationToken cancellationToken )
174
174
{
175
+ uint frameCount = 0 ;
176
+ ImageFrameMetadata ? previousFrame = null ;
177
+ List < ImageFrameMetadata > framesMetadata = new ( ) ;
175
178
try
176
179
{
177
180
this . ReadLogicalScreenDescriptorAndGlobalColorTable ( stream ) ;
@@ -182,14 +185,23 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
182
185
{
183
186
if ( nextFlag == GifConstants . ImageLabel )
184
187
{
185
- this . ReadImageDescriptor ( stream ) ;
188
+ if ( previousFrame != null && ++ frameCount == this . maxFrames )
189
+ {
190
+ break ;
191
+ }
192
+
193
+ this . ReadFrameMetadata ( stream , framesMetadata , ref previousFrame ) ;
194
+
195
+ // Reset per-frame state.
196
+ this . imageDescriptor = default ;
197
+ this . graphicsControlExtension = default ;
186
198
}
187
199
else if ( nextFlag == GifConstants . ExtensionIntroducer )
188
200
{
189
201
switch ( stream . ReadByte ( ) )
190
202
{
191
203
case GifConstants . GraphicControlLabel :
192
- SkipBlock ( stream ) ; // Skip graphic control extension block
204
+ this . ReadGraphicalControlExtension ( stream ) ;
193
205
break ;
194
206
case GifConstants . CommentLabel :
195
207
this . ReadComments ( stream ) ;
@@ -226,9 +238,9 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
226
238
227
239
return new ImageInfo (
228
240
new PixelTypeInfo ( this . logicalScreenDescriptor . BitsPerPixel ) ,
229
- this . logicalScreenDescriptor . Width ,
230
- this . logicalScreenDescriptor . Height ,
231
- this . metadata ) ;
241
+ new ( this . logicalScreenDescriptor . Width , this . logicalScreenDescriptor . Height ) ,
242
+ this . metadata ,
243
+ framesMetadata ) ;
232
244
}
233
245
234
246
/// <summary>
@@ -486,7 +498,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
486
498
image = new Image < TPixel > ( this . configuration , imageWidth , imageHeight , this . metadata ) ;
487
499
}
488
500
489
- this . SetFrameMetadata ( image . Frames . RootFrame . Metadata , true ) ;
501
+ this . SetFrameMetadata ( image . Frames . RootFrame . Metadata ) ;
490
502
491
503
imageFrame = image . Frames . RootFrame ;
492
504
}
@@ -499,7 +511,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
499
511
500
512
currentFrame = image ! . Frames . CreateFrame ( ) ;
501
513
502
- this . SetFrameMetadata ( currentFrame . Metadata , false ) ;
514
+ this . SetFrameMetadata ( currentFrame . Metadata ) ;
503
515
504
516
imageFrame = currentFrame ;
505
517
@@ -606,6 +618,37 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TP
606
618
}
607
619
}
608
620
621
+ /// <summary>
622
+ /// Reads the frames metadata.
623
+ /// </summary>
624
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
625
+ /// <param name="frameMetadata">The collection of frame metadata.</param>
626
+ /// <param name="previousFrame">The previous frame metadata.</param>
627
+ private void ReadFrameMetadata ( BufferedReadStream stream , List < ImageFrameMetadata > frameMetadata , ref ImageFrameMetadata ? previousFrame )
628
+ {
629
+ this . ReadImageDescriptor ( stream ) ;
630
+
631
+ // Skip the color table for this frame if local.
632
+ if ( this . imageDescriptor . LocalColorTableFlag )
633
+ {
634
+ stream . Skip ( this . imageDescriptor . LocalColorTableSize * 3 ) ;
635
+ }
636
+
637
+ // Skip the frame indices. Pixels length + mincode size.
638
+ // The gif format does not tell us the length of the compressed data beforehand.
639
+ int minCodeSize = stream . ReadByte ( ) ;
640
+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream ) ;
641
+ lzwDecoder . SkipIndices ( minCodeSize , this . imageDescriptor . Width * this . imageDescriptor . Height ) ;
642
+
643
+ ImageFrameMetadata currentFrame = new ( ) ;
644
+ frameMetadata . Add ( currentFrame ) ;
645
+ this . SetFrameMetadata ( currentFrame ) ;
646
+ previousFrame = currentFrame ;
647
+
648
+ // Skip any remaining blocks
649
+ SkipBlock ( stream ) ;
650
+ }
651
+
609
652
/// <summary>
610
653
/// Restores the current frame area to the background.
611
654
/// </summary>
@@ -627,34 +670,33 @@ private void RestoreToBackground<TPixel>(ImageFrame<TPixel> frame)
627
670
}
628
671
629
672
/// <summary>
630
- /// Sets the frames metadata.
673
+ /// Sets the metadata for the image frame .
631
674
/// </summary>
632
- /// <param name="meta">The metadata.</param>
633
- /// <param name="isRoot">Whether the metadata represents the root frame.</param>
675
+ /// <param name="metadata">The metadata.</param>
634
676
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
635
- private void SetFrameMetadata ( ImageFrameMetadata meta , bool isRoot )
677
+ private void SetFrameMetadata ( ImageFrameMetadata metadata )
636
678
{
637
679
// Frames can either use the global table or their own local table.
638
- if ( isRoot && this . logicalScreenDescriptor . GlobalColorTableFlag
680
+ if ( this . logicalScreenDescriptor . GlobalColorTableFlag
639
681
&& this . logicalScreenDescriptor . GlobalColorTableSize > 0 )
640
682
{
641
- GifFrameMetadata gifMeta = meta . GetGifMetadata ( ) ;
683
+ GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
642
684
gifMeta . ColorTableMode = GifColorTableMode . Global ;
643
685
gifMeta . ColorTableLength = this . logicalScreenDescriptor . GlobalColorTableSize ;
644
686
}
645
687
646
688
if ( this . imageDescriptor . LocalColorTableFlag
647
689
&& this . imageDescriptor . LocalColorTableSize > 0 )
648
690
{
649
- GifFrameMetadata gifMeta = meta . GetGifMetadata ( ) ;
691
+ GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
650
692
gifMeta . ColorTableMode = GifColorTableMode . Local ;
651
693
gifMeta . ColorTableLength = this . imageDescriptor . LocalColorTableSize ;
652
694
}
653
695
654
696
// Graphics control extensions is optional.
655
697
if ( this . graphicsControlExtension != default )
656
698
{
657
- GifFrameMetadata gifMeta = meta . GetGifMetadata ( ) ;
699
+ GifFrameMetadata gifMeta = metadata . GetGifMetadata ( ) ;
658
700
gifMeta . FrameDelay = this . graphicsControlExtension . DelayTime ;
659
701
gifMeta . DisposalMethod = this . graphicsControlExtension . DisposalMethod ;
660
702
}
0 commit comments