Browse Source

Merge pull request #1223 from SixLabors/af/FixedCapacityPooledMemoryStream-use-allocator

Use MemoryAllocator in FixedCapacityPooledMemoryStream
pull/1226/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
bbf323374b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 89
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 81
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  3. 38
      src/ImageSharp/Formats/IImageDecoderInternals.cs
  4. 55
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  5. 85
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  6. 95
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  7. 90
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  8. 20
      src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
  9. 5
      src/ImageSharp/Image.FromStream.cs
  10. 5
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

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

@ -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.
@ -89,11 +89,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private BmpInfoHeader infoHeader;
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
@ -111,67 +106,28 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <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();
@ -242,30 +198,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
/// <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>
@ -999,7 +938,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
@ -1028,7 +967,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
@ -1065,7 +1004,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
bgraRowSpan,
width);
@ -1101,7 +1040,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
pixelSpan,
width);
@ -1115,7 +1054,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
this.Configuration,
row.GetSpan(),
bgraRowSpan,
width);

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

@ -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>
@ -78,9 +73,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
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>
@ -96,40 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </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;
@ -188,31 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
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
@ -410,11 +355,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
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());
@ -438,7 +383,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
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);
}
@ -464,7 +409,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
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);

38
src/ImageSharp/Formats/IImageDecoderInternals.cs

@ -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);
}
}

55
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
internal static class ImageDecoderUtilities
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public static async Task<IImageInfo> IdentifyAsync(this IImageDecoderInternals decoder, Stream stream)
{
if (stream.CanSeek)
{
return decoder.Identify(stream);
}
using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return decoder.Identify(ms);
}
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <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>
public static async Task<Image<TPixel>> DecodeAsync<TPixel>(this IImageDecoderInternals decoder, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return decoder.Decode<TPixel>(stream);
}
using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return decoder.Decode<TPixel>(ms);
}
}
}

85
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -25,18 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/>
/// with additional fixes for both performance and common encoding errors.
/// </summary>
internal sealed class JpegDecoderCore : IRawJpegData
internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
{
/// <summary>
/// The only supported precision
/// </summary>
private readonly int[] supportedPrecisions = { 8, 12 };
/// <summary>
/// The global configuration
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The buffer used to temporarily store bytes read from the stream.
/// </summary>
@ -109,10 +104,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="options">The options.</param>
public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
{
this.configuration = configuration ?? Configuration.Default;
this.Configuration = configuration ?? Configuration.Default;
this.IgnoreMetadata = options.IgnoreMetadata;
}
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary>
/// Gets the frame
/// </summary>
@ -213,38 +211,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return new JpegFileMarker(marker[1], stream.Position - 2, true);
}
/// <summary>
/// Decodes the image from the specified <see cref="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.</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);
}
}
}
/// <summary>
/// Decodes the image from the specified <see cref="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.</param>
/// <returns>The decoded image.</returns>
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
this.ParseStream(stream);
this.InitExifProfile();
@ -254,31 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return this.PostProcessIntoImage<TPixel>();
}
/// <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)
{
this.ParseStream(stream, true);
@ -298,7 +243,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public void ParseStream(Stream stream, bool metadataOnly = false)
{
this.Metadata = new ImageMetadata();
this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream);
this.InputStream = new DoubleBufferedStreamReader(this.Configuration.MemoryAllocator, stream);
// Check for the Start Of Image marker.
this.InputStream.Read(this.markerBuffer, 0, 2);
@ -937,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
maxV = v;
}
var component = new JpegComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
@ -962,7 +907,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
int length = remaining;
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
using (IManagedByteBuffer huffmanData = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
for (int i = 2; i < remaining;)
@ -985,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(huffmanData.Array, 0, 16);
using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean))
using (IManagedByteBuffer codeLengths = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean))
{
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.GetSpan());
int codeLengthSum = 0;
@ -1002,7 +947,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
}
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
using (IManagedByteBuffer huffmanValues = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
@ -1129,12 +1074,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
var image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.Configuration,
this.ImageWidth,
this.ImageHeight,
this.Metadata);
using (var postProcessor = new JpegImagePostProcessor(this.configuration, this))
using (var postProcessor = new JpegImagePostProcessor(this.Configuration, this))
{
postProcessor.PostProcess(image.Frames.RootFrame);
}

