Browse Source

Refactor Bmp and Gif

pull/2180/head
James Jackson-South 4 years ago
parent
commit
dfe04e36e3
  1. 40
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  2. 30
      src/ImageSharp/Formats/Bmp/BmpDecoder2.cs
  3. 41
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  4. 2
      src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs
  5. 16
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  6. 6
      src/ImageSharp/Formats/DecoderOptions.cs
  7. 35
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  8. 73
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  9. 14
      src/ImageSharp/Formats/Gif/GifDecoderOptions.cs
  10. 10
      src/ImageSharp/Formats/IImageDecoderInternals{T}.cs
  11. 17
      src/ImageSharp/Formats/ImageDecoderUtilities.cs

40
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO; using System.IO;
@ -10,43 +10,31 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream. /// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary> /// </summary>
/// <remarks> public class BmpDecoder : ImageDecoder<BmpDecoderOptions>
/// Does not support the following formats at the moment:
/// <list type="bullet">
/// <item>JPG</item>
/// <item>PNG</item>
/// <item>Some OS/2 specific subtypes like: Bitmap Array, Color Icon, Color Pointer, Icon, Pointer.</item>
/// </list>
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
/// </remarks>
public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector
{ {
/// <summary>
/// Gets or sets a value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps.
/// </summary>
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black;
/// <inheritdoc/> /// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> DecodeSpecialized<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
var decoder = new BmpDecoderCore(configuration, this); BmpDecoderCore decoder = new(options);
return decoder.Decode<TPixel>(configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<BmpDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
} }
/// <inheritdoc /> /// <inheritdoc/>
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(configuration, stream, cancellationToken); => this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/> /// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); return new BmpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
} }
} }
} }

30
src/ImageSharp/Formats/Bmp/BmpDecoder2.cs

@ -1,30 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary>
public class BmpDecoder2 : ImageDecoder<BmpDecoderOptions>
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> throw new NotImplementedException();
}
}

41
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <remarks> /// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/> /// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks> /// </remarks>
internal sealed class BmpDecoderCore : IImageDecoderInternals internal sealed class BmpDecoderCore : IImageDecoderInternals<BmpDecoderOptions>
{ {
/// <summary> /// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps. /// The default mask for the red part of the color for 16 bit rgb bitmaps.
@ -90,33 +90,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private BmpInfoHeader infoHeader; private BmpInfoHeader infoHeader;
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// The global configuration.
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration;
/// <summary> /// <summary>
/// The bitmap decoder options. /// Used for allocating memory during processing operations.
/// </summary> /// </summary>
private readonly IBmpDecoderOptions options; private readonly MemoryAllocator memoryAllocator;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class. /// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) public BmpDecoderCore(BmpDecoderOptions options)
{ {
this.Configuration = configuration; this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
this.options = options; this.Options = options;
} }
/// <inheritdoc /> /// <inheritdoc />
public Configuration Configuration { get; } public BmpDecoderOptions Options { get; }
/// <summary> /// <inheritdoc />
/// Gets the dimensions of the image.
/// </summary>
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);
/// <inheritdoc /> /// <inheritdoc />
@ -128,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
image = new Image<TPixel>(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
@ -325,7 +322,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
byte colorIdx = bufferRow[x]; byte colorIdx = bufferRow[x];
if (undefinedPixelsSpan[rowStartIdx + x]) if (undefinedPixelsSpan[rowStartIdx + x])
{ {
switch (this.options.RleSkippedPixelHandling) switch (this.Options.RleSkippedPixelHandling)
{ {
case RleSkippedPixelHandling.FirstColorOfPalette: case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4])); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4]));
@ -397,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int idx = rowStartIdx + (x * 3); int idx = rowStartIdx + (x * 3);
if (undefinedPixelsSpan[yMulWidth + x]) if (undefinedPixelsSpan[yMulWidth + x])
{ {
switch (this.options.RleSkippedPixelHandling) switch (this.Options.RleSkippedPixelHandling)
{ {
case RleSkippedPixelHandling.FirstColorOfPalette: case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
@ -943,7 +940,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgr24Bytes( PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.Configuration, this.configuration,
rowSpan, rowSpan,
pixelSpan, pixelSpan,
width); width);
@ -971,7 +968,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes( PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.Configuration, this.configuration,
rowSpan, rowSpan,
pixelSpan, pixelSpan,
width); width);
@ -1006,7 +1003,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(rowSpan); this.stream.Read(rowSpan);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes( PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.Configuration, this.configuration,
rowSpan, rowSpan,
bgraRowSpan, bgraRowSpan,
width); width);
@ -1042,7 +1039,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes( PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.Configuration, this.configuration,
rowSpan, rowSpan,
pixelSpan, pixelSpan,
width); width);
@ -1056,7 +1053,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
this.stream.Read(rowSpan); this.stream.Read(rowSpan);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes( PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.Configuration, this.configuration,
rowSpan, rowSpan,
bgraRowSpan, bgraRowSpan,
width); width);

