Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/ImageSharp/Common/Extensions/StreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
#if !SUPPORTS_SPAN_STREAM
using System.Buffers;
#endif

namespace SixLabors.ImageSharp
{
Expand Down Expand Up @@ -40,7 +38,7 @@ public static int Read(this Stream stream, Span<byte> buffer, int offset, int co
/// Skips the number of bytes in the given stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="count">The count.</param>
/// <param name="count">A byte offset relative to the origin parameter.</param>
public static void Skip(this Stream stream, int count)
{
if (count < 1)
Expand All @@ -50,14 +48,16 @@ public static void Skip(this Stream stream, int count)

if (stream.CanSeek)
{
stream.Seek(count, SeekOrigin.Current); // Position += count;
stream.Seek(count, SeekOrigin.Current);
return;
}
else

byte[] buffer = ArrayPool<byte>.Shared.Rent(count);
try
{
var foo = new byte[count];
while (count > 0)
{
int bytesRead = stream.Read(foo, 0, count);
int bytesRead = stream.Read(buffer, 0, count);
if (bytesRead == 0)
{
break;
Expand All @@ -66,6 +66,10 @@ public static void Skip(this Stream stream, int count)
count -= bytesRead;
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}

public static void Read(this Stream stream, IManagedByteBuffer buffer)
Expand Down
35 changes: 21 additions & 14 deletions src/ImageSharp/Formats/Bmp/BmpDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

Expand All @@ -29,65 +30,71 @@ public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDe
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black;

/// <inheritdoc/>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(stream, nameof(stream));

var decoder = new BmpDecoderCore(configuration, this);

try
{
return await decoder.DecodeAsync<TPixel>(stream).ConfigureAwait(false);
using var bufferedStream = new BufferedReadStream(stream);
return decoder.Decode<TPixel>(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);
}
}

/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream)
=> this.Decode<Rgba32>(configuration, stream);

/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(stream, nameof(stream));

var decoder = new BmpDecoderCore(configuration, this);

try
{
return decoder.Decode<TPixel>(stream);
using var bufferedStream = new BufferedReadStream(stream);
return await decoder.DecodeAsync<TPixel>(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);
}
}

/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);

/// <inheritdoc />
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync<Rgba32>(configuration, stream).ConfigureAwait(false);
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream)
=> await this.DecodeAsync<Rgba32>(configuration, stream).ConfigureAwait(false);

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
{
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);
}
}
}
10 changes: 4 additions & 6 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,7 +60,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary>
/// The stream to decode from.
/// </summary>
private Stream stream;
private BufferedReadStream stream;

/// <summary>
/// The metadata.
Expand Down Expand Up @@ -120,7 +118,7 @@ public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height);

/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(Stream stream)
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
try
Expand Down Expand Up @@ -199,7 +197,7 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
}

/// <inheritdoc />
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);
Expand Down Expand Up @@ -1355,7 +1353,7 @@ private void ReadFileHeader()
/// </summary>
/// <returns>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.</returns>
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;

Expand Down
38 changes: 23 additions & 15 deletions src/ImageSharp/Formats/Gif/GifDecoder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -26,54 +26,66 @@ public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDe
public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All;

/// <inheritdoc/>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new GifDecoderCore(configuration, this);

try
{
return await decoder.DecodeAsync<TPixel>(stream).ConfigureAwait(false);
using var bufferedStream = new BufferedReadStream(stream);
return decoder.Decode<TPixel>(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;
}
}

/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream)
=> this.Decode<Rgba32>(configuration, stream);

/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new GifDecoderCore(configuration, this);

try
{
return decoder.Decode<TPixel>(stream);
using var bufferedStream = new BufferedReadStream(stream);
return await decoder.DecodeAsync<TPixel>(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;
}
}

/// <inheritdoc />
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream)
=> await this.DecodeAsync<Rgba32>(configuration, stream).ConfigureAwait(false);

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
Expand All @@ -82,13 +94,9 @@ public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream
Guard.NotNull(stream, nameof(stream));

var decoder = new GifDecoderCore(configuration, this);
return decoder.IdentifyAsync(stream);
}

/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);

/// <inheritdoc />
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync<Rgba32>(configuration, stream).ConfigureAwait(false);
using var bufferedStream = new BufferedReadStream(stream);
return decoder.IdentifyAsync(bufferedStream);
}
}
}
8 changes: 4 additions & 4 deletions src/ImageSharp/Formats/Gif/GifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// The currently loaded stream.
/// </summary>
private Stream stream;
private BufferedReadStream stream;

/// <summary>
/// The global color table.
Expand Down Expand Up @@ -97,7 +97,7 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;

/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(Stream stream)
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
Image<TPixel> image = null;
Expand Down Expand Up @@ -158,7 +158,7 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
}

/// <inheritdoc />
public IImageInfo Identify(Stream stream)
public IImageInfo Identify(BufferedReadStream stream)
{
try
{
Expand Down Expand Up @@ -572,7 +572,7 @@ private void SetFrameMetadata(ImageFrameMetadata meta)
/// Reads the logical screen descriptor and global color table blocks
/// </summary>
/// <param name="stream">The stream containing image data. </param>
private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream)
{
this.stream = stream;

Expand Down
9 changes: 4 additions & 5 deletions src/ImageSharp/Formats/Gif/LzwDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,7 +28,7 @@ internal sealed class LzwDecoder : IDisposable
/// <summary>
/// The stream to decode.
/// </summary>
private readonly Stream stream;
private readonly BufferedReadStream stream;

/// <summary>
/// The prefix buffer.
Expand All @@ -52,8 +51,8 @@ internal sealed class LzwDecoder : IDisposable
/// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="stream">The stream to read from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="stream"/> is null.</exception>
public LzwDecoder(MemoryAllocator memoryAllocator, Stream stream)
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream)
{
this.stream = stream ?? throw new ArgumentNullException(nameof(stream));

Expand Down
15 changes: 7 additions & 8 deletions src/ImageSharp/Formats/IImageDecoderInternals.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,18 +22,16 @@ internal interface IImageDecoderInternals
/// </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>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
/// <returns>The decoded image.</returns>
Image<TPixel> Decode<TPixel>(Stream stream)
Image<TPixel> Decode<TPixel>(BufferedReadStream 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>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/>.</returns>
IImageInfo Identify(Stream stream);
IImageInfo Identify(BufferedReadStream stream);
}
}
Loading