Browse Source

remove nullable disable from format gif

pull/2346/head
Stefan Nikolei 3 years ago
parent
commit
94fd43f71f
  1. 139
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  2. 24
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 2
      src/ImageSharp/Image.Decode.cs
  4. 4
      src/ImageSharp/Image.cs
  5. 4
      src/ImageSharp/ImageInfo.cs
  6. 4
      src/ImageSharp/Image{TPixel}.cs

139
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@ -24,15 +24,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// The currently loaded stream.
/// </summary>
private BufferedReadStream stream;
/// <summary>
/// The global color table.
/// </summary>
private IMemoryOwner<byte> globalColorTable;
private IMemoryOwner<byte>? globalColorTable;
/// <summary>
/// The area to restore.
@ -77,12 +72,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// The abstract metadata.
/// </summary>
private ImageMetadata metadata;
private ImageMetadata? metadata;
/// <summary>
/// The gif specific metadata.
/// </summary>
private GifMetadata gifMetadata;
private GifMetadata? gifMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
@ -108,8 +103,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel>
{
uint frameCount = 0;
Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null;
Image<TPixel>? image = null;
ImageFrame<TPixel>? previousFrame = null;
try
{
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
@ -125,7 +120,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
break;
}
this.ReadFrame(ref image, ref previousFrame);
this.ReadFrame(stream, ref image, ref previousFrame);
// Reset per-frame state.
this.imageDescriptor = default;
@ -136,16 +131,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
this.ReadGraphicalControlExtension(stream);
break;
case GifConstants.CommentLabel:
this.ReadComments();
this.ReadComments(stream);
break;
case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension();
this.ReadApplicationExtension(stream);
break;
case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder.
this.SkipBlock(stream); // Not supported by any known decoder.
break;
}
}
@ -187,23 +182,23 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{
if (nextFlag == GifConstants.ImageLabel)
{
this.ReadImageDescriptor();
this.ReadImageDescriptor(stream);
}
else if (nextFlag == GifConstants.ExtensionIntroducer)
{
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
this.SkipBlock(); // Skip graphic control extension block
this.SkipBlock(stream); // Skip graphic control extension block
break;
case GifConstants.CommentLabel:
this.ReadComments();
this.ReadComments(stream);
break;
case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension();
this.ReadApplicationExtension(stream);
break;
case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder.
this.SkipBlock(stream); // Not supported by any known decoder.
break;
}
}
@ -239,9 +234,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the graphic control extension.
/// </summary>
private void ReadGraphicalControlExtension()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadGraphicalControlExtension(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 6);
int bytesRead = stream.Read(this.buffer, 0, 6);
if (bytesRead != 6)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
@ -253,9 +249,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the image descriptor.
/// </summary>
private void ReadImageDescriptor()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadImageDescriptor(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 9);
int bytesRead = stream.Read(this.buffer, 0, 9);
if (bytesRead != 9)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
@ -271,9 +268,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the logical screen descriptor.
/// </summary>
private void ReadLogicalScreenDescriptor()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadLogicalScreenDescriptor(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 7);
int bytesRead = stream.Read(this.buffer, 0, 7);
if (bytesRead != 7)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
@ -286,84 +284,87 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads the application extension block parsing any animation or XMP information
/// if present.
/// </summary>
private void ReadApplicationExtension()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadApplicationExtension(BufferedReadStream stream)
{
int appLength = this.stream.ReadByte();
int appLength = stream.ReadByte();
// If the length is 11 then it's a valid extension and most likely
// a NETSCAPE, XMP or ANIMEXTS extension. We want the loop count from this.
long position = this.stream.Position;
long position = stream.Position;
if (appLength == GifConstants.ApplicationBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
if (isXmp && !this.skipMetadata)
{
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator);
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator);
if (extension.Data.Length > 0)
{
this.metadata.XmpProfile = new XmpProfile(extension.Data);
this.metadata!.XmpProfile = new XmpProfile(extension.Data);
}
else
{
// Reset the stream position and continue.
this.stream.Position = position;
this.SkipBlock(appLength);
stream.Position = position;
this.SkipBlock(stream, appLength);
}
return;
}
int subBlockSize = this.stream.ReadByte();
int subBlockSize = stream.ReadByte();
// TODO: There's also a NETSCAPE buffer extension.
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
this.stream.Skip(1); // Skip the terminator.
stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
stream.Skip(1); // Skip the terminator.
return;
}
// Could be something else not supported yet.
// Skip the subblock and terminator.
this.SkipBlock(subBlockSize);
this.SkipBlock(stream, subBlockSize);
return;
}
this.SkipBlock(appLength); // Not supported by any known decoder.
this.SkipBlock(stream, appLength); // Not supported by any known decoder.
}
/// <summary>
/// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// </summary>
private void SkipBlock(int blockSize = 0)
private void SkipBlock(BufferedReadStream stream, int blockSize = 0)
{
if (blockSize > 0)
{
this.stream.Skip(blockSize);
stream.Skip(blockSize);
}
int flag;
while ((flag = this.stream.ReadByte()) > 0)
while ((flag = stream.ReadByte()) > 0)
{
this.stream.Skip(flag);
stream.Skip(flag);
}
}
/// <summary>
/// Reads the gif comments.
/// </summary>
private void ReadComments()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadComments(BufferedReadStream stream)
{
int length;
var stringBuilder = new StringBuilder();
while ((length = this.stream.ReadByte()) != 0)
while ((length = stream.ReadByte()) != 0)
{
if (length > GifConstants.MaxCommentSubBlockLength)
{
@ -372,21 +373,21 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
if (this.skipMetadata)
{
this.stream.Seek(length, SeekOrigin.Current);
stream.Seek(length, SeekOrigin.Current);
continue;
}
using IMemoryOwner<byte> commentsBuffer = this.memoryAllocator.Allocate<byte>(length);
Span<byte> commentsSpan = commentsBuffer.GetSpan();
this.stream.Read(commentsSpan);
stream.Read(commentsSpan);
string commentPart = GifConstants.Encoding.GetString(commentsSpan);
stringBuilder.Append(commentPart);
}
if (stringBuilder.Length > 0)
{
this.gifMetadata.Comments.Add(stringBuilder.ToString());
this.gifMetadata!.Comments.Add(stringBuilder.ToString());
}
}
@ -394,15 +395,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads an individual gif frame.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame)
private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? image, ref ImageFrame<TPixel>? previousFrame)
where TPixel : unmanaged, IPixel<TPixel>
{
this.ReadImageDescriptor();
this.ReadImageDescriptor(stream);
IMemoryOwner<byte> localColorTable = null;
Buffer2D<byte> indices = null;
IMemoryOwner<byte>? localColorTable = null;
Buffer2D<byte>? indices = null;
try
{
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
@ -410,11 +412,11 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{
int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
this.stream.Read(localColorTable.GetSpan());
stream.Read(localColorTable.GetSpan());
}
indices = this.configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
this.ReadFrameIndices(indices);
this.ReadFrameIndices(stream, indices);
Span<byte> rawColorTable = default;
if (localColorTable != null)
@ -430,7 +432,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
// Skip any remaining blocks
this.SkipBlock();
this.SkipBlock(stream);
}
finally
{
@ -442,12 +444,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the frame indices marking the color to use for each pixel.
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="indices">The 2D pixel buffer to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(Buffer2D<byte> indices)
private void ReadFrameIndices(BufferedReadStream stream, Buffer2D<byte> indices)
{
int minCodeSize = this.stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream);
int minCodeSize = stream.ReadByte();
using LzwDecoder lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, stream);
lzwDecoder.DecodePixels(minCodeSize, indices);
}
@ -460,15 +463,15 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Buffer2D<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TPixel>? previousFrame, Buffer2D<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
where TPixel : unmanaged, IPixel<TPixel>
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
bool transFlag = this.graphicsControlExtension.TransparencyFlag;
ImageFrame<TPixel> prevFrame = null;
ImageFrame<TPixel> currentFrame = null;
ImageFrame<TPixel>? prevFrame = null;
ImageFrame<TPixel>? currentFrame = null;
ImageFrame<TPixel> imageFrame;
if (previousFrame is null)
@ -494,7 +497,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
prevFrame = previousFrame;
}
currentFrame = image.Frames.CreateFrame();
currentFrame = image!.Frames.CreateFrame();
this.SetFrameMetadata(currentFrame.Metadata, false);
@ -661,13 +664,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads the logical screen descriptor and global color table blocks
/// </summary>
/// <param name="stream">The stream containing image data. </param>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(gifMetadata))]
private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream)
{
this.stream = stream;
// Skip the identifier
this.stream.Skip(6);
this.ReadLogicalScreenDescriptor();
stream.Skip(6);
this.ReadLogicalScreenDescriptor(stream);
ImageMetadata meta = new();

24
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -93,7 +93,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
IndexedImageFrame<TPixel> quantized;
IndexedImageFrame<TPixel>? quantized;
using (IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration))
{
if (useGlobalTable)
@ -129,7 +129,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
this.WriteComments(gifMetadata, stream);
// Write application extensions.
XmpProfile xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
XmpProfile? xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile);
}
@ -141,7 +141,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
private void EncodeFrames<TPixel>(
Stream stream,
Image<TPixel> image,
IndexedImageFrame<TPixel> quantized,
IndexedImageFrame<TPixel>? quantized,
ReadOnlyMemory<TPixel> palette)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -152,8 +152,8 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
// Gather the metadata for this frame.
ImageFrame<TPixel> frame = image.Frames[i];
ImageFrameMetadata metadata = frame.Metadata;
bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata frameMetadata);
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata.ColorTableMode == GifColorTableMode.Local);
bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata? frameMetadata);
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata!.ColorTableMode == GifColorTableMode.Local);
if (!useLocal && !hasPaletteQuantizer && i > 0)
{
@ -164,7 +164,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
paletteQuantizer = new(this.configuration, this.quantizer.Options, palette);
}
this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized, ref paletteQuantizer);
this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized!, ref paletteQuantizer);
// Clean up for the next run.
quantized.Dispose();
@ -182,7 +182,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
ImageFrame<TPixel> frame,
int frameIndex,
bool useLocal,
GifFrameMetadata metadata,
GifFrameMetadata? metadata,
ref IndexedImageFrame<TPixel> quantized,
ref PaletteQuantizer<TPixel> paletteQuantizer)
where TPixel : unmanaged, IPixel<TPixel>
@ -193,7 +193,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
if (useLocal)
{
// Reassign using the current frame and details.
QuantizerOptions options = null;
QuantizerOptions? options = null;
int colorTableLength = metadata?.ColorTableLength ?? 0;
if (colorTableLength > 0)
{
@ -338,7 +338,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
/// <param name="frameCount">The frame count fo this image.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="xmpProfile">The XMP metadata profile. Null if profile is not to be written.</param>
private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile xmpProfile)
private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile? xmpProfile)
{
// Application Extension: Loop repeat count.
if (frameCount > 1 && repeatCount != 1)
@ -350,7 +350,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
// Application Extension: XMP Profile.
if (xmpProfile != null)
{
GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data);
GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data!);
this.WriteExtension(xmpExtension, stream);
}
}
@ -439,7 +439,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
private void WriteExtension<TGifExtension>(TGifExtension extension, Stream stream)
where TGifExtension : struct, IGifExtension
{
IMemoryOwner<byte> owner = null;
IMemoryOwner<byte>? owner = null;
Span<byte> extensionBuffer;
int extensionSize = extension.ContentLength;

2
src/ImageSharp/Image.Decode.cs

@ -91,7 +91,7 @@ public abstract partial class Image
ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
}
return format!;
return format;
}
/// <summary>

