1
1
// Copyright (c) Six Labors.
2
2
// Licensed under the Six Labors Split License.
3
- #nullable disable
4
3
5
4
using System . Buffers ;
5
+ using System . Diagnostics . CodeAnalysis ;
6
6
using System . Runtime . CompilerServices ;
7
7
using System . Runtime . InteropServices ;
8
8
using System . Text ;
@@ -24,15 +24,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
24
24
/// </summary>
25
25
private readonly byte [ ] buffer = new byte [ 16 ] ;
26
26
27
- /// <summary>
28
- /// The currently loaded stream.
29
- /// </summary>
30
- private BufferedReadStream stream ;
31
-
32
27
/// <summary>
33
28
/// The global color table.
34
29
/// </summary>
35
- private IMemoryOwner < byte > globalColorTable ;
30
+ private IMemoryOwner < byte > ? globalColorTable ;
36
31
37
32
/// <summary>
38
33
/// The area to restore.
@@ -77,12 +72,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
77
72
/// <summary>
78
73
/// The abstract metadata.
79
74
/// </summary>
80
- private ImageMetadata metadata ;
75
+ private ImageMetadata ? metadata ;
81
76
82
77
/// <summary>
83
78
/// The gif specific metadata.
84
79
/// </summary>
85
- private GifMetadata gifMetadata ;
80
+ private GifMetadata ? gifMetadata ;
86
81
87
82
/// <summary>
88
83
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
@@ -108,8 +103,8 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
108
103
where TPixel : unmanaged, IPixel < TPixel >
109
104
{
110
105
uint frameCount = 0 ;
111
- Image < TPixel > image = null ;
112
- ImageFrame < TPixel > previousFrame = null ;
106
+ Image < TPixel > ? image = null ;
107
+ ImageFrame < TPixel > ? previousFrame = null ;
113
108
try
114
109
{
115
110
this . ReadLogicalScreenDescriptorAndGlobalColorTable ( stream ) ;
@@ -125,7 +120,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
125
120
break ;
126
121
}
127
122
128
- this . ReadFrame ( ref image , ref previousFrame ) ;
123
+ this . ReadFrame ( stream , ref image , ref previousFrame ) ;
129
124
130
125
// Reset per-frame state.
131
126
this . imageDescriptor = default ;
@@ -136,16 +131,16 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
136
131
switch ( stream . ReadByte ( ) )
137
132
{
138
133
case GifConstants . GraphicControlLabel :
139
- this . ReadGraphicalControlExtension ( ) ;
134
+ this . ReadGraphicalControlExtension ( stream ) ;
140
135
break ;
141
136
case GifConstants . CommentLabel :
142
- this . ReadComments ( ) ;
137
+ this . ReadComments ( stream ) ;
143
138
break ;
144
139
case GifConstants . ApplicationExtensionLabel :
145
- this . ReadApplicationExtension ( ) ;
140
+ this . ReadApplicationExtension ( stream ) ;
146
141
break ;
147
142
case GifConstants . PlainTextLabel :
148
- this . SkipBlock ( ) ; // Not supported by any known decoder.
143
+ SkipBlock ( stream ) ; // Not supported by any known decoder.
149
144
break ;
150
145
}
151
146
}
@@ -187,23 +182,23 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
187
182
{
188
183
if ( nextFlag == GifConstants . ImageLabel )
189
184
{
190
- this . ReadImageDescriptor ( ) ;
185
+ this . ReadImageDescriptor ( stream ) ;
191
186
}
192
187
else if ( nextFlag == GifConstants . ExtensionIntroducer )
193
188
{
194
189
switch ( stream . ReadByte ( ) )
195
190
{
196
191
case GifConstants . GraphicControlLabel :
197
- this . SkipBlock ( ) ; // Skip graphic control extension block
192
+ SkipBlock ( stream ) ; // Skip graphic control extension block
198
193
break ;
199
194
case GifConstants . CommentLabel :
200
- this . ReadComments ( ) ;
195
+ this . ReadComments ( stream ) ;
201
196
break ;
202
197
case GifConstants . ApplicationExtensionLabel :
203
- this . ReadApplicationExtension ( ) ;
198
+ this . ReadApplicationExtension ( stream ) ;
204
199
break ;
205
200
case GifConstants . PlainTextLabel :
206
- this . SkipBlock ( ) ; // Not supported by any known decoder.
201
+ SkipBlock ( stream ) ; // Not supported by any known decoder.
207
202
break ;
208
203
}
209
204
}
@@ -239,9 +234,10 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
239
234
/// <summary>
240
235
/// Reads the graphic control extension.
241
236
/// </summary>
242
- private void ReadGraphicalControlExtension ( )
237
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
238
+ private void ReadGraphicalControlExtension ( BufferedReadStream stream )
243
239
{
244
- int bytesRead = this . stream . Read ( this . buffer , 0 , 6 ) ;
240
+ int bytesRead = stream . Read ( this . buffer , 0 , 6 ) ;
245
241
if ( bytesRead != 6 )
246
242
{
247
243
GifThrowHelper . ThrowInvalidImageContentException ( "Not enough data to read the graphic control extension" ) ;
@@ -253,9 +249,10 @@ private void ReadGraphicalControlExtension()
253
249
/// <summary>
254
250
/// Reads the image descriptor.
255
251
/// </summary>
256
- private void ReadImageDescriptor ( )
252
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
253
+ private void ReadImageDescriptor ( BufferedReadStream stream )
257
254
{
258
- int bytesRead = this . stream . Read ( this . buffer , 0 , 9 ) ;
255
+ int bytesRead = stream . Read ( this . buffer , 0 , 9 ) ;
259
256
if ( bytesRead != 9 )
260
257
{
261
258
GifThrowHelper . ThrowInvalidImageContentException ( "Not enough data to read the image descriptor" ) ;
@@ -271,9 +268,10 @@ private void ReadImageDescriptor()
271
268
/// <summary>
272
269
/// Reads the logical screen descriptor.
273
270
/// </summary>
274
- private void ReadLogicalScreenDescriptor ( )
271
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
272
+ private void ReadLogicalScreenDescriptor ( BufferedReadStream stream )
275
273
{
276
- int bytesRead = this . stream . Read ( this . buffer , 0 , 7 ) ;
274
+ int bytesRead = stream . Read ( this . buffer , 0 , 7 ) ;
277
275
if ( bytesRead != 7 )
278
276
{
279
277
GifThrowHelper . ThrowInvalidImageContentException ( "Not enough data to read the logical screen descriptor" ) ;
@@ -286,84 +284,87 @@ private void ReadLogicalScreenDescriptor()
286
284
/// Reads the application extension block parsing any animation or XMP information
287
285
/// if present.
288
286
/// </summary>
289
- private void ReadApplicationExtension ( )
287
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
288
+ private void ReadApplicationExtension ( BufferedReadStream stream )
290
289
{
291
- int appLength = this . stream . ReadByte ( ) ;
290
+ int appLength = stream . ReadByte ( ) ;
292
291
293
292
// If the length is 11 then it's a valid extension and most likely
294
293
// a NETSCAPE, XMP or ANIMEXTS extension. We want the loop count from this.
295
- long position = this . stream . Position ;
294
+ long position = stream . Position ;
296
295
if ( appLength == GifConstants . ApplicationBlockSize )
297
296
{
298
- this . stream . Read ( this . buffer , 0 , GifConstants . ApplicationBlockSize ) ;
297
+ stream . Read ( this . buffer , 0 , GifConstants . ApplicationBlockSize ) ;
299
298
bool isXmp = this . buffer . AsSpan ( ) . StartsWith ( GifConstants . XmpApplicationIdentificationBytes ) ;
300
299
if ( isXmp && ! this . skipMetadata )
301
300
{
302
- GifXmpApplicationExtension extension = GifXmpApplicationExtension . Read ( this . stream , this . memoryAllocator ) ;
301
+ GifXmpApplicationExtension extension = GifXmpApplicationExtension . Read ( stream , this . memoryAllocator ) ;
303
302
if ( extension . Data . Length > 0 )
304
303
{
305
- this . metadata . XmpProfile = new XmpProfile ( extension . Data ) ;
304
+ this . metadata ! . XmpProfile = new XmpProfile ( extension . Data ) ;
306
305
}
307
306
else
308
307
{
309
308
// Reset the stream position and continue.
310
- this . stream . Position = position ;
311
- this . SkipBlock ( appLength ) ;
309
+ stream . Position = position ;
310
+ SkipBlock ( stream , appLength ) ;
312
311
}
313
312
314
313
return ;
315
314
}
316
315
317
- int subBlockSize = this . stream . ReadByte ( ) ;
316
+ int subBlockSize = stream . ReadByte ( ) ;
318
317
319
318
// TODO: There's also a NETSCAPE buffer extension.
320
319
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
321
320
if ( subBlockSize == GifConstants . NetscapeLoopingSubBlockSize )
322
321
{
323
- this . stream . Read ( this . buffer , 0 , GifConstants . NetscapeLoopingSubBlockSize ) ;
324
- this . gifMetadata . RepeatCount = GifNetscapeLoopingApplicationExtension . Parse ( this . buffer . AsSpan ( 1 ) ) . RepeatCount ;
325
- this . stream . Skip ( 1 ) ; // Skip the terminator.
322
+ stream . Read ( this . buffer , 0 , GifConstants . NetscapeLoopingSubBlockSize ) ;
323
+ this . gifMetadata ! . RepeatCount = GifNetscapeLoopingApplicationExtension . Parse ( this . buffer . AsSpan ( 1 ) ) . RepeatCount ;
324
+ stream . Skip ( 1 ) ; // Skip the terminator.
326
325
return ;
327
326
}
328
327
329
328
// Could be something else not supported yet.
330
329
// Skip the subblock and terminator.
331
- this . SkipBlock ( subBlockSize ) ;
330
+ SkipBlock ( stream , subBlockSize ) ;
332
331
333
332
return ;
334
333
}
335
334
336
- this . SkipBlock ( appLength ) ; // Not supported by any known decoder.
335
+ SkipBlock ( stream , appLength ) ; // Not supported by any known decoder.
337
336
}
338
337
339
338
/// <summary>
340
339
/// Skips over a block or reads its terminator.
341
- /// <param name="blockSize">The length of the block to skip.</param>
342
340
/// </summary>
343
- private void SkipBlock ( int blockSize = 0 )
341
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
342
+ /// <param name="blockSize">The length of the block to skip.</param>
343
+ private static void SkipBlock ( BufferedReadStream stream , int blockSize = 0 )
344
344
{
345
345
if ( blockSize > 0 )
346
346
{
347
- this . stream . Skip ( blockSize ) ;
347
+ stream . Skip ( blockSize ) ;
348
348
}
349
349
350
350
int flag ;
351
351
352
- while ( ( flag = this . stream . ReadByte ( ) ) > 0 )
352
+ while ( ( flag = stream . ReadByte ( ) ) > 0 )
353
353
{
354
- this . stream . Skip ( flag ) ;
354
+ stream . Skip ( flag ) ;
355
355
}
356
356
}
357
357
358
358
/// <summary>
359
359
/// Reads the gif comments.
360
360
/// </summary>
361
- private void ReadComments ( )
361
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
362
+ private void ReadComments ( BufferedReadStream stream )
362
363
{
363
364
int length ;
364
365
365
- var stringBuilder = new StringBuilder ( ) ;
366
- while ( ( length = this . stream . ReadByte ( ) ) != 0 )
366
+ StringBuilder stringBuilder = new ( ) ;
367
+ while ( ( length = stream . ReadByte ( ) ) != 0 )
367
368
{
368
369
if ( length > GifConstants . MaxCommentSubBlockLength )
369
370
{
@@ -372,49 +373,50 @@ private void ReadComments()
372
373
373
374
if ( this . skipMetadata )
374
375
{
375
- this . stream . Seek ( length , SeekOrigin . Current ) ;
376
+ stream . Seek ( length , SeekOrigin . Current ) ;
376
377
continue ;
377
378
}
378
379
379
380
using IMemoryOwner < byte > commentsBuffer = this . memoryAllocator . Allocate < byte > ( length ) ;
380
381
Span < byte > commentsSpan = commentsBuffer . GetSpan ( ) ;
381
382
382
- this . stream . Read ( commentsSpan ) ;
383
+ stream . Read ( commentsSpan ) ;
383
384
string commentPart = GifConstants . Encoding . GetString ( commentsSpan ) ;
384
385
stringBuilder . Append ( commentPart ) ;
385
386
}
386
387
387
388
if ( stringBuilder . Length > 0 )
388
389
{
389
- this . gifMetadata . Comments . Add ( stringBuilder . ToString ( ) ) ;
390
+ this . gifMetadata ! . Comments . Add ( stringBuilder . ToString ( ) ) ;
390
391
}
391
392
}
392
393
393
394
/// <summary>
394
395
/// Reads an individual gif frame.
395
396
/// </summary>
396
397
/// <typeparam name="TPixel">The pixel format.</typeparam>
398
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
397
399
/// <param name="image">The image to decode the information to.</param>
398
400
/// <param name="previousFrame">The previous frame.</param>
399
- private void ReadFrame < TPixel > ( ref Image < TPixel > image , ref ImageFrame < TPixel > previousFrame )
401
+ private void ReadFrame < TPixel > ( BufferedReadStream stream , ref Image < TPixel > ? image , ref ImageFrame < TPixel > ? previousFrame )
400
402
where TPixel : unmanaged, IPixel < TPixel >
401
403
{
402
- this . ReadImageDescriptor ( ) ;
404
+ this . ReadImageDescriptor ( stream ) ;
403
405
404
- IMemoryOwner < byte > localColorTable = null ;
405
- Buffer2D < byte > indices = null ;
406
+ IMemoryOwner < byte > ? localColorTable = null ;
407
+ Buffer2D < byte > ? indices = null ;
406
408
try
407
409
{
408
410
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
409
411
if ( this . imageDescriptor . LocalColorTableFlag )
410
412
{
411
413
int length = this . imageDescriptor . LocalColorTableSize * 3 ;
412
414
localColorTable = this . configuration . MemoryAllocator . Allocate < byte > ( length , AllocationOptions . Clean ) ;
413
- this . stream . Read ( localColorTable . GetSpan ( ) ) ;
415
+ stream . Read ( localColorTable . GetSpan ( ) ) ;
414
416
}
415
417
416
418
indices = this . configuration . MemoryAllocator . Allocate2D < byte > ( this . imageDescriptor . Width , this . imageDescriptor . Height , AllocationOptions . Clean ) ;
417
- this . ReadFrameIndices ( indices ) ;
419
+ this . ReadFrameIndices ( stream , indices ) ;
418
420
419
421
Span < byte > rawColorTable = default ;
420
422
if ( localColorTable != null )
@@ -430,7 +432,7 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
430
432
this . ReadFrameColors ( ref image , ref previousFrame , indices , colorTable , this . imageDescriptor ) ;
431
433
432
434
// Skip any remaining blocks
433
- this . SkipBlock ( ) ;
435
+ SkipBlock ( stream ) ;
434
436
}
435
437
finally
436
438
{
@@ -442,12 +444,13 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
442
444
/// <summary>
443
445
/// Reads the frame indices marking the color to use for each pixel.
444
446
/// </summary>
447
+ /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
445
448
/// <param name="indices">The 2D pixel buffer to write to.</param>
446
449
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
447
- private void ReadFrameIndices ( Buffer2D < byte > indices )
450
+ private void ReadFrameIndices ( BufferedReadStream stream , Buffer2D < byte > indices )
448
451
{
449
- int minCodeSize = this . stream . ReadByte ( ) ;
450
- using var lzwDecoder = new LzwDecoder ( this . configuration . MemoryAllocator , this . stream ) ;
452
+ int minCodeSize = stream . ReadByte ( ) ;
453
+ using LzwDecoder lzwDecoder = new ( this . configuration . MemoryAllocator , stream ) ;
451
454
lzwDecoder . DecodePixels ( minCodeSize , indices ) ;
452
455
}
453
456
@@ -460,15 +463,15 @@ private void ReadFrameIndices(Buffer2D<byte> indices)
460
463
/// <param name="indices">The indexed pixels.</param>
461
464
/// <param name="colorTable">The color table containing the available colors.</param>
462
465
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
463
- private void ReadFrameColors < TPixel > ( ref Image < TPixel > image , ref ImageFrame < TPixel > previousFrame , Buffer2D < byte > indices , ReadOnlySpan < Rgb24 > colorTable , in GifImageDescriptor descriptor )
466
+ private void ReadFrameColors < TPixel > ( ref Image < TPixel > ? image , ref ImageFrame < TPixel > ? previousFrame , Buffer2D < byte > indices , ReadOnlySpan < Rgb24 > colorTable , in GifImageDescriptor descriptor )
464
467
where TPixel : unmanaged, IPixel < TPixel >
465
468
{
466
469
int imageWidth = this . logicalScreenDescriptor . Width ;
467
470
int imageHeight = this . logicalScreenDescriptor . Height ;
468
471
bool transFlag = this . graphicsControlExtension . TransparencyFlag ;
469
472
470
- ImageFrame < TPixel > prevFrame = null ;
471
- ImageFrame < TPixel > currentFrame = null ;
473
+ ImageFrame < TPixel > ? prevFrame = null ;
474
+ ImageFrame < TPixel > ? currentFrame = null ;
472
475
ImageFrame < TPixel > imageFrame ;
473
476
474
477
if ( previousFrame is null )
@@ -494,7 +497,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
494
497
prevFrame = previousFrame ;
495
498
}
496
499
497
- currentFrame = image . Frames . CreateFrame ( ) ;
500
+ currentFrame = image ! . Frames . CreateFrame ( ) ;
498
501
499
502
this . SetFrameMetadata ( currentFrame . Metadata , false ) ;
500
503
@@ -661,13 +664,13 @@ private void SetFrameMetadata(ImageFrameMetadata meta, bool isRoot)
661
664
/// Reads the logical screen descriptor and global color table blocks
662
665
/// </summary>
663
666
/// <param name="stream">The stream containing image data. </param>
667
+ [ MemberNotNull ( nameof ( metadata ) ) ]
668
+ [ MemberNotNull ( nameof ( gifMetadata ) ) ]
664
669
private void ReadLogicalScreenDescriptorAndGlobalColorTable ( BufferedReadStream stream )
665
670
{
666
- this . stream = stream ;
667
-
668
671
// Skip the identifier
669
- this . stream . Skip ( 6 ) ;
670
- this . ReadLogicalScreenDescriptor ( ) ;
672
+ stream . Skip ( 6 ) ;
673
+ this . ReadLogicalScreenDescriptor ( stream ) ;
671
674
672
675
ImageMetadata meta = new ( ) ;
673
676
0 commit comments