Skip to content
Merged
89 changes: 14 additions & 75 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks>
internal sealed class BmpDecoderCore
internal sealed class BmpDecoderCore : IImageDecoderInternals
{
/// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps.
Expand Down Expand Up @@ -89,11 +89,6 @@ internal sealed class BmpDecoderCore
/// </summary>
private BmpInfoHeader infoHeader;

/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;

/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
Expand All @@ -111,67 +106,28 @@ internal sealed class BmpDecoderCore
/// <param name="options">The options.</param>
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
this.Configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator;
this.options = options;
}

/// <inheritdoc />
public Configuration Configuration { get; }

/// <summary>
/// Gets the dimensions of the image.
/// </summary>
public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height);

/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
// if we can seek then we arn't in a context that errors on async operations
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
// cheat for now do async copy of the stream into memory stream and use the sync version
// we should use an array pool backed memorystream implementation
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}

/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
try
{
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);

var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
var image = new Image<TPixel>(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);

Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();

Expand Down Expand Up @@ -242,30 +198,13 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
}
}

/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <inheritdoc />
public IImageInfo Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata);
}

/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}

/// <summary>
/// Returns the y- value based on the given height.
/// </summary>
Expand Down Expand Up @@ -999,7 +938,7 @@ private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
Expand Down Expand Up @@ -1028,7 +967,7 @@ private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
Expand Down Expand Up @@ -1065,7 +1004,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
this.stream.Read(row);

PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
bgraRowSpan,
width);
Expand Down Expand Up @@ -1101,7 +1040,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);

PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
Expand All @@ -1115,7 +1054,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
{
this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
bgraRowSpan,
width);
Expand Down
81 changes: 13 additions & 68 deletions src/ImageSharp/Formats/Gif/GifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
internal sealed class GifDecoderCore
internal sealed class GifDecoderCore : IImageDecoderInternals
{
/// <summary>
/// The temp buffer used to reduce allocations.
/// </summary>
private readonly byte[] buffer = new byte[16];

/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;

/// <summary>
/// The currently loaded stream.
/// </summary>
Expand Down Expand Up @@ -78,9 +73,12 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
{
this.IgnoreMetadata = options.IgnoreMetadata;
this.DecodingMode = options.DecodingMode;
this.configuration = configuration ?? Configuration.Default;
this.Configuration = configuration ?? Configuration.Default;
}

/// <inheritdoc />
public Configuration Configuration { get; }

/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
Expand All @@ -96,40 +94,11 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
/// </summary>
public Size Dimensions => new Size(this.imageDescriptor.Width, this.imageDescriptor.Height);

private MemoryAllocator MemoryAllocator => this.configuration.MemoryAllocator;

/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <returns>The decoded image</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;

/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <returns>The decoded image</returns>
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null;
Expand Down Expand Up @@ -188,31 +157,7 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
return image;
}

/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
if (stream.CanSeek)
{
return this.Identify(stream);
}
else
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}

/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <inheritdoc />
public IImageInfo Identify(Stream stream)
{
try
Expand Down Expand Up @@ -410,11 +355,11 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
if (this.imageDescriptor.LocalColorTableFlag)
{
int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
localColorTable = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
this.stream.Read(localColorTable.Array, 0, length);
}

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);
ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan());
Expand All @@ -438,7 +383,7 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
private void ReadFrameIndices(Buffer2D<byte> indices)
{
int dataSize = 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(dataSize, indices);
}

Expand All @@ -464,7 +409,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
if (previousFrame is null)
{
// 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);

Expand Down
38 changes: 38 additions & 0 deletions src/ImageSharp/Formats/IImageDecoderInternals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System.IO;
using SixLabors.ImageSharp.PixelFormats;

namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Abstraction for shared internals for ***DecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary>
internal interface IImageDecoderInternals
{
/// <summary>
/// Gets the associated configuration.
/// </summary>
Configuration Configuration { get; }

/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>;

/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/>.</returns>
IImageInfo Identify(Stream stream);
}
}
Loading