Skip to content

Commit

Permalink
Merge pull request #1296 from SixLabors/af/cancellation
Browse files Browse the repository at this point in the history
Cancellable codec API, and overloads for loading/saving
  • Loading branch information
JimBobSquarePants authored Aug 4, 2020
2 parents 586d99e + e707a8d commit 521fae3
Show file tree
Hide file tree
Showing 66 changed files with 1,936 additions and 936 deletions.
6 changes: 4 additions & 2 deletions src/ImageSharp/Advanced/AdvancedImageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -73,9 +74,10 @@ public static void AcceptVisitor(this Image source, IImageVisitor visitor)
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="visitor">The image visitor.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor)
=> source.AcceptAsync(visitor);
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor, CancellationToken cancellationToken = default)
=> source.AcceptAsync(visitor, cancellationToken);

/// <summary>
/// Gets the configuration for the image.
Expand Down
4 changes: 3 additions & 1 deletion src/ImageSharp/Advanced/IImageVisitor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

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

Expand Down Expand Up @@ -31,9 +32,10 @@ public interface IImageVisitorAsync
/// Provides a pixel-specific implementation for a given operation.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task VisitAsync<TPixel>(Image<TPixel> image)
Task VisitAsync<TPixel>(Image<TPixel> image, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp
{
Expand Down Expand Up @@ -32,5 +33,10 @@ public InvalidImageContentException(string errorMessage, Exception innerExceptio
: base(errorMessage, innerException)
{
}

internal InvalidImageContentException(Size size, InvalidMemoryOperationException memoryException)
: this($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {size.Width}x{size.Height}.", memoryException)
{
}
}
}
42 changes: 10 additions & 32 deletions src/ImageSharp/Formats/Bmp/BmpDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -36,65 +37,42 @@ public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
Guard.NotNull(stream, nameof(stream));

var decoder = new BmpDecoderCore(configuration, this);

try
{
using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.Decode<TPixel>(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;

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);
}
return decoder.Decode<TPixel>(configuration, stream);
}

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

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

var decoder = new BmpDecoderCore(configuration, this);

try
{
using var bufferedStream = new BufferedReadStream(configuration, stream);
return await decoder.DecodeAsync<TPixel>(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;

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);
}
return decoder.DecodeAsync<TPixel>(configuration, stream, cancellationToken);
}

/// <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, CancellationToken cancellationToken)
=> await this.DecodeAsync<Rgba32>(configuration, stream, cancellationToken)
.ConfigureAwait(false);

/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));

using var bufferedStream = new BufferedReadStream(configuration, stream);
return new BmpDecoderCore(configuration, this).Identify(bufferedStream);
return new BmpDecoderCore(configuration, this).Identify(configuration, stream);
}

/// <inheritdoc/>
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));

using var bufferedStream = new BufferedReadStream(configuration, stream);
return new BmpDecoderCore(configuration, this).IdentifyAsync(bufferedStream);
return new BmpDecoderCore(configuration, this).IdentifyAsync(configuration, stream, cancellationToken);
}
}
}
5 changes: 3 additions & 2 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -118,7 +119,7 @@ public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height);

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

/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream)
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Expand Down
5 changes: 3 additions & 2 deletions src/ImageSharp/Formats/Bmp/BmpEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
Expand Down Expand Up @@ -42,11 +43,11 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
}

/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new BmpEncoderCore(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream);
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}
}
30 changes: 4 additions & 26 deletions src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
Expand All @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary>
internal sealed class BmpEncoderCore
internal sealed class BmpEncoderCore : IImageEncoderInternals
{
/// <summary>
/// The amount to pad each row by.
Expand Down Expand Up @@ -97,32 +98,9 @@ public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocato
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public async Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
/// <param name="cancellationToken">The token to request cancellation.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
this.Encode(image, stream);
}
else
{
using (var ms = new MemoryStream())
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
}
}
}

/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
Expand Down
48 changes: 10 additions & 38 deletions src/ImageSharp/Formats/Gif/GifDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -30,52 +31,25 @@ public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new GifDecoderCore(configuration, this);

try
{
using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.Decode<TPixel>(bufferedStream);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;

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;
}
return decoder.Decode<TPixel>(configuration, stream);
}

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

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

try
{
using var bufferedStream = new BufferedReadStream(configuration, stream);
return await decoder.DecodeAsync<TPixel>(bufferedStream).ConfigureAwait(false);
}
catch (InvalidMemoryOperationException ex)
{
Size dims = decoder.Dimensions;

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;
}
return decoder.DecodeAsync<TPixel>(configuration, stream, cancellationToken);
}

/// <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, CancellationToken cancellationToken)
=> await this.DecodeAsync<Rgba32>(configuration, stream, cancellationToken)
.ConfigureAwait(false);

/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
Expand All @@ -85,18 +59,16 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
var decoder = new GifDecoderCore(configuration, this);

using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.Identify(bufferedStream);
return decoder.Identify(bufferedStream, default);
}

/// <inheritdoc/>
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));

var decoder = new GifDecoderCore(configuration, this);

using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.IdentifyAsync(bufferedStream);
return decoder.IdentifyAsync(configuration, stream, cancellationToken);
}
}
}
5 changes: 3 additions & 2 deletions src/ImageSharp/Formats/Gif/GifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -97,7 +98,7 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;

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

/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream)
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
try
{
Expand Down
5 changes: 3 additions & 2 deletions src/ImageSharp/Formats/Gif/GifEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
Expand Down Expand Up @@ -41,11 +42,11 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
}

/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new GifEncoderCore(image.GetConfiguration(), this);
return encoder.EncodeAsync(image, stream);
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}
}
Loading

0 comments on commit 521fae3

Please sign in to comment.