@@ -32,6 +32,11 @@ internal class WebpAnimationDecoder : IDisposable
3232 /// </summary>
3333 private readonly uint maxFrames ;
3434
35+ /// <summary>
36+ /// Whether to skip metadata.
37+ /// </summary>
38+ private readonly bool skipMetadata ;
39+
3540 /// <summary>
3641 /// The area to restore.
3742 /// </summary>
@@ -57,19 +62,97 @@ internal class WebpAnimationDecoder : IDisposable
5762 /// </summary>
5863 private readonly BackgroundColorHandling backgroundColorHandling ;
5964
65+ /// <summary>
66+ /// How to handle validation of errors in different segments of encoded image files.
67+ /// </summary>
68+ private readonly SegmentIntegrityHandling segmentIntegrityHandling ;
69+
6070 /// <summary>
6171 /// Initializes a new instance of the <see cref="WebpAnimationDecoder"/> class.
6272 /// </summary>
6373 /// <param name="memoryAllocator">The memory allocator.</param>
6474 /// <param name="configuration">The global configuration.</param>
6575 /// <param name="maxFrames">The maximum number of frames to decode. Inclusive.</param>
76+ /// <param name="skipMetadata">Whether to skip metadata.</param>
6677 /// <param name="backgroundColorHandling">The flag to decide how to handle the background color in the Animation Chunk.</param>
67- public WebpAnimationDecoder ( MemoryAllocator memoryAllocator , Configuration configuration , uint maxFrames , BackgroundColorHandling backgroundColorHandling )
78+ /// <param name="segmentIntegrityHandling">How to handle validation of errors in different segments of encoded image files.</param>
79+ public WebpAnimationDecoder (
80+ MemoryAllocator memoryAllocator ,
81+ Configuration configuration ,
82+ uint maxFrames ,
83+ bool skipMetadata ,
84+ BackgroundColorHandling backgroundColorHandling ,
85+ SegmentIntegrityHandling segmentIntegrityHandling )
6886 {
6987 this . memoryAllocator = memoryAllocator ;
7088 this . configuration = configuration ;
7189 this . maxFrames = maxFrames ;
90+ this . skipMetadata = skipMetadata ;
7291 this . backgroundColorHandling = backgroundColorHandling ;
92+ this . segmentIntegrityHandling = segmentIntegrityHandling ;
93+ }
94+
95+ /// <summary>
96+ /// Reads the animated webp image information from the specified stream.
97+ /// </summary>
98+ /// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
99+ /// <param name="features">The webp features.</param>
100+ /// <param name="width">The width of the image.</param>
101+ /// <param name="height">The height of the image.</param>
102+ /// <param name="completeDataSize">The size of the image data in bytes.</param>
103+ public ImageInfo Identify (
104+ BufferedReadStream stream ,
105+ WebpFeatures features ,
106+ uint width ,
107+ uint height ,
108+ uint completeDataSize )
109+ {
110+ List < ImageFrameMetadata > framesMetadata = [ ] ;
111+ this . metadata = new ImageMetadata ( ) ;
112+ this . webpMetadata = this . metadata . GetWebpMetadata ( ) ;
113+ this . webpMetadata . RepeatCount = features . AnimationLoopCount ;
114+
115+ Color backgroundColor = this . backgroundColorHandling == BackgroundColorHandling . Ignore
116+ ? Color . Transparent
117+ : features . AnimationBackgroundColor ! . Value ;
118+
119+ this . webpMetadata . BackgroundColor = backgroundColor ;
120+
121+ Span < byte > buffer = stackalloc byte [ 4 ] ;
122+ uint frameCount = 0 ;
123+ int remainingBytes = ( int ) completeDataSize ;
124+ while ( remainingBytes > 0 )
125+ {
126+ WebpChunkType chunkType = WebpChunkParsingUtils . ReadChunkType ( stream , buffer ) ;
127+ remainingBytes -= 4 ;
128+ switch ( chunkType )
129+ {
130+ case WebpChunkType . FrameData :
131+
132+ ImageFrameMetadata frameMetadata = new ( ) ;
133+ uint dataSize = ReadFrameInfo ( stream , ref frameMetadata ) ;
134+ framesMetadata . Add ( frameMetadata ) ;
135+
136+ remainingBytes -= ( int ) dataSize ;
137+ break ;
138+ case WebpChunkType . Xmp :
139+ case WebpChunkType . Exif :
140+ WebpChunkParsingUtils . ParseOptionalChunks ( stream , chunkType , this . metadata , this . skipMetadata , this . segmentIntegrityHandling , buffer ) ;
141+ break ;
142+ default :
143+
144+ // Specification explicitly states to ignore unknown chunks.
145+ // We do not support writing these chunks at present.
146+ break ;
147+ }
148+
149+ if ( stream . Position == stream . Length || ++ frameCount == this . maxFrames )
150+ {
151+ break ;
152+ }
153+ }
154+
155+ return new ImageInfo ( new Size ( ( int ) width , ( int ) height ) , this . metadata , framesMetadata ) ;
73156 }
74157
75158 /// <summary>
@@ -128,10 +211,12 @@ public Image<TPixel> Decode<TPixel>(
128211 break ;
129212 case WebpChunkType . Xmp :
130213 case WebpChunkType . Exif :
131- WebpChunkParsingUtils . ParseOptionalChunks ( stream , chunkType , image ! . Metadata , false , buffer ) ;
214+ WebpChunkParsingUtils . ParseOptionalChunks ( stream , chunkType , image ! . Metadata , this . skipMetadata , this . segmentIntegrityHandling , buffer ) ;
132215 break ;
133216 default :
134- WebpThrowHelper . ThrowImageFormatException ( "Read unexpected webp chunk data" ) ;
217+
218+ // Specification explicitly states to ignore unknown chunks.
219+ // We do not support writing these chunks at present.
135220 break ;
136221 }
137222
@@ -144,6 +229,26 @@ public Image<TPixel> Decode<TPixel>(
144229 return image ! ;
145230 }
146231
232+ /// <summary>
233+ /// Reads frame information from the specified stream and updates the provided frame metadata.
234+ /// </summary>
235+ /// <param name="stream">The stream from which to read the frame information. Must support reading and seeking.</param>
236+ /// <param name="frameMetadata">A reference to the structure that will be updated with the parsed frame metadata.</param>
237+ /// <returns>The number of bytes read from the stream while parsing the frame information.</returns>
238+ private static uint ReadFrameInfo ( BufferedReadStream stream , ref ImageFrameMetadata frameMetadata )
239+ {
240+ WebpFrameData frameData = WebpFrameData . Parse ( stream ) ;
241+ SetFrameMetadata ( frameMetadata , frameData ) ;
242+
243+ // Size of the frame header chunk.
244+ const int chunkHeaderSize = 16 ;
245+
246+ uint remaining = frameData . DataSize - chunkHeaderSize ;
247+ stream . Skip ( ( int ) remaining ) ;
248+
249+ return remaining ;
250+ }
251+
147252 /// <summary>
148253 /// Reads an individual webp frame.
149254 /// </summary>
@@ -155,6 +260,7 @@ public Image<TPixel> Decode<TPixel>(
155260 /// <param name="width">The width of the image.</param>
156261 /// <param name="height">The height of the image.</param>
157262 /// <param name="backgroundColor">The default background color of the canvas in.</param>
263+ /// <returns>The number of bytes read from the stream while parsing the frame information.</returns>
158264 private uint ReadFrame < TPixel > (
159265 BufferedReadStream stream ,
160266 ref Image < TPixel > ? image ,
0 commit comments