Browse Source

Refactor Identify and DetectFormat and fix tests

pull/2317/head
James Jackson-South 3 years ago
parent
commit
0aa12a4602
  1. 24
      src/ImageSharp/Common/Attempt{T}.cs
  2. 18
      src/ImageSharp/Formats/ImageDecoder.cs
  3. 79
      src/ImageSharp/Image.Decode.cs
  4. 49
      src/ImageSharp/Image.FromBytes.cs
  5. 134
      src/ImageSharp/Image.FromFile.cs
  6. 210
      src/ImageSharp/Image.FromStream.cs
  7. 16
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  8. 33
      tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
  9. 12
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  10. 18
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  11. 23
      tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs
  12. 18
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  13. 46
      tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
  14. 6
      tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs
  15. 50
      tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs
  16. 40
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  17. 28
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  18. 14
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  19. 2
      tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs
  20. 55
      tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs
  21. 152
      tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
  22. 14
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
  23. 3
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  24. 8
      tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs

24
src/ImageSharp/Common/Attempt{T}.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp;
/// <summary>
/// A wrapper for nullable values that correctly handles the return type based on the result.
/// </summary>
/// <typeparam name="T">The type of nullable value.</typeparam>
public readonly struct Attempt<T>
{
/// <summary>
/// Gets a value indicating whether the attempted return was successful.
/// </summary>
[MemberNotNullWhen(returnValue: true, member: nameof(Value))]
public bool Success => this.Value is not null;
/// <summary>
/// Gets the value when the attempted return is successful; otherwise, the default value for the type.
/// </summary>
public T? Value { get; init; }
}

18
src/ImageSharp/Formats/ImageDecoder.cs

@ -84,12 +84,18 @@ public abstract class ImageDecoder : IImageDecoder
}
/// <inheritdoc/>
public Task<ImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Identify(options, s, ct),
cancellationToken);
public async Task<ImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{
ImageInfo info = await WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Identify(options, s, ct),
cancellationToken).ConfigureAwait(false);
this.SetDecoderFormat(options.Configuration, info);
return info;
}
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}" /> of a specific pixel type.

79
src/ImageSharp/Image.Decode.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
@ -45,7 +44,7 @@ public abstract partial class Image
/// <param name="configuration">The general configuration.</param>
/// <param name="stream">The image stream to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
private static IImageFormat InternalDetectFormat(Configuration configuration, Stream stream)
private static IImageFormat? InternalDetectFormat(Configuration configuration, Stream stream)
{
// We take a minimum of the stream length vs the max header size and always check below
// to ensure that only formats that headers fit within the given buffer length are tested.
@ -77,15 +76,12 @@ public abstract partial class Image
// Does the given stream contain enough data to fit in the header for the format
// and does that data match the format specification?
// Individual formats should still check since they are public.
IImageFormat format = null;
IImageFormat? format = null;
foreach (IImageFormatDetector formatDetector in configuration.ImageFormatsManager.FormatDetectors)
{
if (formatDetector.HeaderSize <= headerSize)
if (formatDetector.HeaderSize <= headerSize && formatDetector.TryDetectFormat(headersBuffer, out IImageFormat? attemptFormat))
{
if (formatDetector.TryDetectFormat(headersBuffer, out IImageFormat attemptFormat))
{
format = attemptFormat;
}
format = attemptFormat;
}
}
@ -97,13 +93,11 @@ public abstract partial class Image
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="format">The IImageFormat.</param>
/// <returns>The image format or null if none found.</returns>
private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format)
/// <returns>The <see cref="IImageDecoder"/> or <see langword="null"/>.</returns>
private static IImageDecoder? DiscoverDecoder(DecoderOptions options, Stream stream)
{
format = InternalDetectFormat(options.Configuration, stream);
return format != null
IImageFormat? format = InternalDetectFormat(options.Configuration, stream);
return format is not null
? options.Configuration.ImageFormatsManager.FindDecoder(format)
: null;
}
@ -117,60 +111,56 @@ public abstract partial class Image
/// <returns>
/// A new <see cref="Image{TPixel}"/>.
/// </returns>
private static (Image<TPixel> Image, IImageFormat Format) Decode<TPixel>(DecoderOptions options, Stream stream)
private static Image<TPixel>? Decode<TPixel>(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return (null, null);
return null;
}
Image<TPixel> img = decoder.Decode<TPixel>(options, stream);
return (img, format);
return decoder.Decode<TPixel>(options, stream);
}
private static async Task<(Image<TPixel> Image, IImageFormat Format)> DecodeAsync<TPixel>(
private static async Task<Image<TPixel>?> DecodeAsync<TPixel>(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return (null, null);
return null;
}
Image<TPixel> img = await decoder.DecodeAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
return await decoder.DecodeAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
}
private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream)
private static Image? Decode(DecoderOptions options, Stream stream)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return (null, null);
return null;
}
Image img = decoder.Decode(options, stream);
return (img, format);
return decoder.Decode(options, stream);
}
private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(
private static async Task<Image?> DecodeAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return (null, null);
return null;
}
Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
return await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@ -181,11 +171,15 @@ public abstract partial class Image
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static (ImageInfo ImageInfo, IImageFormat Format) InternalIdentify(DecoderOptions options, Stream stream)
private static ImageInfo? InternalIdentify(DecoderOptions options, Stream stream)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
ImageInfo info = decoder?.Identify(options, stream);
return (info, format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return null;
}
return decoder.Identify(options, stream);
}
/// <summary>
@ -197,19 +191,18 @@ public abstract partial class Image
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static async Task<(ImageInfo ImageInfo, IImageFormat Format)> InternalIdentifyAsync(
private static async Task<ImageInfo?> InternalIdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageDecoder? decoder = DiscoverDecoder(options, stream);
if (decoder is null)
{
return (null, null);
return null;
}
ImageInfo info = await decoder.IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (info, format);
return await decoder.IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
}

49
src/ImageSharp/Image.FromBytes.cs