4
src/ImageSharp/Image.cs

@ -28,7 +28,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv
/// <param name="pixelType">The pixel type information.</param>
/// <param name="metadata">The image metadata.</param>
/// <param name="size">The size in px units.</param>
protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size)
protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata? metadata, Size size)
: base(pixelType, size, metadata)
=> this.configuration = configuration ?? Configuration.Default;
@ -43,7 +43,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv
internal Image(
Configuration configuration,
PixelTypeInfo pixelType,
ImageMetadata metadata,
ImageMetadata? metadata,
int width,
int height)
: this(configuration, pixelType, metadata, new Size(width, height))

4
src/ImageSharp/ImageInfo.cs

@ -18,7 +18,7 @@ public class ImageInfo
/// <param name="width">The width of the image in px units.</param>
/// <param name="height">The height of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata)
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata? metadata)
: this(pixelType, new(width, height), metadata)
{
}
@ -29,7 +29,7 @@ public class ImageInfo
/// <param name="pixelType">The pixel type information.</param>
/// <param name="size">The size of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata metadata)
public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata? metadata)
{
this.PixelType = pixelType;
this.Metadata = metadata ?? new ImageMetadata();

4
src/ImageSharp/Image{TPixel}.cs

@ -77,7 +77,7 @@ public sealed class Image<TPixel> : Image
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetadata metadata)
internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
@ -128,7 +128,7 @@ public sealed class Image<TPixel> : Image
int width,
int height,
TPixel backgroundColor,
ImageMetadata metadata)
ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor);

Loading…
Cancel
Save