95
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -24,18 +24,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Performs the png decoding operation.
/// </summary>
internal sealed class PngDecoderCore
internal sealed class PngDecoderCore : IImageDecoderInternals
{
/// <summary>
/// Reusable buffer.
/// </summary>
private readonly byte[] buffer = new byte[4];
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
@ -123,60 +118,22 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="options">The decoder options.</param>
public PngDecoderCore(Configuration configuration, IPngDecoderOptions options)
{
this.configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.Configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.Configuration.MemoryAllocator;
this.ignoreMetadata = options.IgnoreMetadata;
}
/// <inheritdoc/>
public Configuration Configuration { get; }
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
public Size Dimensions => new Size(this.header.Width, this.header.Height);
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
/// <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);
}
}
}
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
/// <returns>The decoded image.</returns>
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
var metadata = new ImageMetadata();
PngMetadata pngMetadata = metadata.GetPngMetadata();
@ -266,31 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
/// <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)
{
var metadata = new ImageMetadata();
@ -446,7 +379,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : unmanaged, IPixel<TPixel>
{
image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.Configuration,
this.header.Width,
this.header.Height,
metadata);
@ -460,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean);
}
/// <summary>
@ -759,7 +692,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline(
this.configuration,
this.Configuration,
this.header,
scanlineSpan,
rowSpan,
@ -773,7 +706,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline(
this.configuration,
this.Configuration,
this.header,
scanlineSpan,
rowSpan,
@ -1258,7 +1191,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private IManagedByteBuffer ReadChunkData(int length)
{
// We rent the buffer here to return it afterwards in Decode()
IManagedByteBuffer buffer = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
IManagedByteBuffer buffer = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
this.currentStream.Read(buffer.Array, 0, length);

90
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Performs the tga decoding operation.
/// </summary>
internal sealed class TgaDecoderCore
internal sealed class TgaDecoderCore : IImageDecoderInternals
{
/// <summary>
/// A scratch buffer to reduce allocations.
@ -38,11 +37,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary>
private TgaFileHeader fileHeader;
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
@ -70,54 +64,22 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <param name="options">The options.</param>
public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions 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.fileHeader.Width, this.fileHeader.Height);
/// <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>
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);
}
}
}
/// <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>
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
try
{
@ -135,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
throw new UnknownImageFormatException("Width or height cannot be 0");
}
var image = Image.CreateUninitialized<TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
var image = Image.CreateUninitialized<TPixel>(this.Configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1)
@ -489,11 +451,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
{
PixelOperations<TPixel>.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width);
}
else
{
PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width);
}
}
}
@ -678,31 +640,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
}
/// <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)
{
this.ReadFileHeader(stream);
@ -719,7 +657,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width);
PixelOperations<TPixel>.Instance.FromL8Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -746,7 +684,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -765,7 +703,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

20
src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs

@ -3,6 +3,7 @@
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.IO
{
@ -11,18 +12,19 @@ namespace SixLabors.ImageSharp.IO
/// </summary>
internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
{
private readonly byte[] buffer;
private readonly IManagedByteBuffer buffer;
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityPooledMemoryStream"/> class.
/// </summary>
/// <param name="length">The length of the stream buffer to rent.</param>
public FixedCapacityPooledMemoryStream(long length)
: this(RentBuffer(length)) => this.Length = length;
/// <param name="allocator">The allocator to rent the buffer from.</param>
public FixedCapacityPooledMemoryStream(long length, MemoryAllocator allocator)
: this(RentBuffer(length, allocator)) => this.Length = length;
private FixedCapacityPooledMemoryStream(byte[] buffer)
: base(buffer) => this.buffer = buffer;
private FixedCapacityPooledMemoryStream(IManagedByteBuffer buffer)
: base(buffer.Array) => this.buffer = buffer;
/// <inheritdoc/>
public override long Length { get; }
@ -36,7 +38,7 @@ namespace SixLabors.ImageSharp.IO
if (disposing)
{
ArrayPool<byte>.Shared.Return(this.buffer);
this.buffer.Dispose();
}
base.Dispose(disposing);
@ -45,6 +47,10 @@ namespace SixLabors.ImageSharp.IO
// In the extrememly unlikely event someone ever gives us a stream
// with length longer than int.MaxValue then we'll use something else.
private static byte[] RentBuffer(long length) => ArrayPool<byte>.Shared.Rent((int)length);
private static IManagedByteBuffer RentBuffer(long length, MemoryAllocator allocator)
{
Guard.MustBeBetweenOrEqualTo(length, 0, int.MaxValue, nameof(length));
return allocator.AllocateManagedByteBuffer((int)length);
}
}
}

5
src/ImageSharp/Image.FromStream.cs

@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@ -675,7 +676,7 @@ namespace SixLabors.ImageSharp
}
// We want to be able to load images from things like HttpContext.Request.Body
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
{
stream.CopyTo(memoryStream);
memoryStream.Position = 0;
@ -711,7 +712,7 @@ namespace SixLabors.ImageSharp
return await action(stream).ConfigureAwait(false);
}
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
{
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;

5
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Memory
{
@ -98,5 +100,8 @@ namespace SixLabors.ImageSharp.Memory
AllocationOptions options = AllocationOptions.None)
where T : struct
=> MemoryGroup<T>.Allocate(memoryAllocator, totalLength, bufferAlignment, options);
internal static MemoryStream AllocateFixedCapacityMemoryStream(this MemoryAllocator allocator, long length) =>
new FixedCapacityPooledMemoryStream(length, allocator);
}
}

Loading…
Cancel
Save