@ -55,46 +55,41 @@ public abstract partial class Image
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="data">The byte span containing encoded image data to read the header from.</param>
/// <exception cref="ArgumentNullException">The data is null.</exception>
/// <exception cref="NotSupportedException">The data is not readable.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static ImageInfo Identify(ReadOnlySpan<byte> data) => Identify(data, out IImageFormat _);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The data is null.</exception>
/// <exception cref="NotSupportedException">The data is not readable.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static ImageInfo Identify(ReadOnlySpan<byte> data, out IImageFormat format)
=> Identify(DecoderOptions.Default, data, out format);
public static bool TryIdentify(ReadOnlySpan<byte> data, [NotNullWhen(true)] out ImageInfo? info)
=> TryIdentify(DecoderOptions.Default, data, out info);
/// <summary>
/// Reads the raw image information from the specified span of bytes without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="data">The byte span containing encoded image data to read the header from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The data is null.</exception>
/// <exception cref="NotSupportedException">The data is not readable.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static unsafe ImageInfo Identify(DecoderOptions options, ReadOnlySpan<byte> data, out IImageFormat format)
public static unsafe bool TryIdentify(DecoderOptions options, ReadOnlySpan<byte> data, [NotNullWhen(true)] out ImageInfo? info)
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
return Identify(options, stream, out format);
using UnmanagedMemoryStream stream = new(ptr, data.Length);
return TryIdentify(options, stream, out info);
}
}
@ -141,7 +136,7 @@ public abstract partial class Image
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
using UnmanagedMemoryStream stream = new(ptr, data.Length);
return Load<TPixel>(options, stream);
}
}
@ -166,7 +161,7 @@ public abstract partial class Image
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
using UnmanagedMemoryStream stream = new(ptr, data.Length);
return Load<TPixel>(options, stream, out format);
}
}
@ -222,7 +217,7 @@ public abstract partial class Image
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
using UnmanagedMemoryStream stream = new(ptr, data.Length);
return Load(options, stream, out format);
}
}

134
src/ImageSharp/Image.FromFile.cs

