diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 26f6c5080..16da086c9 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -83,11 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- public async Task IdentifyAsync(Configuration configuration, Stream stream)
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
- return await new BmpDecoderCore(configuration, this).IdentifyAsync(stream).ConfigureAwait(false);
+ return new BmpDecoderCore(configuration, this).IdentifyAsync(stream);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index f5b576f17..e37144bd5 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -9,6 +9,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Common.Helpers;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -257,7 +258,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The containing image data.
public async Task IdentifyAsync(Stream stream)
{
- using (var ms = new MemoryStream())
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index fbfbee2e4..5f4fdd0fa 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -77,12 +77,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
///
- public async Task IdentifyAsync(Configuration configuration, Stream stream)
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this);
- return await decoder.IdentifyAsync(stream).ConfigureAwait(false);
+ return decoder.IdentifyAsync(stream);
}
///
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 63eab072b..8f8426780 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
- using (var ms = new MemoryStream())
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index ed5c17faa..af1a705d4 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -266,7 +266,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
else
{
- using (var ms = new MemoryStream())
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs
index 2eeb1ac80..a6a040789 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -9,25 +9,8 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png
{
///
- /// Encoder for generating an image out of a png encoded stream.
+ /// Decoder for generating an image out of a png encoded stream.
///
- ///
- /// At the moment the following features are supported:
- ///
- /// Filters: all filters are supported.
- ///
- ///
- /// Pixel formats:
- ///
- /// - RGBA (True color) with alpha (8 bit).
- /// - RGB (True color) without alpha (8 bit).
- /// - grayscale with alpha (8 bit).
- /// - grayscale without alpha (8 bit).
- /// - Palette Index with alpha (8 bit).
- /// - Palette Index without alpha (8 bit).
- ///
- ///
- ///
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{
///
@@ -97,10 +80,10 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- public async Task IdentifyAsync(Configuration configuration, Stream stream)
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
{
var decoder = new PngDecoderCore(configuration, this);
- return await decoder.IdentifyAsync(stream).ConfigureAwait(false);
+ return decoder.IdentifyAsync(stream);
}
///
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 0aec984cc..bb8589de4 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/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.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -277,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
- using (var ms = new MemoryStream())
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index 66cb0ed18..ae8946173 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -6,6 +6,7 @@ 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;
using SixLabors.ImageSharp.PixelFormats;
@@ -689,7 +690,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
else
{
- using (var ms = new MemoryStream())
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
diff --git a/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs b/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
new file mode 100644
index 000000000..878fcc53a
--- /dev/null
+++ b/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
+{
+ ///
+ /// A memory stream constructed from a pooled buffer of known length.
+ ///
+ internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
+ {
+ private readonly byte[] buffer;
+ private bool isDisposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The length of the stream buffer to rent.
+ public FixedCapacityPooledMemoryStream(long length)
+ : this(RentBuffer(length)) => this.Length = length;
+
+ private FixedCapacityPooledMemoryStream(byte[] buffer)
+ : base(buffer) => this.buffer = buffer;
+
+ ///
+ public override long Length { get; }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
+ {
+ this.isDisposed = true;
+
+ if (disposing)
+ {
+ ArrayPool.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.Shared.Rent((int)length);
+ }
+}
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index 5c64ae559..499f5ac19 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -674,10 +675,7 @@ namespace SixLabors.ImageSharp
}
// 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.
- // Investigate readonly version of the linked implementation.
- // https://github.com/mgravell/Pipelines.Sockets.Unofficial/compare/mgravell:24482d4...mgravell:6740ea4
- using (var memoryStream = new MemoryStream())
+ using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
{
stream.CopyTo(memoryStream);
memoryStream.Position = 0;
@@ -713,8 +711,7 @@ namespace SixLabors.ImageSharp
return await action(stream).ConfigureAwait(false);
}
- // TODO: See above comment.
- using (var memoryStream = new MemoryStream())
+ using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
diff --git a/tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs b/tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs
index 2000c6e0c..6a05ce0bc 100644
--- a/tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/AsyncStreamWrapper.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities
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
{