@@ -81,16 +81,29 @@ public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration confi
81
81
/// <param name="width">The width of the image.</param>
82
82
/// <param name="height">The height of the image.</param>
83
83
/// <param name="completeDataSize">The size of the image data in bytes.</param>
84
- public Image < TPixel > Decode < TPixel > ( BufferedReadStream stream , WebpFeatures features , uint width , uint height , uint completeDataSize )
84
+ public Image < TPixel > Decode < TPixel > (
85
+ BufferedReadStream stream ,
86
+ WebpFeatures features ,
87
+ uint width ,
88
+ uint height ,
89
+ uint completeDataSize )
85
90
where TPixel : unmanaged, IPixel < TPixel >
86
91
{
87
92
Image < TPixel > ? image = null ;
88
93
ImageFrame < TPixel > ? previousFrame = null ;
94
+ WebpFrameData ? prevFrameData = null ;
89
95
90
96
this . metadata = new ImageMetadata ( ) ;
91
97
this . webpMetadata = this . metadata . GetWebpMetadata ( ) ;
92
98
this . webpMetadata . RepeatCount = features . AnimationLoopCount ;
93
99
100
+ Color backgroundColor = this . backgroundColorHandling == BackgroundColorHandling . Ignore
101
+ ? Color . Transparent
102
+ : features . AnimationBackgroundColor ! . Value ;
103
+
104
+ this . webpMetadata . BackgroundColor = backgroundColor ;
105
+ TPixel backgroundPixel = backgroundColor . ToPixel < TPixel > ( ) ;
106
+
94
107
Span < byte > buffer = stackalloc byte [ 4 ] ;
95
108
uint frameCount = 0 ;
96
109
int remainingBytes = ( int ) completeDataSize ;
@@ -101,10 +114,15 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, WebpFeatures feat
101
114
switch ( chunkType )
102
115
{
103
116
case WebpChunkType . FrameData :
104
- Color backgroundColor = this . backgroundColorHandling == BackgroundColorHandling . Ignore
105
- ? new Color ( new Bgra32 ( 0 , 0 , 0 , 0 ) )
106
- : features . AnimationBackgroundColor ! . Value ;
107
- uint dataSize = this . ReadFrame ( stream , ref image , ref previousFrame , width , height , backgroundColor ) ;
117
+ uint dataSize = this . ReadFrame (
118
+ stream ,
119
+ ref image ,
120
+ ref previousFrame ,
121
+ ref prevFrameData ,
122
+ width ,
123
+ height ,
124
+ backgroundPixel ) ;
125
+
108
126
remainingBytes -= ( int ) dataSize ;
109
127
break ;
110
128
case WebpChunkType . Xmp :
@@ -132,10 +150,18 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, WebpFeatures feat
132
150
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
133
151
/// <param name="image">The image to decode the information to.</param>
134
152
/// <param name="previousFrame">The previous frame.</param>
153
+ /// <param name="prevFrameData">The previous frame data.</param>
135
154
/// <param name="width">The width of the image.</param>
136
155
/// <param name="height">The height of the image.</param>
137
156
/// <param name="backgroundColor">The default background color of the canvas in.</param>
138
- private uint ReadFrame < TPixel > ( BufferedReadStream stream , ref Image < TPixel > ? image , ref ImageFrame < TPixel > ? previousFrame , uint width , uint height , Color backgroundColor )
157
+ private uint ReadFrame < TPixel > (
158
+ BufferedReadStream stream ,
159
+ ref Image < TPixel > ? image ,
160
+ ref ImageFrame < TPixel > ? previousFrame ,
161
+ ref WebpFrameData ? prevFrameData ,
162
+ uint width ,
163
+ uint height ,
164
+ TPixel backgroundColor )
139
165
where TPixel : unmanaged, IPixel < TPixel >
140
166
{
141
167
WebpFrameData frameData = WebpFrameData . Parse ( stream ) ;
@@ -174,40 +200,54 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
174
200
break ;
175
201
}
176
202
177
- ImageFrame < TPixel > ? currentFrame = null ;
178
- ImageFrame < TPixel > imageFrame ;
203
+ bool isKeyFrame = false ;
204
+ ImageFrame < TPixel > currentFrame ;
179
205
if ( previousFrame is null )
180
206
{
181
- image = new Image < TPixel > ( this . configuration , ( int ) width , ( int ) height , backgroundColor . ToPixel < TPixel > ( ) , this . metadata ) ;
207
+ image = new Image < TPixel > ( this . configuration , ( int ) width , ( int ) height , backgroundColor , this . metadata ) ;
182
208
183
209
SetFrameMetadata ( image . Frames . RootFrame . Metadata , frameData ) ;
184
210
185
- imageFrame = image . Frames . RootFrame ;
211
+ currentFrame = image . Frames . RootFrame ;
212
+ isKeyFrame = true ;
186
213
}
187
214
else
188
215
{
189
- currentFrame = image ! . Frames . AddFrame ( previousFrame ) ; // This clones the frame and adds it the collection.
216
+ // If the frame is a key frame we do not need to clone the frame or clear it.
217
+ isKeyFrame = prevFrameData ? . DisposalMethod is WebpDisposalMethod . RestoreToBackground
218
+ && this . restoreArea == image ! . Bounds ;
190
219
191
- SetFrameMetadata ( currentFrame . Metadata , frameData ) ;
220
+ if ( isKeyFrame )
221
+ {
222
+ currentFrame = image ! . Frames . CreateFrame ( backgroundColor ) ;
223
+ }
224
+ else
225
+ {
226
+ // This clones the frame and adds it the collection.
227
+ currentFrame = image ! . Frames . AddFrame ( previousFrame ) ;
228
+ if ( prevFrameData ? . DisposalMethod is WebpDisposalMethod . RestoreToBackground )
229
+ {
230
+ this . RestoreToBackground ( currentFrame , backgroundColor ) ;
231
+ }
232
+ }
192
233
193
- imageFrame = currentFrame ;
234
+ SetFrameMetadata ( currentFrame . Metadata , frameData ) ;
194
235
}
195
236
196
- Rectangle regionRectangle = frameData . Bounds ;
237
+ Rectangle interest = frameData . Bounds ;
238
+ bool blend = previousFrame != null && frameData . BlendingMethod == WebpBlendMethod . Over ;
239
+ using Buffer2D < TPixel > pixelData = this . DecodeImageFrameData < TPixel > ( frameData , webpInfo ) ;
240
+ DrawDecodedImageFrameOnCanvas ( pixelData , currentFrame , interest , blend ) ;
241
+
242
+ webpInfo ? . Dispose ( ) ;
243
+ previousFrame = currentFrame ;
244
+ prevFrameData = frameData ;
197
245
198
246
if ( frameData . DisposalMethod is WebpDisposalMethod . RestoreToBackground )
199
247
{
200
- this . RestoreToBackground ( imageFrame , backgroundColor ) ;
248
+ this . restoreArea = interest ;
201
249
}
202
250
203
- using Buffer2D < TPixel > decodedImageFrame = this . DecodeImageFrameData < TPixel > ( frameData , webpInfo ) ;
204
-
205
- bool blend = previousFrame != null && frameData . BlendingMethod == WebpBlendMethod . Over ;
206
- DrawDecodedImageFrameOnCanvas ( decodedImageFrame , imageFrame , regionRectangle , blend ) ;
207
-
208
- previousFrame = currentFrame ?? image . Frames . RootFrame ;
209
- this . restoreArea = regionRectangle ;
210
-
211
251
return ( uint ) ( stream . Position - streamStartPosition ) ;
212
252
}
213
253
@@ -257,31 +297,27 @@ private Buffer2D<TPixel> DecodeImageFrameData<TPixel>(WebpFrameData frameData, W
257
297
258
298
try
259
299
{
260
- Buffer2D < TPixel > pixelBufferDecoded = decodedFrame . PixelBuffer ;
300
+ Buffer2D < TPixel > decodeBuffer = decodedFrame . PixelBuffer ;
261
301
if ( webpInfo . IsLossless )
262
302
{
263
303
WebpLosslessDecoder losslessDecoder =
264
304
new ( webpInfo . Vp8LBitReader , this . memoryAllocator , this . configuration ) ;
265
- losslessDecoder . Decode ( pixelBufferDecoded , ( int ) webpInfo . Width , ( int ) webpInfo . Height ) ;
305
+ losslessDecoder . Decode ( decodeBuffer , ( int ) frameData . Width , ( int ) frameData . Height ) ;
266
306
}
267
307
else
268
308
{
269
309
WebpLossyDecoder lossyDecoder =
270
310
new ( webpInfo . Vp8BitReader , this . memoryAllocator , this . configuration ) ;
271
- lossyDecoder . Decode ( pixelBufferDecoded , ( int ) webpInfo . Width , ( int ) webpInfo . Height , webpInfo , this . alphaData ) ;
311
+ lossyDecoder . Decode ( decodeBuffer , ( int ) frameData . Width , ( int ) frameData . Height , webpInfo , this . alphaData ) ;
272
312
}
273
313
274
- return pixelBufferDecoded ;
314
+ return decodeBuffer ;
275
315
}
276
316
catch
277
317
{
278
318
decodedFrame ? . Dispose ( ) ;
279
319
throw ;
280
320
}
281
- finally
282
- {
283
- webpInfo . Dispose ( ) ;
284
- }
285
321
}
286
322
287
323
/// <summary>
@@ -290,17 +326,17 @@ private Buffer2D<TPixel> DecodeImageFrameData<TPixel>(WebpFrameData frameData, W
290
326
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
291
327
/// <param name="decodedImageFrame">The decoded image.</param>
292
328
/// <param name="imageFrame">The image frame to draw into.</param>
293
- /// <param name="restoreArea ">The area of the frame.</param>
329
+ /// <param name="interest ">The area of the frame to draw to .</param>
294
330
/// <param name="blend">Whether to blend the decoded frame data onto the target frame.</param>
295
331
private static void DrawDecodedImageFrameOnCanvas < TPixel > (
296
332
Buffer2D < TPixel > decodedImageFrame ,
297
333
ImageFrame < TPixel > imageFrame ,
298
- Rectangle restoreArea ,
334
+ Rectangle interest ,
299
335
bool blend )
300
336
where TPixel : unmanaged, IPixel < TPixel >
301
337
{
302
338
// Trim the destination frame to match the restore area. The source frame is already trimmed.
303
- Buffer2DRegion < TPixel > imageFramePixels = imageFrame . PixelBuffer . GetRegion ( restoreArea ) ;
339
+ Buffer2DRegion < TPixel > imageFramePixels = imageFrame . PixelBuffer . GetRegion ( interest ) ;
304
340
if ( blend )
305
341
{
306
342
// The destination frame has already been prepopulated with the pixel data from the previous frame
@@ -309,21 +345,21 @@ private static void DrawDecodedImageFrameOnCanvas<TPixel>(
309
345
PixelBlender < TPixel > blender =
310
346
PixelOperations < TPixel > . Instance . GetPixelBlender ( PixelColorBlendingMode . Normal , PixelAlphaCompositionMode . SrcOver ) ;
311
347
312
- for ( int y = 0 ; y < restoreArea . Height ; y ++ )
348
+ for ( int y = 0 ; y < interest . Height ; y ++ )
313
349
{
314
350
Span < TPixel > framePixelRow = imageFramePixels . DangerousGetRowSpan ( y ) ;
315
- Span < TPixel > decodedPixelRow = decodedImageFrame . DangerousGetRowSpan ( y ) [ .. restoreArea . Width ] ;
351
+ Span < TPixel > decodedPixelRow = decodedImageFrame . DangerousGetRowSpan ( y ) ;
316
352
317
353
blender . Blend < TPixel > ( imageFrame . Configuration , framePixelRow , framePixelRow , decodedPixelRow , 1f ) ;
318
354
}
319
355
320
356
return ;
321
357
}
322
358
323
- for ( int y = 0 ; y < restoreArea . Height ; y ++ )
359
+ for ( int y = 0 ; y < interest . Height ; y ++ )
324
360
{
325
361
Span < TPixel > framePixelRow = imageFramePixels . DangerousGetRowSpan ( y ) ;
326
- Span < TPixel > decodedPixelRow = decodedImageFrame . DangerousGetRowSpan ( y ) [ .. restoreArea . Width ] ;
362
+ Span < TPixel > decodedPixelRow = decodedImageFrame . DangerousGetRowSpan ( y ) ;
327
363
decodedPixelRow . CopyTo ( framePixelRow ) ;
328
364
}
329
365
}
@@ -335,7 +371,7 @@ private static void DrawDecodedImageFrameOnCanvas<TPixel>(
335
371
/// <typeparam name="TPixel">The pixel format.</typeparam>
336
372
/// <param name="imageFrame">The image frame.</param>
337
373
/// <param name="backgroundColor">Color of the background.</param>
338
- private void RestoreToBackground < TPixel > ( ImageFrame < TPixel > imageFrame , Color backgroundColor )
374
+ private void RestoreToBackground < TPixel > ( ImageFrame < TPixel > imageFrame , TPixel backgroundColor )
339
375
where TPixel : unmanaged, IPixel < TPixel >
340
376
{
341
377
if ( ! this . restoreArea . HasValue )
@@ -345,8 +381,7 @@ private void RestoreToBackground<TPixel>(ImageFrame<TPixel> imageFrame, Color ba
345
381
346
382
Rectangle interest = Rectangle . Intersect ( imageFrame . Bounds ( ) , this . restoreArea . Value ) ;
347
383
Buffer2DRegion < TPixel > pixelRegion = imageFrame . PixelBuffer . GetRegion ( interest ) ;
348
- TPixel backgroundPixel = backgroundColor . ToPixel < TPixel > ( ) ;
349
- pixelRegion . Fill ( backgroundPixel ) ;
384
+ pixelRegion . Fill ( backgroundColor ) ;
350
385
}
351
386
352
387
/// <inheritdoc/>
0 commit comments