@ -13,22 +13,32 @@ namespace SixLabors.ImageSharp;
public abstract partial class Image
{
/// <summary>
/// By reading the header on the provided file this calculates the images mime type.
/// Detects the encoded image format type from the specified file.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="format">The mime type or null if none found.</param>
/// <returns>returns true when format was detected otherwise false.</returns>
/// <param name="format">
/// When this method returns, contains the format that matches the given file;
/// otherwise, the default value for the type of the <paramref name="format"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if a match is found; otherwise, <see langword="false"/></returns>
public static bool TryDetectFormat(string filePath, [NotNullWhen(true)] out IImageFormat? format)
=> TryDetectFormat(DecoderOptions.Default, filePath, out format);
/// <summary>
/// By reading the header on the provided file this calculates the images mime type.
/// Detects the encoded image format type from the specified file.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="format">The mime type or null if none found.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>returns true when format was detected otherwise false.</returns>
/// <param name="format">
/// When this method returns, contains the format that matches the given file;
/// otherwise, the default value for the type of the <paramref name="format"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if a match is found; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
public static bool TryDetectFormat(DecoderOptions options, string filePath, [NotNullWhen(true)] out IImageFormat? format)
{
Guard.NotNull(options, nameof(options));
@ -38,112 +48,106 @@ public abstract partial class Image
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// Detects the encoded image format type from the specified file.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static ImageInfo Identify(string filePath)
=> Identify(filePath, out IImageFormat _);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static ImageInfo Identify(string filePath, out IImageFormat format)
=> Identify(DecoderOptions.Default, filePath, out format);
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Attempt}"/> representing the asynchronous operation.</returns>
public static Task<Attempt<IImageFormat>> TryDetectFormatAsync(
string filePath,
CancellationToken cancellationToken = default)
=> TryDetectFormatAsync(DecoderOptions.Default, filePath, cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// Detects the encoded image format type from the specified file.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static ImageInfo Identify(DecoderOptions options, string filePath, out IImageFormat format)
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <returns>A <see cref="Task{Attempt}"/> representing the asynchronous operation.</returns>
public static async Task<Attempt<IImageFormat>> TryDetectFormatAsync(
DecoderOptions options,
string filePath,
CancellationToken cancellationToken = default)
{
Guard.NotNull(options, nameof(options));
using Stream file = options.Configuration.FileSystem.OpenRead(filePath);
return Identify(options, file, out format);
using Stream stream = options.Configuration.FileSystem.OpenRead(filePath);
return await TryDetectFormatAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// Reads the raw image information from the specified file path without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// </returns>
public static Task<ImageInfo> IdentifyAsync(string filePath, CancellationToken cancellationToken = default)
=> IdentifyAsync(DecoderOptions.Default, filePath, cancellationToken);
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
public static bool TryIdentify(string filePath, [NotNullWhen(true)] out ImageInfo? info)
=> TryIdentify(DecoderOptions.Default, filePath, out info);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// Reads the raw image information from the specified file path without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// </returns>
public static async Task<ImageInfo> IdentifyAsync(
DecoderOptions options,
string filePath,
CancellationToken cancellationToken = default)
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
public static bool TryIdentify(DecoderOptions options, string filePath, [NotNullWhen(true)] out ImageInfo? info)
{
(ImageInfo ImageInfo, IImageFormat Format) res =
await IdentifyWithFormatAsync(options, filePath, cancellationToken).ConfigureAwait(false);
return res.ImageInfo;
Guard.NotNull(options, nameof(options));
using Stream stream = options.Configuration.FileSystem.OpenRead(filePath);
return TryIdentify(options, stream, out info);
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{Attempt}"/> representing the asynchronous operation.
/// </returns>
public static Task<(ImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
public static Task<Attempt<ImageInfo>> TryIdentifyAsync(
string filePath,
CancellationToken cancellationToken = default)
=> IdentifyWithFormatAsync(DecoderOptions.Default, filePath, cancellationToken);
=> TryIdentifyAsync(DecoderOptions.Default, filePath, cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{Attempt}"/> representing the asynchronous operation.
/// </returns>
public static async Task<(ImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
public static async Task<Attempt<ImageInfo>> TryIdentifyAsync(
DecoderOptions options,
string filePath,
CancellationToken cancellationToken = default)
{
Guard.NotNull(options, nameof(options));
using Stream stream = options.Configuration.FileSystem.OpenRead(filePath);
return await IdentifyWithFormatAsync(options, stream, cancellationToken)
.ConfigureAwait(false);
return await TryIdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
/// <summary>

210
src/ImageSharp/Image.FromStream.cs

@ -16,45 +16,59 @@ namespace SixLabors.ImageSharp;
public abstract partial class Image
{
/// <summary>
/// By reading the header on the provided stream this calculates the images format type.
/// Detects the encoded image format type from the specified stream.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="format">The format type or null if none found.</param>
/// <param name="format">
/// When this method returns, contains the format that matches the given stream;
/// otherwise, the default value for the type of the <paramref name="format"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if a match is found; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <returns>returns true when format was detected otherwise false.</returns>
public static bool TryDetectFormat(Stream stream, [NotNullWhen(true)] out IImageFormat? format)
=> TryDetectFormat(DecoderOptions.Default, stream, out format);
/// <summary>
/// By reading the header on the provided stream this calculates the images format type.
/// Detects the encoded image format type from the specified stream.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="format">The format type or null if none found.</param>
/// <param name="format">
/// When this method returns, contains the format that matches the given stream;
/// otherwise, the default value for the type of the <paramref name="format"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if a match is found; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <returns>returns true when format was detected otherwise false.</returns>
public static bool TryDetectFormat(DecoderOptions options, Stream stream, [NotNullWhen(true)] out IImageFormat? format)
{
format = WithSeekableStream(options, stream, s => InternalDetectFormat(options.Configuration, s));
return format != null;
return format is not null;
}
/// <summary>
/// By reading the header on the provided stream this calculates the images format type.
/// Detects the encoded image format type from the specified stream.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation or null if none is found.</returns>
public static Task<IImageFormat> DetectFormatAsync(Stream stream, CancellationToken cancellationToken = default)
=> DetectFormatAsync(DecoderOptions.Default, stream, cancellationToken);
/// <returns>A <see cref="Task{Attempt}"/> representing the asynchronous operation.</returns>
public static Task<Attempt<IImageFormat>> TryDetectFormatAsync(
Stream stream,
CancellationToken cancellationToken = default)
=> TryDetectFormatAsync(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// By reading the header on the provided stream this calculates the images format type.
/// Detects the encoded image format type from the specified stream.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the header from.</param>
@ -62,117 +76,63 @@ public abstract partial class Image
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</returns>
public static Task<IImageFormat> DetectFormatAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableStreamAsync(
/// <returns>A <see cref="Task{Attempt}"/> representing the asynchronous operation.</returns>
public static async Task<Attempt<IImageFormat>> TryDetectFormatAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
{
IImageFormat? format = await WithSeekableStreamAsync(
options,
stream,
(s, _) => Task.FromResult(InternalDetectFormat(options.Configuration, s)),
cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static ImageInfo Identify(Stream stream)
=> Identify(stream, out IImageFormat _);
cancellationToken).ConfigureAwait(false);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// A <see cref="Task{IImageInfo}"/> representing the asynchronous operation or null if
/// a suitable detector is not found.
/// </returns>
public static Task<ImageInfo> IdentifyAsync(Stream stream, CancellationToken cancellationToken = default)
=> IdentifyAsync(DecoderOptions.Default, stream, cancellationToken);
return new() { Value = format };
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static ImageInfo Identify(Stream stream, out IImageFormat format)
=> Identify(DecoderOptions.Default, stream, out format);
public static bool TryIdentify(Stream stream, [NotNullWhen(true)] out ImageInfo? info)
=> TryIdentify(DecoderOptions.Default, stream, out info);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the information from.</param>
/// <param name="info">
/// When this method returns, contains the raw image information;
/// otherwise, the default value for the type of the <paramref name="info"/> parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static ImageInfo Identify(DecoderOptions options, Stream stream)
=> Identify(options, stream, out _);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the information from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// A <see cref="Task{IImageInfo}"/> representing the asynchronous operation or null if
/// a suitable detector is not found.
/// </returns>
public static async Task<ImageInfo> IdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
public static bool TryIdentify(DecoderOptions options, Stream stream, [NotNullWhen(true)] out ImageInfo? info)
{
(ImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false);
return res.ImageInfo;
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the information from.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="ImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static ImageInfo Identify(DecoderOptions options, Stream stream, out IImageFormat format)
{
(ImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentify(options, s));
format = data.Format;
return data.ImageInfo;
info = WithSeekableStream(options, stream, s => InternalIdentify(options, s));
return info is not null;
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="stream">The image stream to read the information from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
@ -181,16 +141,16 @@ public abstract partial class Image
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{Attempt}"/> representing the asynchronous operation.
/// </returns>
public static Task<(ImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
public static Task<Attempt<ImageInfo>> TryIdentifyAsync(
Stream stream,
CancellationToken cancellationToken = default)
=> IdentifyWithFormatAsync(DecoderOptions.Default, stream, cancellationToken);
=> TryIdentifyAsync(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// A return value indicates whether the operation succeeded.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the information from.</param>
@ -200,18 +160,21 @@ public abstract partial class Image
/// <exception cref="NotSupportedException">The stream is not readable.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="ImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{Attempt}"/> representing the asynchronous operation.
/// </returns>
public static Task<(ImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
public static async Task<Attempt<ImageInfo>> TryIdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
=> WithSeekableStreamAsync(
{
ImageInfo? info = await WithSeekableStreamAsync(
options,
stream,
(s, ct) => InternalIdentifyAsync(options, s, ct),
cancellationToken);
cancellationToken).ConfigureAwait(false);
return new() { Value = info };
}
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
@ -387,16 +350,15 @@ public abstract partial class Image
public static Image<TPixel> Load<TPixel>(DecoderOptions options, Stream stream, out IImageFormat format)
where TPixel : unmanaged, IPixel<TPixel>
{
(Image<TPixel> Image, IImageFormat Format) data = WithSeekableStream(options, stream, s => Decode<TPixel>(options, s));
Image<TPixel>? image = WithSeekableStream(options, stream, s => Decode<TPixel>(options, s));
format = data.Format;
if (data.Image is null)
if (image is null)
{
ThrowNotLoaded(options);
}
return data.Image;
format = image.Metadata.DecodedImageFormat!;
return image;
}
/// <summary>
@ -416,16 +378,15 @@ public abstract partial class Image
Stream stream,
CancellationToken cancellationToken = default)
{
(Image Image, IImageFormat Format) data =
await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
Image? image = await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
if (image is null)
{
ThrowNotLoaded(options);
}
return data;
return new(image, image.Metadata.DecodedImageFormat!);
}
/// <summary>
@ -447,16 +408,15 @@ public abstract partial class Image
CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
{
(Image<TPixel> Image, IImageFormat Format) data =
await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync<TPixel>(options, s, ct), cancellationToken)
.ConfigureAwait(false);
Image<TPixel>? image = await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync<TPixel>(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
if (image is null)
{
ThrowNotLoaded(options);
}
return data;
return new(image, image.Metadata.DecodedImageFormat!);
}
/// <summary>
@ -498,16 +458,14 @@ public abstract partial class Image
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image Load(DecoderOptions options, Stream stream, out IImageFormat format)
{
(Image img, IImageFormat fmt) = WithSeekableStream(options, stream, s => Decode(options, s));
format = fmt;
if (img is null)
Image? image = WithSeekableStream(options, stream, s => Decode(options, s));
if (image is null)
{
ThrowNotLoaded(options);
}
return img;
format = image.Metadata.DecodedImageFormat!;
return image;
}
/// <summary>

16
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -473,9 +473,9 @@ public class BmpDecoderTests
[InlineData(Bit1Pal1, 1)]
public void Identify_DetectsCorrectPixelType(string imagePath, int expectedPixelSize)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel);
}
@ -491,9 +491,9 @@ public class BmpDecoderTests
[InlineData(RLE8Inverted, 491, 272)]
public void Identify_DetectsCorrectWidthAndHeight(string imagePath, int expectedWidth, int expectedHeight)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedHeight, imageInfo.Height);
@ -503,8 +503,8 @@ public class BmpDecoderTests
[MemberData(nameof(RatioFiles))]
public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
using Image<Rgba32> image = BmpDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);

33
tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs

@ -14,8 +14,9 @@ public class BmpMetadataTests
[Fact]
public void CloneIsDeep()
{
var meta = new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 };
var clone = (BmpMetadata)meta.DeepClone();
BmpMetadata meta = new()
{ BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 };
BmpMetadata clone = (BmpMetadata)meta.DeepClone();
clone.BitsPerPixel = BmpBitsPerPixel.Pixel32;
clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2;
@ -35,15 +36,13 @@ public class BmpMetadataTests
[InlineData(Os2v2, BmpInfoHeaderType.Os2Version2)]
public void Identify_DetectsCorrectBitmapInfoHeaderType(string imagePath, BmpInfoHeaderType expectedInfoHeaderType)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
ImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
BmpMetadata bitmapMetadata = imageInfo.Metadata.GetBmpMetadata();
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedInfoHeaderType, bitmapMetadata.InfoHeaderType);
}
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
BmpMetadata bitmapMetadata = imageInfo.Metadata.GetBmpMetadata();
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedInfoHeaderType, bitmapMetadata.InfoHeaderType);
}
[Theory]
@ -51,12 +50,10 @@ public class BmpMetadataTests
public void Decoder_CanReadColorProfile<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(BmpDecoder.Instance))
{
ImageSharp.Metadata.ImageMetadata metaData = image.Metadata;
Assert.NotNull(metaData);
Assert.NotNull(metaData.IccProfile);
Assert.Equal(16, metaData.IccProfile.Entries.Length);
}
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
ImageSharp.Metadata.ImageMetadata metaData = image.Metadata;
Assert.NotNull(metaData);
Assert.NotNull(metaData.IccProfile);
Assert.Equal(16, metaData.IccProfile.Entries.Length);
}
}

12
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities;
namespace SixLabors.ImageSharp.Tests.Formats;
@ -257,15 +256,11 @@ public class GeneralFormatTests
image.Save(memoryStream, format);
memoryStream.Position = 0;
ImageInfo imageInfo = Image.Identify(memoryStream);
Image.TryIdentify(memoryStream, out ImageInfo imageInfo);
Assert.Equal(imageInfo.Width, width);
Assert.Equal(imageInfo.Height, height);
memoryStream.Position = 0;
imageInfo = Image.Identify(memoryStream, out IImageFormat detectedFormat);
Assert.Equal(format, detectedFormat);
Assert.Equal(format, imageInfo.Metadata.DecodedImageFormat);
}
[Fact]
@ -274,10 +269,9 @@ public class GeneralFormatTests
byte[] invalid = new byte[10];
using MemoryStream memoryStream = new(invalid);
ImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format);
Image.TryIdentify(memoryStream, out ImageInfo imageInfo);
Assert.Null(imageInfo);
Assert.Null(format);
}
private static IImageFormat GetFormat(string format)

18
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -59,13 +59,13 @@ public class GifDecoderTests
[Fact]
public unsafe void Decode_NonTerminatedFinalFrame()
{
var testFile = TestFile.Create(TestImages.Gif.Rings);
TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
int length = testFile.Bytes.Length - 2;
fixed (byte* data = testFile.Bytes.AsSpan(0, length))
{
using var stream = new UnmanagedMemoryStream(data, length);
using UnmanagedMemoryStream stream = new(data, length);
using Image<Rgba32> image = GifDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
Assert.Equal((200, 200), (image.Width, image.Height));
}
@ -120,9 +120,13 @@ public class GifDecoderTests
[InlineData(TestImages.Gif.Trans, 8)]
public void DetectPixelSize(string imagePath, int expectedPixelSize)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
}
[Theory]
@ -155,9 +159,9 @@ public class GifDecoderTests
[Fact]
public void CanDecodeIntermingledImages()
{
using (var kumin1 = Image.Load<Rgba32>(TestFile.Create(TestImages.Gif.Kumin).Bytes))
using (Image<Rgba32> kumin1 = Image.Load<Rgba32>(TestFile.Create(TestImages.Gif.Kumin).Bytes))
using (Image.Load(TestFile.Create(TestImages.Png.Icon).Bytes))
using (var kumin2 = Image.Load<Rgba32>(TestFile.Create(TestImages.Gif.Kumin).Bytes))
using (Image<Rgba32> kumin2 = Image.Load<Rgba32>(TestFile.Create(TestImages.Gif.Kumin).Bytes))
{
for (int i = 0; i < kumin1.Frames.Count; i++)
{

23
tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs

@ -13,8 +13,9 @@ public class PbmMetadataTests
[Fact]
public void CloneIsDeep()
{
var meta = new PbmMetadata { ColorType = PbmColorType.Grayscale };
var clone = (PbmMetadata)meta.DeepClone();
PbmMetadata meta = new()
{ ColorType = PbmColorType.Grayscale };
PbmMetadata clone = (PbmMetadata)meta.DeepClone();
clone.ColorType = PbmColorType.Rgb;
clone.ComponentType = PbmComponentType.Short;
@ -33,9 +34,9 @@ public class PbmMetadataTests
[InlineData(RgbPlain, PbmEncoding.Plain)]
public void Identify_DetectsCorrectEncoding(string imagePath, PbmEncoding expectedEncoding)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);
@ -52,9 +53,9 @@ public class PbmMetadataTests
[InlineData(RgbPlain, PbmColorType.Rgb)]
public void Identify_DetectsCorrectColorType(string imagePath, PbmColorType expectedColorType)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);
@ -71,9 +72,9 @@ public class PbmMetadataTests
[InlineData(RgbPlain, PbmComponentType.Byte)]
public void Identify_DetectsCorrectComponentType(string imagePath, PbmComponentType expectedComponentType)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);

18
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -82,7 +82,7 @@ public partial class PngDecoderTests
public void Decode_NonGeneric_CreatesCorrectImageType(string path, Type type)
{
string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, path);
using var image = Image.Load(file);
using Image image = Image.Load(file);
Assert.IsType(type, image);
}
@ -304,9 +304,13 @@ public partial class PngDecoderTests
[InlineData(TestImages.Png.Rgb48BppInterlaced, 48)]
public void Identify(string imagePath, int expectedPixelSize)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
}
[Theory]
@ -502,9 +506,9 @@ public partial class PngDecoderTests
[InlineData(TestImages.Png.Issue2209IndexedWithTransparency)]
public void Issue2209_Identify_HasTransparencyIsTrue(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
PngMetadata metadata = imageInfo.Metadata.GetPngMetadata();
Assert.True(metadata.HasTransparency);
}

46
tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs

@ -23,7 +23,7 @@ public class PngMetadataTests
[Fact]
public void CloneIsDeep()
{
var meta = new PngMetadata
PngMetadata meta = new()
{
BitDepth = PngBitDepth.Bit16,
ColorType = PngColorType.GrayscaleWithAlpha,
@ -32,7 +32,7 @@ public class PngMetadataTests
TextData = new List<PngTextData> { new PngTextData("name", "value", "foo", "bar") }
};
var clone = (PngMetadata)meta.DeepClone();
PngMetadata clone = (PngMetadata)meta.DeepClone();
clone.BitDepth = PngBitDepth.Bit2;
clone.ColorType = PngColorType.Palette;
@ -63,7 +63,7 @@ public class PngMetadataTests
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> input = provider.GetImage(PngDecoder.Instance);
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
input.Save(memoryStream, new PngEncoder());
memoryStream.Position = 0;
@ -93,13 +93,13 @@ public class PngMetadataTests
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> input = provider.GetImage(PngDecoder.Instance);
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
// This will be a zTXt chunk.
var expectedText = new PngTextData("large-text", new string('c', 100), string.Empty, string.Empty);
PngTextData expectedText = new("large-text", new string('c', 100), string.Empty, string.Empty);
// This will be a iTXt chunk.
var expectedTextNoneLatin = new PngTextData("large-text-non-latin", new string('Ф', 100), "language-tag", "translated-keyword");
PngTextData expectedTextNoneLatin = new("large-text-non-latin", new string('Ф', 100), "language-tag", "translated-keyword");
PngMetadata inputMetadata = input.Metadata.GetFormatMetadata(PngFormat.Instance);
inputMetadata.TextData.Add(expectedText);
inputMetadata.TextData.Add(expectedTextNoneLatin);
@ -153,7 +153,7 @@ public class PngMetadataTests
SkipMetadata = false
};
var testFile = TestFile.Create(TestImages.Png.Blur);
TestFile testFile = TestFile.Create(TestImages.Png.Blur);
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance, options);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
@ -172,7 +172,7 @@ public class PngMetadataTests
SkipMetadata = true
};
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata);
TestFile testFile = TestFile.Create(TestImages.Png.PngWithMetadata);
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance, options);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
@ -183,8 +183,8 @@ public class PngMetadataTests
[MemberData(nameof(RatioFiles))]
public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
using Image<Rgba32> image = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
@ -201,11 +201,11 @@ public class PngMetadataTests
ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile;
byte[] expectedProfileBytes = expectedProfile.ToByteArray();
using var memStream = new MemoryStream();
using MemoryStream memStream = new();
input.Save(memStream, new PngEncoder());
memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile;
byte[] actualProfileBytes = actualProfile.ToByteArray();
@ -217,8 +217,8 @@ public class PngMetadataTests
[MemberData(nameof(RatioFiles))]
public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = PngDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
@ -230,9 +230,9 @@ public class PngMetadataTests
[InlineData(TestImages.Png.PngWithMetadata)]
public void Identify_ReadsTextData(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance);
VerifyTextDataIsPresent(meta);
@ -242,9 +242,9 @@ public class PngMetadataTests
[InlineData(TestImages.Png.PngWithMetadata)]
public void Identify_ReadsExifData(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.NotNull(imageInfo.Metadata.ExifProfile);
ExifProfile exif = imageInfo.Metadata.ExifProfile;
@ -279,9 +279,9 @@ public class PngMetadataTests
[InlineData(TestImages.Png.Issue1875)]
public void Identify_ReadsLegacyExifData(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.NotNull(imageInfo.Metadata.ExifProfile);

6
tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs

@ -22,7 +22,7 @@ public class TgaFileHeaderTests
[InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 32, 8 })] // invalid height
public void ImageLoad_WithNoValidTgaHeaderBytes_Throws_UnknownImageFormatException(byte[] data)
{
using var stream = new MemoryStream(data);
using MemoryStream stream = new(data);
Assert.Throws<UnknownImageFormatException>(() =>
{
@ -39,9 +39,9 @@ public class TgaFileHeaderTests
[InlineData(new byte[] { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 124, 0, 24, 32 }, 124, 124, TgaBitsPerPixel.Pixel24)]
public void Identify_WithValidData_Works(byte[] data, int width, int height, TgaBitsPerPixel bitsPerPixel)
{
using var stream = new MemoryStream(data);
using MemoryStream stream = new(data);
ImageInfo info = Image.Identify(stream);
Image.TryIdentify(stream, out ImageInfo info);
TgaMetadata tgaData = info.Metadata.GetTgaMetadata();
Assert.Equal(bitsPerPixel, tgaData.BitsPerPixel);
Assert.Equal(width, info.Width);

50
tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs

@ -60,23 +60,21 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester
[InlineData(MinIsBlack, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)]
public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
ImageInfo info = Image.Identify(stream);
Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel);
Assert.Equal(expectedWidth, info.Width);
Assert.Equal(expectedHeight, info.Height);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution);
Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution);
Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits);
TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata();
Assert.NotNull(tiffmeta);
Assert.Equal(TiffFormatType.BigTIFF, tiffmeta.FormatType);
}
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo info);
Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel);
Assert.Equal(expectedWidth, info.Width);
Assert.Equal(expectedHeight, info.Height);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution);
Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution);
Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits);
TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata();
Assert.NotNull(tiffmeta);
Assert.Equal(TiffFormatType.BigTIFF, tiffmeta.FormatType);
}
[Theory]
@ -84,19 +82,17 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester
[InlineData(BigTIFFMotorola, ImageSharp.ByteOrder.BigEndian)]
public void ByteOrder(string imagePath, ByteOrder expectedByteOrder)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
ImageInfo info = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo info);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder);
stream.Seek(0, SeekOrigin.Begin);
stream.Seek(0, SeekOrigin.Begin);
using var img = Image.Load(stream);
Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder);
}
using Image img = Image.Load(stream);
Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder);
}
[Theory]

