Browse Source

Use pooled stream for async decode/identify

pull/1574/head
James Jackson-South 6 years ago
parent
commit
089b41e2a9
  1. 4
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  2. 3
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 4
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  4. 3
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  5. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  6. 23
      src/ImageSharp/Formats/Png/PngDecoder.cs
  7. 3
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  8. 3
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  9. 50
      src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
  10. 9
      src/ImageSharp/Image.FromStream.cs
  11. 2
      tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs

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

@ -83,11 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream) public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
return await new BmpDecoderCore(configuration, this).IdentifyAsync(stream).ConfigureAwait(false); return new BmpDecoderCore(configuration, this).IdentifyAsync(stream);
} }
} }
} }

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

@ -9,6 +9,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -257,7 +258,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream) public async Task<IImageInfo> IdentifyAsync(Stream stream)
{ {
using (var ms = new MemoryStream()) using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(ms).ConfigureAwait(false); await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0; ms.Position = 0;

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

@ -77,12 +77,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream) public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this); var decoder = new GifDecoderCore(configuration, this);
return await decoder.IdentifyAsync(stream).ConfigureAwait(false); return decoder.IdentifyAsync(stream);
} }
/// <inheritdoc /> /// <inheritdoc />

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

@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
else else
{ {
using (var ms = new MemoryStream()) using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(ms).ConfigureAwait(false); await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0; ms.Position = 0;

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

@ -266,7 +266,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
else else
{ {
using (var ms = new MemoryStream()) using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(ms).ConfigureAwait(false); await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0; ms.Position = 0;

23
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -9,25 +9,8 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
/// Encoder for generating an image out of a png encoded stream. /// Decoder for generating an image out of a png encoded stream.
/// </summary> /// </summary>
/// <remarks>
/// At the moment the following features are supported:
/// <para>
/// <b>Filters:</b> all filters are supported.
/// </para>
/// <para>
/// <b>Pixel formats:</b>
/// <list type="bullet">
/// <item>RGBA (True color) with alpha (8 bit).</item>
/// <item>RGB (True color) without alpha (8 bit).</item>
/// <item>grayscale with alpha (8 bit).</item>
/// <item>grayscale without alpha (8 bit).</item>
/// <item>Palette Index with alpha (8 bit).</item>
/// <item>Palette Index without alpha (8 bit).</item>
/// </list>
/// </para>
/// </remarks>
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
@ -97,10 +80,10 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream) public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
{ {
var decoder = new PngDecoderCore(configuration, this); var decoder = new PngDecoderCore(configuration, this);
return await decoder.IdentifyAsync(stream).ConfigureAwait(false); return decoder.IdentifyAsync(stream);
} }
/// <inheritdoc /> /// <inheritdoc />

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

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -277,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
else else
{ {
using (var ms = new MemoryStream()) using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(ms).ConfigureAwait(false); await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0; ms.Position = 0;

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

@ -6,6 +6,7 @@ using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -689,7 +690,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
} }
else else
{ {
using (var ms = new MemoryStream()) using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(ms).ConfigureAwait(false); await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0; ms.Position = 0;

50
src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs

@ -0,0 +1,50 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.IO;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// A memory stream constructed from a pooled buffer of known length.
/// </summary>
internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
{
private readonly byte[] 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;
private FixedCapacityPooledMemoryStream(byte[] buffer)
: base(buffer) => this.buffer = buffer;
/// <inheritdoc/>
public override long Length { get; }
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (!this.isDisposed)
{
this.isDisposed = true;
if (disposing)
{
ArrayPool<byte>.Shared.Return(this.buffer);
}
base.Dispose(disposing);
}
}
// 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);
}
}

9
src/ImageSharp/Image.FromStream.cs

@ -7,6 +7,7 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -674,10 +675,7 @@ namespace SixLabors.ImageSharp
} }
// We want to be able to load images from things like HttpContext.Request.Body // We want to be able to load images from things like HttpContext.Request.Body
// TODO: Should really find a nice way to use a pool for these. using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
// Investigate readonly version of the linked implementation.
// https://github.com/mgravell/Pipelines.Sockets.Unofficial/compare/mgravell:24482d4...mgravell:6740ea4
using (var memoryStream = new MemoryStream())
{ {
stream.CopyTo(memoryStream); stream.CopyTo(memoryStream);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -713,8 +711,7 @@ namespace SixLabors.ImageSharp
return await action(stream).ConfigureAwait(false); return await action(stream).ConfigureAwait(false);
} }
// TODO: See above comment. using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
using (var memoryStream = new MemoryStream())
{ {
await stream.CopyToAsync(memoryStream).ConfigureAwait(false); await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;

2
tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities
public override bool CanWrite => this.inner.CanWrite; public override bool CanWrite => this.inner.CanWrite;
public override long Length => throw new NotSupportedException("The stream is not seekable."); public override long Length => this.inner.Length;
public override long Position public override long Position
{ {

Loading…
Cancel
Save