2
src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats.Bmp namespace SixLabors.ImageSharp.Formats.Bmp
{ {
/// <summary> /// <summary>
/// Image decoder options for decoding Windows bitmap streams. /// Configuration options for decoding Windows Bitmap images.
/// </summary> /// </summary>
public sealed class BmpDecoderOptions : ISpecializedDecoderOptions public sealed class BmpDecoderOptions : ISpecializedDecoderOptions
{ {

16
src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs

@ -1,16 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Image decoder options for decoding Windows bitmap streams.
/// </summary>
internal interface IBmpDecoderOptions
{
/// <summary>
/// Gets the value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps.
/// </summary>
RleSkippedPixelHandling RleSkippedPixelHandling { get; }
}
}

6
src/ImageSharp/Formats/DecoderOptions.cs

@ -24,10 +24,8 @@ namespace SixLabors.ImageSharp.Formats
public bool SkipMetadata { get; set; } = false; public bool SkipMetadata { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets the number of image frames to decode. /// Gets or sets the maximum number of image frames to decode, inclusive.
/// Leave <see langword="null"/> to decode all frames.
/// </summary> /// </summary>
// supported decoders may handle this internally but we will fallback to removeing additional frames after decode. public int MaxFrames { get; set; } = int.MaxValue;
public int? MaxFrames { get; set; } = null;
} }
} }