40
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -30,19 +30,17 @@ public class TiffDecoderTests : TiffDecoderBaseTester
[InlineData(Flower4BitPalette, 4, 73, 43, 72, 72, PixelResolutionUnit.PixelsPerInch)]
public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
ImageInfo info = Image.Identify(stream);
Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel);
Assert.Equal(expectedWidth, info.Width);
Assert.Equal(expectedHeight, info.Height);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution);
Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution);
Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits);
}
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo info);
Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel);
Assert.Equal(expectedWidth, info.Width);
Assert.Equal(expectedHeight, info.Height);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution);
Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution);
Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits);
}
[Theory]
@ -51,18 +49,16 @@ public class TiffDecoderTests : TiffDecoderBaseTester
public void ByteOrder(string imagePath, ByteOrder expectedByteOrder)
{
TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
ImageInfo info = Image.Identify(stream);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo info);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder);
stream.Seek(0, SeekOrigin.Begin);
stream.Seek(0, SeekOrigin.Begin);
using Image img = Image.Load(stream);
Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder);
}
using Image img = Image.Load(stream);
Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder);
}
[Theory]

28
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -27,12 +27,12 @@ public class TiffMetadataTests
[Fact]
public void TiffMetadata_CloneIsDeep()
{
var meta = new TiffMetadata
TiffMetadata meta = new()
{
ByteOrder = ByteOrder.BigEndian,
};
var clone = (TiffMetadata)meta.DeepClone();
TiffMetadata clone = (TiffMetadata)meta.DeepClone();
clone.ByteOrder = ByteOrder.LittleEndian;
@ -46,10 +46,10 @@ public class TiffMetadataTests
{
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone();
TiffFrameMetadata cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone();
VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData);
var clone = (TiffFrameMetadata)meta.DeepClone();
TiffFrameMetadata clone = (TiffFrameMetadata)meta.DeepClone();
clone.BitsPerPixel = TiffBitsPerPixel.Bit8;
clone.Compression = TiffCompression.None;
@ -77,10 +77,10 @@ public class TiffMetadataTests
[InlineData(RgbUncompressed, 24)]
public void Identify_DetectsCorrectBitPerPixel(string imagePath, int expectedBitsPerPixel)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata();
@ -93,10 +93,10 @@ public class TiffMetadataTests
[InlineData(LittleEndianByteOrder, ByteOrder.LittleEndian)]
public void Identify_DetectsCorrectByteOrder(string imagePath, ByteOrder expectedByteOrder)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata();
@ -173,7 +173,7 @@ public class TiffMetadataTests
Assert.Equal("Copyright", exifProfile.GetValue(ExifTag.Copyright).Value);
Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value);
Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value);
var expectedResolution = new Rational(10, 1, simplify: false);
Rational expectedResolution = new(10, 1, simplify: false);
Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.XResolution).Value);
Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value);
Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer());
@ -242,13 +242,13 @@ public class TiffMetadataTests
Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel);
// Save to Tiff
var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb };
using var ms = new MemoryStream();
TiffEncoder tiffEncoder = new() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb };
using MemoryStream ms = new();
image.Save(ms, tiffEncoder);
// Assert
ms.Position = 0;
using var encodedImage = Image.Load<Rgba32>(ms);
using Image<Rgba32> encodedImage = Image.Load<Rgba32>(ms);
ImageMetadata encodedImageMetaData = encodedImage.Metadata;
ImageFrame<Rgba32> rootFrameEncodedImage = encodedImage.Frames.RootFrame;

