diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 16da086c9..7e8ac0721 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -29,8 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black;
///
- public async Task> DecodeAsync(Configuration configuration, Stream stream)
- where TPixel : unmanaged, IPixel
+ public Image Decode(Configuration configuration, Stream stream)
+ where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -38,19 +39,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
try
{
- return await decoder.DecodeAsync(stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Decode(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- throw new InvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}. This error can happen for very large RLE bitmaps, which are not supported.", ex);
+ throw new InvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}. This error can happen for very large RLE bitmaps, which are not supported.", ex);
}
}
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ => this.Decode(configuration, stream);
+
///
- public Image Decode(Configuration configuration, Stream stream)
- where TPixel : unmanaged, IPixel
+ public async Task> DecodeAsync(Configuration configuration, Stream stream)
+ where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -58,28 +64,28 @@ namespace SixLabors.ImageSharp.Formats.Bmp
try
{
- return decoder.Decode(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return await decoder.DecodeAsync(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- throw new InvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}. This error can happen for very large RLE bitmaps, which are not supported.", ex);
+ throw new InvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}. This error can happen for very large RLE bitmaps, which are not supported.", ex);
}
}
///
- public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
+ public async Task DecodeAsync(Configuration configuration, Stream stream)
+ => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
- return new BmpDecoderCore(configuration, this).Identify(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return new BmpDecoderCore(configuration, this).Identify(bufferedStream);
}
///
@@ -87,7 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
Guard.NotNull(stream, nameof(stream));
- return new BmpDecoderCore(configuration, this).IdentifyAsync(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return new BmpDecoderCore(configuration, this).IdentifyAsync(bufferedStream);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 4b14061cf..ea8fd11a8 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -4,10 +4,8 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
-using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -62,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// The stream to decode from.
///
- private Stream stream;
+ private BufferedReadStream stream;
///
/// The metadata.
@@ -120,7 +118,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height);
///
- public Image Decode(Stream stream)
+ public Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel
{
try
@@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- public IImageInfo Identify(Stream stream)
+ public IImageInfo Identify(BufferedReadStream stream)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata);
@@ -1355,7 +1353,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.
- private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
+ private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette)
{
this.stream = stream;
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index 5f4fdd0fa..2a5fde6ac 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.IO;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -26,54 +26,66 @@ namespace SixLabors.ImageSharp.Formats.Gif
public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All;
///
- public async Task> DecodeAsync(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
var decoder = new GifDecoderCore(configuration, this);
try
{
- return await decoder.DecodeAsync(stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Decode(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- GifThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ GifThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ => this.Decode(configuration, stream);
+
///
- public Image Decode(Configuration configuration, Stream stream)
+ public async Task> DecodeAsync(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
var decoder = new GifDecoderCore(configuration, this);
try
{
- return decoder.Decode(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return await decoder.DecodeAsync(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- GifThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ GifThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
+ ///
+ public async Task DecodeAsync(Configuration configuration, Stream stream)
+ => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
+
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this);
- return decoder.Identify(stream);
+
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Identify(bufferedStream);
}
///
@@ -82,13 +94,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this);
- return decoder.IdentifyAsync(stream);
- }
- ///
- public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.IdentifyAsync(bufferedStream);
+ }
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index e4c98799b..78ffee8bd 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The currently loaded stream.
///
- private Stream stream;
+ private BufferedReadStream stream;
///
/// The global color table.
@@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;
///
- public Image Decode(Stream stream)
+ public Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel
{
Image image = null;
@@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
///
- public IImageInfo Identify(Stream stream)
+ public IImageInfo Identify(BufferedReadStream stream)
{
try
{
@@ -572,7 +572,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Reads the logical screen descriptor and global color table blocks
///
/// The stream containing image data.
- private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
+ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream)
{
this.stream = stream;
diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
index 6a975951c..9eaa55566 100644
--- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
@@ -3,10 +3,9 @@
using System;
using System.Buffers;
-using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@@ -29,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The stream to decode.
///
- private readonly Stream stream;
+ private readonly BufferedReadStream stream;
///
/// The prefix buffer.
@@ -52,8 +51,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The to use for buffer allocations.
/// The stream to read from.
- /// is null.
- public LzwDecoder(MemoryAllocator memoryAllocator, Stream stream)
+ /// is null.
+ public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream)
{
this.stream = stream ?? throw new ArgumentNullException(nameof(stream));
diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs
index 3ab912353..33748bf24 100644
--- a/src/ImageSharp/Formats/IImageDecoderInternals.cs
+++ b/src/ImageSharp/Formats/IImageDecoderInternals.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System.IO;
+using System;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
@@ -21,18 +22,16 @@ namespace SixLabors.ImageSharp.Formats
///
/// The pixel format.
/// The stream, where the image should be decoded from. Cannot be null.
- ///
- /// is null.
- ///
+ /// is null.
/// The decoded image.
- Image Decode(Stream stream)
+ Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel;
///
/// Reads the raw image information from the specified stream.
///
- /// The containing image data.
+ /// The containing image data.
/// The .
- IImageInfo Identify(Stream stream);
+ IImageInfo Identify(BufferedReadStream stream);
}
}
diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs
index 6bb9116cd..9d1639a09 100644
--- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs
+++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs
@@ -1,9 +1,9 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System.IO;
+using System;
using System.Threading.Tasks;
-using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
@@ -14,42 +14,21 @@ namespace SixLabors.ImageSharp.Formats
/// Reads the raw image information from the specified stream.
///
/// The decoder.
- /// The containing image data.
- public static async Task 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);
- }
+ /// The containing image data.
+ /// is null.
+ /// A representing the asynchronous operation.
+ public static Task IdentifyAsync(this IImageDecoderInternals decoder, BufferedReadStream stream)
+ => Task.FromResult(decoder.Identify(stream));
///
/// Decodes the image from the specified stream.
///
/// The pixel format.
/// The decoder.
- /// The stream, where the image should be decoded from. Cannot be null.
- ///
- /// is null.
- ///
- /// The decoded image.
- public static async Task> DecodeAsync(this IImageDecoderInternals decoder, Stream stream)
+ /// The containing image data.
+ /// A representing the asynchronous operation.
+ public static Task> DecodeAsync(this IImageDecoderInternals decoder, BufferedReadStream stream)
where TPixel : unmanaged, IPixel
- {
- if (stream.CanSeek)
- {
- return decoder.Decode(stream);
- }
-
- using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
- await stream.CopyToAsync(ms).ConfigureAwait(false);
- ms.Position = 0;
- return decoder.Decode(ms);
- }
+ => Task.FromResult(decoder.Decode(stream));
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
index 44f3aa563..774780170 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
@@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System.IO;
using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
internal struct HuffmanScanBuffer
{
- private readonly Stream stream;
+ private readonly BufferedReadStream stream;
// The entropy encoded code buffer.
private ulong data;
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Whether there is no more good data to pull from the stream for the current mcu.
private bool badData;
- public HuffmanScanBuffer(Stream stream)
+ public HuffmanScanBuffer(BufferedReadStream stream)
{
this.stream = stream;
this.data = 0ul;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
index 52ea65dd8..d6c16f826 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
@@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private readonly JpegFrame frame;
private readonly HuffmanTable[] dcHuffmanTables;
private readonly HuffmanTable[] acHuffmanTables;
- private readonly Stream stream;
+ private readonly BufferedReadStream stream;
private readonly JpegComponent[] components;
// The restart interval.
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The successive approximation bit high end.
/// The successive approximation bit low end.
public HuffmanScanDecoder(
- Stream stream,
+ BufferedReadStream stream,
JpegFrame frame,
HuffmanTable[] dcHuffmanTables,
HuffmanTable[] acHuffmanTables,
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
index 2d2d7fb56..c5332acb5 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -13,13 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
- ///
- /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
- ///
+ ///
public bool IgnoreMetadata { get; set; }
///
- public async Task> DecodeAsync(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -27,21 +26,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
using var decoder = new JpegDecoderCore(configuration, this);
try
{
- return await decoder.DecodeAsync(stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Decode(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
(int w, int h) = (decoder.ImageWidth, decoder.ImageHeight);
- JpegThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {w}x{h}.", ex);
+ JpegThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {w}x{h}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ => this.Decode(configuration, stream);
+
///
- public Image Decode(Configuration configuration, Stream stream)
+ public async Task> DecodeAsync(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -49,23 +53,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
using var decoder = new JpegDecoderCore(configuration, this);
try
{
- return decoder.Decode(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return await decoder.DecodeAsync(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
(int w, int h) = (decoder.ImageWidth, decoder.ImageHeight);
- JpegThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {w}x{h}.", ex);
+ JpegThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {w}x{h}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
- ///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
-
///
public async Task DecodeAsync(Configuration configuration, Stream stream)
=> await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
@@ -75,21 +76,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
Guard.NotNull(stream, nameof(stream));
- using (var decoder = new JpegDecoderCore(configuration, this))
- {
- return decoder.Identify(stream);
- }
+ using var decoder = new JpegDecoderCore(configuration, this);
+ using var bufferedStream = new BufferedReadStream(stream);
+
+ return decoder.Identify(bufferedStream);
}
///
- public async Task IdentifyAsync(Configuration configuration, Stream stream)
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
- using (var decoder = new JpegDecoderCore(configuration, this))
- {
- return await decoder.IdentifyAsync(stream).ConfigureAwait(false);
- }
+ using var decoder = new JpegDecoderCore(configuration, this);
+ using var bufferedStream = new BufferedReadStream(stream);
+
+ return decoder.IdentifyAsync(bufferedStream);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 48501ddf6..2956d2c11 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -3,13 +3,12 @@
using System;
using System.Buffers.Binary;
-using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -174,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The buffer to read file markers to
/// The input stream
/// The
- public static JpegFileMarker FindNextFileMarker(byte[] marker, Stream stream)
+ public static JpegFileMarker FindNextFileMarker(byte[] marker, BufferedReadStream stream)
{
int value = stream.Read(marker, 0, 2);
@@ -206,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
///
- public Image Decode(Stream stream)
+ public Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel
{
this.ParseStream(stream);
@@ -218,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
///
- public IImageInfo Identify(Stream stream)
+ public IImageInfo Identify(BufferedReadStream stream)
{
this.ParseStream(stream, true);
this.InitExifProfile();
@@ -234,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream
/// Whether to decode metadata only.
- public void ParseStream(Stream stream, bool metadataOnly = false)
+ public void ParseStream(BufferedReadStream stream, bool metadataOnly = false)
{
this.Metadata = new ImageMetadata();
@@ -497,7 +496,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessApplicationHeaderMarker(Stream stream, int remaining)
+ private void ProcessApplicationHeaderMarker(BufferedReadStream stream, int remaining)
{
// We can only decode JFif identifiers.
if (remaining < JFifMarker.Length)
@@ -524,7 +523,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessApp1Marker(Stream stream, int remaining)
+ private void ProcessApp1Marker(BufferedReadStream stream, int remaining)
{
const int Exif00 = 6;
if (remaining < Exif00 || this.IgnoreMetadata)
@@ -558,7 +557,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessApp2Marker(Stream stream, int remaining)
+ private void ProcessApp2Marker(BufferedReadStream stream, int remaining)
{
// Length is 14 though we only need to check 12.
const int Icclength = 14;
@@ -601,7 +600,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessApp13Marker(Stream stream, int remaining)
+ private void ProcessApp13Marker(BufferedReadStream stream, int remaining)
{
if (remaining < ProfileResolver.AdobePhotoshopApp13Marker.Length || this.IgnoreMetadata)
{
@@ -691,7 +690,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessApp14Marker(Stream stream, int remaining)
+ private void ProcessApp14Marker(BufferedReadStream stream, int remaining)
{
const int MarkerLength = AdobeMarker.Length;
if (remaining < MarkerLength)
@@ -720,7 +719,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Thrown if the tables do not match the header
///
- private void ProcessDefineQuantizationTablesMarker(Stream stream, int remaining)
+ private void ProcessDefineQuantizationTablesMarker(BufferedReadStream stream, int remaining)
{
while (remaining > 0)
{
@@ -806,7 +805,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The remaining bytes in the segment block.
/// The current frame marker.
/// Whether to parse metadata only
- private void ProcessStartOfFrameMarker(Stream stream, int remaining, in JpegFileMarker frameMarker, bool metadataOnly)
+ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, in JpegFileMarker frameMarker, bool metadataOnly)
{
if (this.Frame != null)
{
@@ -907,7 +906,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessDefineHuffmanTablesMarker(Stream stream, int remaining)
+ private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining)
{
int length = remaining;
@@ -974,7 +973,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The input stream.
/// The remaining bytes in the segment block.
- private void ProcessDefineRestartIntervalMarker(Stream stream, int remaining)
+ private void ProcessDefineRestartIntervalMarker(BufferedReadStream stream, int remaining)
{
if (remaining != 2)
{
@@ -988,7 +987,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Processes the SOS (Start of scan marker).
///
/// The input stream.
- private void ProcessStartOfScanMarker(Stream stream)
+ private void ProcessStartOfScanMarker(BufferedReadStream stream)
{
if (this.Frame is null)
{
@@ -1061,7 +1060,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The input stream.
/// The
[MethodImpl(InliningOptions.ShortMethod)]
- private ushort ReadUint16(Stream stream)
+ private ushort ReadUint16(BufferedReadStream stream)
{
stream.Read(this.markerBuffer, 0, 2);
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs
index a6a040789..9eb927784 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -13,83 +14,73 @@ namespace SixLabors.ImageSharp.Formats.Png
///
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{
- ///
- /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
- ///
+ ///
public bool IgnoreMetadata { get; set; }
- ///
- /// Decodes the image from the specified stream to the .
- ///
- /// The pixel format.
- /// The configuration for the image.
- /// The containing image data.
- /// The decoded image.
- public async Task> DecodeAsync(Configuration configuration, Stream stream)
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
var decoder = new PngDecoderCore(configuration, this);
try
{
- return await decoder.DecodeAsync(stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Decode(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- PngThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ PngThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
- ///
- /// Decodes the image from the specified stream to the .
- ///
- /// The pixel format.
- /// The configuration for the image.
- /// The containing image data.
- /// The decoded image.
- public Image Decode(Configuration configuration, Stream stream)
+ ///
+ public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
+
+ ///
+ public async Task> DecodeAsync(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
var decoder = new PngDecoderCore(configuration, this);
try
{
- return decoder.Decode(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return await decoder.DecodeAsync(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- PngThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ PngThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
+ ///
+ public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
+
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
var decoder = new PngDecoderCore(configuration, this);
- return decoder.Identify(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Identify(bufferedStream);
}
///
public Task IdentifyAsync(Configuration configuration, Stream stream)
{
var decoder = new PngDecoderCore(configuration, this);
- return decoder.IdentifyAsync(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.IdentifyAsync(bufferedStream);
}
-
- ///
- public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index e2b0e50fc..89fa4e63d 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The stream to decode from.
///
- private Stream currentStream;
+ private BufferedReadStream currentStream;
///
/// The png header.
@@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public Size Dimensions => new Size(this.header.Width, this.header.Height);
///
- public Image Decode(Stream stream)
+ public Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel
{
var metadata = new ImageMetadata();
@@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- public IImageInfo Identify(Stream stream)
+ public IImageInfo Identify(BufferedReadStream stream)
{
var metadata = new ImageMetadata();
PngMetadata pngMetadata = metadata.GetPngMetadata();
@@ -499,7 +499,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The compressed pixel data stream.
/// The image to decode to.
/// The png metadata
- private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata)
+ private void DecodePixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel
{
while (this.currentRow < this.header.Height)
@@ -555,7 +555,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The compressed pixel data stream.
/// The current image.
/// The png metadata.
- private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata)
+ private void DecodeInterlacedPixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel
{
int pass = 0;
@@ -1027,7 +1027,8 @@ namespace SixLabors.ImageSharp.Formats.Png
private bool TryUncompressTextData(ReadOnlySpan compressedData, Encoding encoding, out string value)
{
using (var memoryStream = new MemoryStream(compressedData.ToArray()))
- using (var inflateStream = new ZlibInflateStream(memoryStream))
+ using (var bufferedStream = new BufferedReadStream(memoryStream))
+ using (var inflateStream = new ZlibInflateStream(bufferedStream))
{
if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
{
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
index 07316d37b..52ef0e85b 100644
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
using System.IO.Compression;
+using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
@@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// The inner raw memory stream.
///
- private readonly Stream innerStream;
+ private readonly BufferedReadStream innerStream;
///
/// A value indicating whether this instance of the given entity has been disposed.
@@ -56,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// Initializes a new instance of the class.
///
/// The inner raw stream.
- public ZlibInflateStream(Stream innerStream)
+ public ZlibInflateStream(BufferedReadStream innerStream)
: this(innerStream, GetDataNoOp)
{
}
@@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// The inner raw stream.
/// A delegate to get more data from the inner stream.
- public ZlibInflateStream(Stream innerStream, Func getData)
+ public ZlibInflateStream(BufferedReadStream innerStream, Func getData)
{
this.innerStream = innerStream;
this.getData = getData;
@@ -272,7 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.currentDataRemaining -= 4;
}
- // Initialize the deflate Stream.
+ // Initialize the deflate BufferedReadStream.
this.CompressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
return true;
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
index 06b9ab605..3d9b9a3d2 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector
{
///
- public async Task> DecodeAsync(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -23,21 +24,26 @@ namespace SixLabors.ImageSharp.Formats.Tga
try
{
- return await decoder.DecodeAsync(stream).ConfigureAwait(false);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return decoder.Decode(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- TgaThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ TgaThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
}
}
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ => this.Decode(configuration, stream);
+
///
- public Image Decode(Configuration configuration, Stream stream)
+ public async Task> DecodeAsync(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
@@ -46,13 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Tga
try
{
- return decoder.Decode(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return await decoder.DecodeAsync(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;
- TgaThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
+ TgaThrowHelper.ThrowInvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex);
// Not reachable, as the previous statement will throw a exception.
return null;
@@ -60,17 +67,16 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
///
- public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
+ public async Task DecodeAsync(Configuration configuration, Stream stream)
+ => await this.DecodeAsync(configuration, stream).ConfigureAwait(false);
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
- return new TgaDecoderCore(configuration, this).Identify(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return new TgaDecoderCore(configuration, this).Identify(bufferedStream);
}
///
@@ -78,7 +84,8 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
Guard.NotNull(stream, nameof(stream));
- return new TgaDecoderCore(configuration, this).IdentifyAsync(stream);
+ using var bufferedStream = new BufferedReadStream(stream);
+ return new TgaDecoderCore(configuration, this).IdentifyAsync(bufferedStream);
}
}
}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index 3f6d721f6..7cd83fedb 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
///
/// The stream to decode from.
///
- private Stream currentStream;
+ private BufferedReadStream currentStream;
///
/// The bitmap decoder options.
@@ -78,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height);
///
- public Image Decode(Stream stream)
+ public Image Decode(BufferedReadStream stream)
where TPixel : unmanaged, IPixel
{
try
@@ -641,7 +640,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
///
- public IImageInfo Identify(Stream stream)
+ public IImageInfo Identify(BufferedReadStream stream)
{
this.ReadFileHeader(stream);
return new ImageInfo(
@@ -868,9 +867,9 @@ namespace SixLabors.ImageSharp.Formats.Tga
///
/// Reads the tga file header from the stream.
///
- /// The containing image data.
+ /// The containing image data.
/// The image origin.
- private TgaImageOrigin ReadFileHeader(Stream stream)
+ private TgaImageOrigin ReadFileHeader(BufferedReadStream stream)
{
this.currentStream = stream;
diff --git a/src/ImageSharp/IO/BufferedReadStream.cs b/src/ImageSharp/IO/BufferedReadStream.cs
index 269c1aa8e..816626374 100644
--- a/src/ImageSharp/IO/BufferedReadStream.cs
+++ b/src/ImageSharp/IO/BufferedReadStream.cs
@@ -157,14 +157,38 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
+#if SUPPORTS_SPAN_STREAM
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override int Read(Span buffer)
+ {
+ // Too big for our buffer. Read directly from the stream.
+ int count = buffer.Length;
+ if (count > BufferLength)
+ {
+ return this.ReadToBufferDirectSlow(buffer);
+ }
+
+ // Too big for remaining buffer but less than entire buffer length
+ // Copy to buffer then read from there.
+ if (count + this.readBufferIndex > BufferLength)
+ {
+ return this.ReadToBufferViaCopySlow(buffer);
+ }
+
+ return this.ReadToBufferViaCopyFast(buffer);
+ }
+#endif
+
///
public override void Flush()
{
// Reset the stream position to match reader position.
- if (this.readerPosition != this.BaseStream.Position)
+ Stream baseStream = this.BaseStream;
+ if (this.readerPosition != baseStream.Position)
{
- this.BaseStream.Seek(this.readerPosition, SeekOrigin.Begin);
- this.readerPosition = (int)this.BaseStream.Position;
+ baseStream.Seek(this.readerPosition, SeekOrigin.Begin);
+ this.readerPosition = (int)baseStream.Position;
}
// Reset to trigger full read on next attempt.
@@ -231,9 +255,10 @@ namespace SixLabors.ImageSharp.IO
[MethodImpl(MethodImplOptions.NoInlining)]
private void FillReadBuffer()
{
- if (this.readerPosition != this.BaseStream.Position)
+ Stream baseStream = this.BaseStream;
+ if (this.readerPosition != baseStream.Position)
{
- this.BaseStream.Seek(this.readerPosition, SeekOrigin.Begin);
+ baseStream.Seek(this.readerPosition, SeekOrigin.Begin);
}
// Read doesn't always guarantee the full returned length so read a byte
@@ -242,7 +267,7 @@ namespace SixLabors.ImageSharp.IO
int i;
do
{
- i = this.BaseStream.Read(this.readBuffer, n, BufferLength - n);
+ i = baseStream.Read(this.readBuffer, n, BufferLength - n);
n += i;
}
while (n < BufferLength && i > 0);
@@ -250,6 +275,20 @@ namespace SixLabors.ImageSharp.IO
this.readBufferIndex = 0;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int ReadToBufferViaCopyFast(Span buffer)
+ {
+ int n = this.GetCopyCount(buffer.Length);
+
+ // Just straight copy. MemoryStream does the same so should be fast enough.
+ this.readBuffer.AsSpan(this.readBufferIndex, n).CopyTo(buffer);
+
+ this.readerPosition += n;
+ this.readBufferIndex += n;
+
+ return n;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count)
{
@@ -262,6 +301,15 @@ namespace SixLabors.ImageSharp.IO
return n;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int ReadToBufferViaCopySlow(Span buffer)
+ {
+ // Refill our buffer then copy.
+ this.FillReadBuffer();
+
+ return this.ReadToBufferViaCopyFast(buffer);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count)
{
@@ -271,13 +319,41 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private int ReadToBufferDirectSlow(Span buffer)
+ {
+ // Read to target but don't copy to our read buffer.
+ Stream baseStream = this.BaseStream;
+ if (this.readerPosition != baseStream.Position)
+ {
+ baseStream.Seek(this.readerPosition, SeekOrigin.Begin);
+ }
+
+ // Read doesn't always guarantee the full returned length so read a byte
+ // at a time until we get either our count or hit the end of the stream.
+ int count = buffer.Length;
+ int n = 0;
+ int i;
+ do
+ {
+ i = baseStream.Read(buffer.Slice(n, count - n));
+ n += i;
+ }
+ while (n < count && i > 0);
+
+ this.Position += n;
+
+ return n;
+ }
+
[MethodImpl(MethodImplOptions.NoInlining)]
private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count)
{
// Read to target but don't copy to our read buffer.
- if (this.readerPosition != this.BaseStream.Position)
+ Stream baseStream = this.BaseStream;
+ if (this.readerPosition != baseStream.Position)
{
- this.BaseStream.Seek(this.readerPosition, SeekOrigin.Begin);
+ baseStream.Seek(this.readerPosition, SeekOrigin.Begin);
}
// Read doesn't always guarantee the full returned length so read a byte
@@ -286,7 +362,7 @@ namespace SixLabors.ImageSharp.IO
int i;
do
{
- i = this.BaseStream.Read(buffer, n + offset, count - n);
+ i = baseStream.Read(buffer, n + offset, count - n);
n += i;
}
while (n < count && i > 0);
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index e882bf2f8..beec0b188 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -7,7 +7,6 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp
/// The stream is not readable.
/// The format type or null if none found.
public static IImageFormat DetectFormat(Configuration configuration, Stream stream)
- => WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration), false);
+ => WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration));
///
/// By reading the header on the provided stream this calculates the images format type.
@@ -63,8 +62,7 @@ namespace SixLabors.ImageSharp
=> WithSeekableStreamAsync(
configuration,
stream,
- s => InternalDetectFormatAsync(s, configuration),
- false);
+ s => InternalDetectFormatAsync(s, configuration));
///
/// Reads the raw image information from the specified stream without fully decoding it.
@@ -663,17 +661,11 @@ namespace SixLabors.ImageSharp
/// The configuration.
/// The input stream.
/// The action to perform.
- ///
- /// Whether to buffer the input stream.
- /// Short intial reads like do not require
- /// the overhead of reading the stream to the buffer. Defaults to .
- ///
/// The .
private static T WithSeekableStream(
Configuration configuration,
Stream stream,
- Func action,
- bool buffer = true)
+ Func action)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(stream, nameof(stream));
@@ -690,29 +682,15 @@ namespace SixLabors.ImageSharp
stream.Position = 0;
}
- if (buffer)
- {
- using var bufferedStream = new BufferedReadStream(stream);
- return action(bufferedStream);
- }
-
return action(stream);
}
// We want to be able to load images from things like HttpContext.Request.Body
- using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
- {
- stream.CopyTo(memoryStream);
- memoryStream.Position = 0;
+ using MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
+ stream.CopyTo(memoryStream);
+ memoryStream.Position = 0;
- if (buffer)
- {
- using var bufferedStream = new BufferedReadStream(memoryStream);
- return action(bufferedStream);
- }
-
- return action(memoryStream);
- }
+ return action(memoryStream);
}
///
@@ -722,17 +700,11 @@ namespace SixLabors.ImageSharp
/// The configuration.
/// The input stream.
/// The action to perform.
- ///
- /// Whether to buffer the input stream.
- /// Short intial reads like do not require
- /// the overhead of reading the stream to the buffer. Defaults to .
- ///
/// The .
private static async Task WithSeekableStreamAsync(
Configuration configuration,
Stream stream,
- Func> action,
- bool buffer = true)
+ Func> action)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(stream, nameof(stream));
@@ -753,28 +725,14 @@ namespace SixLabors.ImageSharp
stream.Position = 0;
}
- if (buffer)
- {
- using var bufferedStream = new BufferedReadStream(stream);
- return await action(bufferedStream).ConfigureAwait(false);
- }
-
return await action(stream).ConfigureAwait(false);
}
- using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
- {
- await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
- memoryStream.Position = 0;
-
- if (buffer)
- {
- using var bufferedStream = new BufferedReadStream(memoryStream);
- return await action(bufferedStream).ConfigureAwait(false);
- }
+ using MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
+ await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
+ memoryStream.Position = 0;
- return await action(memoryStream).ConfigureAwait(false);
- }
+ return await action(memoryStream).ConfigureAwait(false);
}
}
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
index dfedf3d89..ef098e263 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
@@ -6,6 +6,7 @@ using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Tests;
using SDSize = System.Drawing.Size;
@@ -30,24 +31,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true, Description = "System.Drawing FULL")]
public SDSize JpegSystemDrawing()
{
- using (var memoryStream = new MemoryStream(this.jpegBytes))
- {
- using (var image = System.Drawing.Image.FromStream(memoryStream))
- {
- return image.Size;
- }
- }
+ using var memoryStream = new MemoryStream(this.jpegBytes);
+ using var image = System.Drawing.Image.FromStream(memoryStream);
+ return image.Size;
}
[Benchmark(Description = "JpegDecoderCore.ParseStream")]
public void ParseStreamPdfJs()
{
- using (var memoryStream = new MemoryStream(this.jpegBytes))
- {
- var decoder = new JpegDecoderCore(Configuration.Default, new Formats.Jpeg.JpegDecoder { IgnoreMetadata = true });
- decoder.ParseStream(memoryStream);
- decoder.Dispose();
- }
+ using var memoryStream = new MemoryStream(this.jpegBytes);
+ using var bufferedStream = new BufferedReadStream(memoryStream);
+
+ var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true });
+ decoder.ParseStream(bufferedStream);
+ decoder.Dispose();
}
// RESULTS (2019 April 23):
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
index e69ba98f9..0694a0855 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using Microsoft.DotNet.RemoteExecutor;
using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
@@ -71,16 +72,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void ParseStream_BasicPropertiesAreCorrect()
{
byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes;
- using (var ms = new MemoryStream(bytes))
- {
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
- decoder.ParseStream(ms);
-
- // I don't know why these numbers are different. All I know is that the decoder works
- // and spectral data is exactly correct also.
- // VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 43, 61, 22, 31, 22, 31);
- VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 44, 62, 22, 31, 22, 31);
- }
+ using var ms = new MemoryStream(bytes);
+ using var bufferedStream = new BufferedReadStream(ms);
+ var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+ decoder.ParseStream(bufferedStream);
+
+ // I don't know why these numbers are different. All I know is that the decoder works
+ // and spectral data is exactly correct also.
+ // VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 43, 61, 22, 31, 22, 31);
+ VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 44, 62, 22, 31, 22, 31);
}
public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg";
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
index fad2f06b1..1d200592a 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
@@ -51,13 +52,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
- using (var ms = new MemoryStream(sourceBytes))
- {
- decoder.ParseStream(ms);
+ using var ms = new MemoryStream(sourceBytes);
+ using var bufferedStream = new BufferedReadStream(ms);
+ decoder.ParseStream(bufferedStream);
- var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
- VerifyJpeg.SaveSpectralImage(provider, data);
- }
+ var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
+ VerifyJpeg.SaveSpectralImage(provider, data);
}
[Theory]
@@ -74,13 +74,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
- using (var ms = new MemoryStream(sourceBytes))
- {
- decoder.ParseStream(ms);
- var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
+ using var ms = new MemoryStream(sourceBytes);
+ using var bufferedStream = new BufferedReadStream(ms);
+ decoder.ParseStream(bufferedStream);
- this.VerifySpectralCorrectnessImpl(provider, imageSharpData);
- }
+ var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
+ this.VerifySpectralCorrectnessImpl(provider, imageSharpData);
}
private void VerifySpectralCorrectnessImpl(
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
index 983faddf1..96d85fd8e 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
@@ -8,7 +8,7 @@ using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
-
+using SixLabors.ImageSharp.IO;
using Xunit;
using Xunit.Abstractions;
@@ -192,12 +192,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false)
{
byte[] bytes = TestFile.Create(testFileName).Bytes;
- using (var ms = new MemoryStream(bytes))
- {
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
- decoder.ParseStream(ms, metaDataOnly);
- return decoder;
- }
+ using var ms = new MemoryStream(bytes);
+ using var bufferedStream = new BufferedReadStream(ms);
+
+ var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+ decoder.ParseStream(bufferedStream, metaDataOnly);
+
+ return decoder;
}
}
}
diff --git a/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs b/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
index c9ace8df2..d08d5adef 100644
--- a/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
+++ b/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
@@ -143,6 +143,42 @@ namespace SixLabors.ImageSharp.Tests.IO
}
}
+ [Fact]
+ public void BufferedStreamCanReadSubsequentMultipleByteSpanCorrectly()
+ {
+ using (MemoryStream stream = this.CreateTestStream())
+ {
+ Span buffer = new byte[2];
+ byte[] expected = stream.ToArray();
+ using (var reader = new BufferedReadStream(stream))
+ {
+ for (int i = 0, o = 0; i < expected.Length / 2; i++, o += 2)
+ {
+ Assert.Equal(2, reader.Read(buffer, 0, 2));
+ Assert.Equal(expected[o], buffer[0]);
+ Assert.Equal(expected[o + 1], buffer[1]);
+ Assert.Equal(o + 2, reader.Position);
+
+ int offset = i * 2;
+ if (offset < BufferedReadStream.BufferLength)
+ {
+ Assert.Equal(stream.Position, BufferedReadStream.BufferLength);
+ }
+ else if (offset >= BufferedReadStream.BufferLength && offset < BufferedReadStream.BufferLength * 2)
+ {
+ // We should have advanced to the second chunk now.
+ Assert.Equal(stream.Position, BufferedReadStream.BufferLength * 2);
+ }
+ else
+ {
+ // We should have advanced to the third chunk now.
+ Assert.Equal(stream.Position, BufferedReadStream.BufferLength * 3);
+ }
+ }
+ }
+ }
+ }
+
[Fact]
public void BufferedStreamCanSkip()
{
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs
index 813c68d4c..9d4ffdace 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs
@@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.IO;
-using Moq;
+
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -44,11 +42,7 @@ namespace SixLabors.ImageSharp.Tests
var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object);
Assert.NotNull(img);
- this.localDecoder
- .Verify(
- x => x.Decode(
- this.TopLevelConfiguration,
- It.Is(x => ((BufferedReadStream)x).BaseStream == this.DataStream)));
+ this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream));
}
[Fact]
@@ -57,10 +51,7 @@ namespace SixLabors.ImageSharp.Tests
var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object);
Assert.NotNull(img);
- this.localDecoder.Verify(
- x => x.Decode(
- this.TopLevelConfiguration,
- It.Is(x => ((BufferedReadStream)x).BaseStream == this.DataStream)));
+ this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream));
}
[Fact]
@@ -90,9 +81,9 @@ namespace SixLabors.ImageSharp.Tests
{
Assert.Throws(
() =>
- {
- Image.Load(this.TopLevelConfiguration, Guid.NewGuid().ToString());
- });
+ {
+ Image.Load(this.TopLevelConfiguration, Guid.NewGuid().ToString());
+ });
}
[Fact]
@@ -100,9 +91,9 @@ namespace SixLabors.ImageSharp.Tests
{
Assert.Throws(
() =>
- {
- Image.Load(this.TopLevelConfiguration, (string)null);
- });
+ {
+ Image.Load(this.TopLevelConfiguration, (string)null);
+ });
}
}
}
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs
index aa3d50eae..c7737ef8b 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs
@@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
-using Moq;
+
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -55,10 +54,7 @@ namespace SixLabors.ImageSharp.Tests
var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object);
Assert.NotNull(img);
- this.localDecoder.Verify(
- x => x.Decode(
- this.TopLevelConfiguration,
- It.Is(x => ((BufferedReadStream)x).BaseStream == stream)));
+ this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream));
}
[Fact]
@@ -68,10 +64,7 @@ namespace SixLabors.ImageSharp.Tests
var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object);
Assert.NotNull(img);
- this.localDecoder.Verify(
- x => x.Decode(
- this.TopLevelConfiguration,
- It.Is(x => ((BufferedReadStream)x).BaseStream == stream)));
+ this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream));
}
[Fact]