35
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -3,7 +3,6 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Gif namespace SixLabors.ImageSharp.Formats.Gif
@ -11,37 +10,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector public sealed class GifDecoder : ImageDecoder<GifDecoderOptions>
{ {
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Gets or sets the decoding mode for multi-frame images
/// </summary>
public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All;
/// <inheritdoc/> /// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> DecodeSpecialized<TPixel>(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
var decoder = new GifDecoderCore(configuration, this); GifDecoderCore decoder = new(options);
return decoder.Decode<TPixel>(configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<GifDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
} }
/// <inheritdoc /> /// <inheritdoc/>
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(configuration, stream, cancellationToken); => this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/> /// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this); return new GifDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
return decoder.Identify(configuration, stream, cancellationToken);
} }
} }
} }

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
internal sealed class GifDecoderCore : IImageDecoderInternals internal sealed class GifDecoderCore : IImageDecoderInternals<GifDecoderOptions>
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// The temp buffer used to reduce allocations.
@ -56,6 +56,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
private GifImageDescriptor imageDescriptor; private GifImageDescriptor imageDescriptor;
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The maximum number of frames to decode.
/// </summary>
private readonly int maxFrames;
/// <summary>
/// Whether to skip metadata during decode.
/// </summary>
private readonly bool skipMetadata;
/// <summary> /// <summary>
/// The abstract metadata. /// The abstract metadata.
/// </summary> /// </summary>
@ -69,39 +89,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class. /// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) public GifDecoderCore(GifDecoderOptions options)
{ {
this.IgnoreMetadata = options.IgnoreMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.DecodingMode = options.DecodingMode; this.configuration = options.GeneralOptions.Configuration;
this.Configuration = configuration ?? Configuration.Default; this.memoryAllocator = this.configuration.MemoryAllocator;
this.maxFrames = options.GeneralOptions.MaxFrames;
} }
/// <inheritdoc /> /// <inheritdoc />
public Configuration Configuration { get; } public GifDecoderOptions Options { get; }
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; internal set; }
/// <summary> /// <inheritdoc />
/// Gets the decoding mode for multi-frame images.
/// </summary>
public FrameDecodingMode DecodingMode { get; }
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;
/// <inheritdoc /> /// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int frameCount = 0;
Image<TPixel> image = null; Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null; ImageFrame<TPixel> previousFrame = null;
try try
@ -114,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
if (nextFlag == GifConstants.ImageLabel) if (nextFlag == GifConstants.ImageLabel)
{ {
if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First) if (previousFrame != null && frameCount++ <= this.maxFrames)
{ {
break; break;
} }
@ -277,9 +284,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize); this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes); bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
if (isXmp && !this.IgnoreMetadata) if (isXmp && !this.skipMetadata)
{ {
var extension = GifXmpApplicationExtension.Read(this.stream, this.MemoryAllocator); var extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator);
if (extension.Data.Length > 0) if (extension.Data.Length > 0)
{ {
this.metadata.XmpProfile = new XmpProfile(extension.Data); this.metadata.XmpProfile = new XmpProfile(extension.Data);
@ -346,13 +353,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block"); GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block");
} }
if (this.IgnoreMetadata) if (this.skipMetadata)
{ {
this.stream.Seek(length, SeekOrigin.Current); this.stream.Seek(length, SeekOrigin.Current);
continue; continue;
} }
using IMemoryOwner<byte> commentsBuffer = this.MemoryAllocator.Allocate<byte>(length); using IMemoryOwner<byte> commentsBuffer = this.memoryAllocator.Allocate<byte>(length);
Span<byte> commentsSpan = commentsBuffer.GetSpan(); Span<byte> commentsSpan = commentsBuffer.GetSpan();
this.stream.Read(commentsSpan); this.stream.Read(commentsSpan);
@ -385,11 +392,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.imageDescriptor.LocalColorTableFlag) if (this.imageDescriptor.LocalColorTableFlag)
{ {
int length = this.imageDescriptor.LocalColorTableSize * 3; int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.Configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean); localColorTable = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
this.stream.Read(localColorTable.GetSpan()); this.stream.Read(localColorTable.GetSpan());
} }
indices = this.Configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); indices = this.configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
this.ReadFrameIndices(indices); this.ReadFrameIndices(indices);
Span<byte> rawColorTable = default; Span<byte> rawColorTable = default;
@ -423,7 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameIndices(Buffer2D<byte> indices) private void ReadFrameIndices(Buffer2D<byte> indices)
{ {
int minCodeSize = this.stream.ReadByte(); int minCodeSize = this.stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream); using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream);
lzwDecoder.DecodePixels(minCodeSize, indices); lzwDecoder.DecodePixels(minCodeSize, indices);
} }
@ -451,12 +458,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
if (!transFlag) if (!transFlag)
{ {
image = new Image<TPixel>(this.Configuration, imageWidth, imageHeight, Color.Black.ToPixel<TPixel>(), this.metadata); image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, Color.Black.ToPixel<TPixel>(), this.metadata);
} }
else else
{ {
// This initializes the image to become fully transparent because the alpha channel is zero. // This initializes the image to become fully transparent because the alpha channel is zero.
image = new Image<TPixel>(this.Configuration, imageWidth, imageHeight, this.metadata); image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metadata);
} }
this.SetFrameMetadata(image.Frames.RootFrame.Metadata); this.SetFrameMetadata(image.Frames.RootFrame.Metadata);
@ -675,7 +682,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (globalColorTableLength > 0) if (globalColorTableLength > 0)
{ {
this.globalColorTable = this.MemoryAllocator.Allocate<byte>(globalColorTableLength, AllocationOptions.Clean); this.globalColorTable = this.memoryAllocator.Allocate<byte>(globalColorTableLength, AllocationOptions.Clean);
// Read the global color table data from the stream // Read the global color table data from the stream
stream.Read(this.globalColorTable.GetSpan()); stream.Read(this.globalColorTable.GetSpan());

14
src/ImageSharp/Formats/Gif/GifDecoderOptions.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Configuration options for decoding Gif images.
/// </summary>
public sealed class GifDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

10
src/ImageSharp/Formats/IImageDecoderInternals.cs → src/ImageSharp/Formats/IImageDecoderInternals{T}.cs

@ -9,14 +9,16 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats namespace SixLabors.ImageSharp.Formats
{ {
/// <summary> /// <summary>
/// Abstraction for shared internals for ***DecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>. /// Abstraction for shared internals for XXXDecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary> /// </summary>
internal interface IImageDecoderInternals /// <typeparam name="T">The type of specialized decoder options.</typeparam>
internal interface IImageDecoderInternals<T>
where T : ISpecializedDecoderOptions
{ {
/// <summary> /// <summary>
/// Gets the associated configuration. /// Gets the specialized decoder options.
/// </summary> /// </summary>
Configuration Configuration { get; } T Options { get; }
/// <summary> /// <summary>
/// Gets the dimensions of the image being decoded. /// Gets the dimensions of the image being decoded.

17
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -12,11 +12,12 @@ namespace SixLabors.ImageSharp.Formats
{ {
internal static class ImageDecoderUtilities internal static class ImageDecoderUtilities
{ {
public static IImageInfo Identify( public static IImageInfo Identify<T>(
this IImageDecoderInternals decoder, this IImageDecoderInternals<T> decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
{ {
using var bufferedReadStream = new BufferedReadStream(configuration, stream); using var bufferedReadStream = new BufferedReadStream(configuration, stream);
@ -30,20 +31,22 @@ namespace SixLabors.ImageSharp.Formats
} }
} }
public static Image<TPixel> Decode<TPixel>( public static Image<TPixel> Decode<T, TPixel>(
this IImageDecoderInternals decoder, this IImageDecoderInternals<T> decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> decoder.Decode<TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); => decoder.Decode<T, TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
public static Image<TPixel> Decode<TPixel>( public static Image<TPixel> Decode<T, TPixel>(
this IImageDecoderInternals decoder, this IImageDecoderInternals<T> decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory, Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using var bufferedReadStream = new BufferedReadStream(configuration, stream); using var bufferedReadStream = new BufferedReadStream(configuration, stream);

Loading…
Cancel
Save