14
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -39,9 +39,9 @@ public class WebpDecoderTests
int expectedHeight,
int expectedBitsPerPixel)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
Image.TryIdentify(stream, out ImageInfo imageInfo);
Assert.NotNull(imageInfo);
Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedHeight, imageInfo.Height);
@ -419,7 +419,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithHorizontalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath);
TestImageProvider<Rgba32> provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
@ -427,7 +427,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithVerticalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath);
TestImageProvider<Rgba32> provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
@ -435,7 +435,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithSimpleFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath);
TestImageProvider<Rgba32> provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
@ -443,7 +443,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithComplexFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath);
TestImageProvider<Rgba32> provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);

2
tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs

@ -34,7 +34,7 @@ public partial class ImageTests
{
using FileStream fs = File.OpenRead(TestFile.GetInputFileFullPath(file));
CancellationToken preCancelled = new(canceled: true);
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await Image.IdentifyAsync(fs, preCancelled));
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await Image.TryIdentifyAsync(fs, preCancelled));
}
private static TheoryData<bool, string, double> CreateLoadData()

55
tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs

@ -16,9 +16,9 @@ public partial class ImageTests
{
private static readonly string ActualImagePath = TestFile.GetInputFileFullPath(TestImages.Bmp.F);
private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private static byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private ReadOnlySpan<byte> ActualImageSpan => this.ActualImageBytes.AsSpan();
private static ReadOnlySpan<byte> ActualImageSpan => ActualImageBytes.AsSpan();
private IImageFormat LocalImageFormat => this.localImageFormatMock.Object;
@ -34,7 +34,7 @@ public partial class ImageTests
[Fact]
public void FromBytes_GlobalConfiguration()
{
bool result = Image.TryDetectFormat(this.ActualImageSpan, out IImageFormat type);
bool result = Image.TryDetectFormat(ActualImageSpan, out IImageFormat type);
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
@ -63,6 +63,15 @@ public partial class ImageTests
Assert.Equal(ExpectedGlobalFormat, type);
}
[Fact]
public async Task FromFileSystemPathAsync_GlobalConfiguration()
{
Attempt<IImageFormat> attempt = await Image.TryDetectFormatAsync(ActualImagePath);
Assert.True(attempt.Success);
Assert.Equal(ExpectedGlobalFormat, attempt.Value);
}
[Fact]
public void FromFileSystemPath_CustomConfiguration()
{
@ -78,15 +87,27 @@ public partial class ImageTests
}
[Fact]
public void FromStream_GlobalConfiguration()
public async Task FromFileSystemPathAsync_CustomConfiguration()
{
using (var stream = new MemoryStream(this.ActualImageBytes))
DecoderOptions options = new()
{
bool result = Image.TryDetectFormat(stream, out IImageFormat type);
Configuration = this.LocalConfiguration
};
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
}
Attempt<IImageFormat> attempt = await Image.TryDetectFormatAsync(options, this.MockFilePath);
Assert.True(attempt.Success);
Assert.Equal(this.LocalImageFormat, attempt.Value);
}
[Fact]
public void FromStream_GlobalConfiguration()
{
using MemoryStream stream = new(ActualImageBytes);
bool result = Image.TryDetectFormat(stream, out IImageFormat type);
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
}
[Fact]
@ -120,11 +141,9 @@ public partial class ImageTests
[Fact]
public async Task FromStreamAsync_GlobalConfiguration()
{
using (var stream = new MemoryStream(this.ActualImageBytes))
{
IImageFormat type = await Image.DetectFormatAsync(new AsyncStreamWrapper(stream, () => false));
Assert.Equal(ExpectedGlobalFormat, type);
}
using MemoryStream stream = new(ActualImageBytes);
Attempt<IImageFormat> attempt = await Image.TryDetectFormatAsync(new AsyncStreamWrapper(stream, () => false));
Assert.Equal(ExpectedGlobalFormat, attempt.Value);
}
[Fact]
@ -135,8 +154,8 @@ public partial class ImageTests
Configuration = this.LocalConfiguration
};
IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Equal(this.LocalImageFormat, type);
Attempt<IImageFormat> attempt = await Image.TryDetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Equal(this.LocalImageFormat, attempt.Value);
}
[Fact]
@ -147,8 +166,8 @@ public partial class ImageTests
Configuration = new()
};
IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Null(type);
Attempt<IImageFormat> attempt = await Image.TryDetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Null(attempt.Value);
}
}
}

152
tests/ImageSharp.Tests/Image/ImageTests.Identify.cs

@ -27,7 +27,7 @@ public partial class ImageTests
{
get
{
Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension("bmp", out var format);
Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension("bmp", out IImageFormat format);
return format!;
}
}
@ -35,30 +35,27 @@ public partial class ImageTests
[Fact]
public void FromBytes_GlobalConfiguration()
{
ImageInfo info = Image.Identify(ActualImageBytes, out IImageFormat type);
Image.TryIdentify(ActualImageBytes, out ImageInfo info);
Assert.Equal(ExpectedImageSize, info.Size());
Assert.Equal(ExpectedGlobalFormat, type);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public void FromBytes_CustomConfiguration()
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
ImageInfo info = Image.Identify(options, this.ByteArray, out IImageFormat type);
Image.TryIdentify(options, this.ByteArray, out ImageInfo info);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromFileSystemPath_GlobalConfiguration()
{
ImageInfo info = Image.TryIdentify(ActualImagePath, out IImageFormat type);
Image.TryIdentify(ActualImagePath, out ImageInfo info);
Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
@ -66,27 +63,26 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
ImageInfo info = Image.Identify(options, this.MockFilePath, out IImageFormat type);
Image.TryIdentify(options, this.MockFilePath, out ImageInfo info);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromStream_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
ImageInfo info = Image.Identify(stream, out IImageFormat type);
using MemoryStream stream = new(ActualImageBytes);
Image.TryIdentify(stream, out ImageInfo info);
Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public void FromStream_GlobalConfiguration_NoFormat()
{
using var stream = new MemoryStream(ActualImageBytes);
ImageInfo info = Image.Identify(stream);
using MemoryStream stream = new(ActualImageBytes);
Image.TryIdentify(stream, out ImageInfo info);
Assert.NotNull(info);
}
@ -94,22 +90,22 @@ public partial class ImageTests
[Fact]
public void FromNonSeekableStream_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
using MemoryStream stream = new(ActualImageBytes);
using NonSeekableStream nonSeekableStream = new(stream);
ImageInfo info = Image.Identify(nonSeekableStream, out IImageFormat type);
Image.TryIdentify(nonSeekableStream, out ImageInfo info);
Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public void FromNonSeekableStream_GlobalConfiguration_NoFormat()
{
using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
using MemoryStream stream = new(ActualImageBytes);
using NonSeekableStream nonSeekableStream = new(stream);
ImageInfo info = Image.Identify(nonSeekableStream);
Image.TryIdentify(nonSeekableStream, out ImageInfo info);
Assert.NotNull(info);
}
@ -119,10 +115,9 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
ImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type);
Image.TryIdentify(options, this.DataStream, out ImageInfo info);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
@ -130,7 +125,7 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
ImageInfo info = Image.TryIdentify(options, this.DataStream);
Image.TryIdentify(options, this.DataStream, out ImageInfo info);
Assert.Equal(this.LocalImageInfo, info);
}
@ -140,17 +135,15 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = new() };
ImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type);
Assert.False(Image.TryIdentify(options, this.DataStream, out ImageInfo info));
Assert.Null(info);
Assert.Null(type);
}
[Fact]
public void FromStream_ZeroLength_ReturnsNull()
{
// https://github.com/SixLabors/ImageSharp/issues/1903
using var zipFile = new ZipArchive(new MemoryStream(
using ZipArchive zipFile = new(new MemoryStream(
new byte[]
{
0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00,
@ -165,61 +158,67 @@ public partial class ImageTests
0x00, 0x00, 0x00, 0x00
}));
using Stream stream = zipFile.Entries[0].Open();
ImageInfo info = Image.Identify(stream);
Assert.False(Image.TryIdentify(stream, out ImageInfo info));
Assert.Null(info);
}
[Fact]
public async Task FromStreamAsync_GlobalConfiguration_NoFormat()
{
using var stream = new MemoryStream(ActualImageBytes);
var asyncStream = new AsyncStreamWrapper(stream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
using MemoryStream stream = new(ActualImageBytes);
AsyncStreamWrapper asyncStream = new(stream, () => false);
Assert.NotNull(info);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(asyncStream);
Assert.True(attempt.Success);
Assert.NotNull(attempt.Value);
}
[Fact]
public async Task FromStreamAsync_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
var asyncStream = new AsyncStreamWrapper(stream, () => false);
(ImageInfo ImageInfo, IImageFormat Format) res = await Image.TryIdentifyAsync(asyncStream);
using MemoryStream stream = new(ActualImageBytes);
AsyncStreamWrapper asyncStream = new(stream, () => false);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(asyncStream);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.True(attempt.Success);
Assert.Equal(ExpectedImageSize, attempt.Value.Size());
Assert.Equal(ExpectedGlobalFormat, attempt.Value.Metadata.DecodedImageFormat);
}
[Fact]
public async Task FromNonSeekableStreamAsync_GlobalConfiguration_NoFormat()
{
using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
using MemoryStream stream = new(ActualImageBytes);
using NonSeekableStream nonSeekableStream = new(stream);
var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
AsyncStreamWrapper asyncStream = new(nonSeekableStream, () => false);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(asyncStream);
Assert.NotNull(info);
Assert.True(attempt.Success);
Assert.NotNull(attempt.Value);
}
[Fact]
public async Task FromNonSeekableStreamAsync_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
using MemoryStream stream = new(ActualImageBytes);
using NonSeekableStream nonSeekableStream = new(stream);
var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false);
(ImageInfo ImageInfo, IImageFormat Format) res = await Image.TryIdentifyAsync(asyncStream);
AsyncStreamWrapper asyncStream = new(nonSeekableStream, () => false);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(asyncStream);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.True(attempt.Success);
Assert.Equal(ExpectedImageSize, attempt.Value.Size());
Assert.Equal(ExpectedGlobalFormat, attempt.Value.Metadata.DecodedImageFormat);
}
[Fact]
public async Task FromStreamAsync_ZeroLength_ReturnsNull()
{
// https://github.com/SixLabors/ImageSharp/issues/1903
using var zipFile = new ZipArchive(new MemoryStream(
using ZipArchive zipFile = new(new MemoryStream(
new byte[]
{
0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00,
@ -234,8 +233,11 @@ public partial class ImageTests
0x00, 0x00, 0x00, 0x00
}));
using Stream stream = zipFile.Entries[0].Open();
ImageInfo info = await Image.IdentifyAsync(stream);
Assert.Null(info);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(stream);
Assert.False(attempt.Success);
Assert.Null(attempt.Value);
}
[Fact]
@ -243,8 +245,10 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
ImageInfo info = await Image.IdentifyAsync(options, this.MockFilePath);
Assert.Equal(this.LocalImageInfo, info);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(options, this.MockFilePath);
Assert.True(attempt.Success);
Assert.Equal(this.LocalImageInfo, attempt.Value);
}
[Fact]
@ -252,27 +256,28 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
(ImageInfo ImageInfo, IImageFormat Format) info =
await Image.TryIdentifyAsync(options, this.MockFilePath);
Assert.NotNull(info.ImageInfo);
Assert.Equal(this.LocalImageFormat, info.Format);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(options, this.MockFilePath);
Assert.True(attempt.Success);
Assert.NotNull(attempt.Value);
}
[Fact]
public async Task IdentifyWithFormatAsync_FromPath_GlobalConfiguration()
{
(ImageInfo ImageInfo, IImageFormat Format) res = await Image.TryIdentifyAsync(ActualImagePath);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(ActualImagePath);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.Equal(ExpectedImageSize, attempt.Value.Size());
Assert.Equal(ExpectedGlobalFormat, attempt.Value.Metadata.DecodedImageFormat);
}
[Fact]
public async Task FromPathAsync_GlobalConfiguration()
{
ImageInfo info = await Image.IdentifyAsync(ActualImagePath);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(ActualImagePath);
Assert.Equal(ExpectedImageSize, info.Size());
Assert.True(attempt.Success);
Assert.Equal(ExpectedImageSize, attempt.Value.Size());
}
[Fact]
@ -280,12 +285,11 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false);
(ImageInfo ImageInfo, IImageFormat Format)
info = await Image.TryIdentifyAsync(options, asyncStream);
AsyncStreamWrapper asyncStream = new(this.DataStream, () => false);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(options, asyncStream);
Assert.Equal(this.LocalImageInfo, info.ImageInfo);
Assert.Equal(this.LocalImageFormat, info.Format);
Assert.True(attempt.Success);
Assert.Equal(this.LocalImageInfo, attempt.Value);
}
[Fact]
@ -293,11 +297,11 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = new() };
var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false);
(ImageInfo ImageInfo, IImageFormat Format)
info = await Image.TryIdentifyAsync(options, asyncStream);
AsyncStreamWrapper asyncStream = new(this.DataStream, () => false);
Attempt<ImageInfo> attempt = await Image.TryIdentifyAsync(options, asyncStream);
Assert.Null(info.ImageInfo);
Assert.False(attempt.Success);
Assert.Null(attempt.Value);
}
}
}

14
tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs

@ -51,18 +51,22 @@ public partial class ImageTests
protected byte[] ByteArray => ((MemoryStream)this.DataStream).ToArray();
protected ImageInfo LocalImageInfo => new(
this.localImageInfoMock.Object.PixelType,
this.localImageInfoMock.Object.Width,
this.localImageInfoMock.Object.Height,
this.localImageInfoMock.Object.Metadata);
protected ImageInfo LocalImageInfo { get; }
protected ImageLoadTestBase()
{
// TODO: Remove all this mocking. It's too complicated and we can now use fakes.
this.localStreamReturnImageRgba32 = new Image<Rgba32>(1, 1);
this.localStreamReturnImageAgnostic = new Image<Bgra4444>(1, 1);
this.localImageInfoMock = new Mock<IImageInfo>();
this.LocalImageInfo = new(
this.localImageInfoMock.Object.PixelType,
this.localImageInfoMock.Object.Width,
this.localImageInfoMock.Object.Height,
this.localImageInfoMock.Object.Metadata);
this.localImageFormatMock = new Mock<IImageFormat>();
this.localDecoder = new Mock<IImageDecoder>();

3
tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

@ -294,7 +294,6 @@ public partial class ImageTests
}
}
[Fact]
public unsafe void WrapMemory_Throws_OnTooLessWrongSize()
{
@ -307,7 +306,7 @@ public partial class ImageTests
{
try
{
using (var image = Image.WrapMemory<Rgba32>(cfg, ptr, 24, 5, 5, metaData));
using var image = Image.WrapMemory<Rgba32>(cfg, ptr, 24, 5, 5, metaData);
}
catch (Exception e)
{

8
tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
@ -11,12 +11,10 @@ namespace SixLabors.ImageSharp.Tests;
/// </summary>
public class MockImageFormatDetector : IImageFormatDetector
{
private IImageFormat localImageFormatMock;
private readonly IImageFormat localImageFormatMock;
public MockImageFormatDetector(IImageFormat imageFormat)
{
this.localImageFormatMock = imageFormat;
}
=> this.localImageFormatMock = imageFormat;
public int HeaderSize => 1;

Loading…
Cancel
Save