Browse Source

Merge pull request #2317 from SixLabors/js/move-iimageformat

Remove ImageFormat out params and return on Metadata
pull/2325/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
271dc99dfc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  2. 2
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  3. 2
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  4. 18
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  5. 2
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  6. 2
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  7. 4
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  8. 6
      src/ImageSharp/Formats/IImageDecoder.cs
  9. 4
      src/ImageSharp/Formats/IImageDecoderInternals.cs
  10. 117
      src/ImageSharp/Formats/ImageDecoder.cs
  11. 2
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  12. 64
      src/ImageSharp/Formats/ImageExtensions.Save.cs
  13. 78
      src/ImageSharp/Formats/ImageFormatManager.cs
  14. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  15. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  16. 2
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  17. 2
      src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
  18. 2
      src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
  19. 15
      src/ImageSharp/Formats/PixelTypeInfo.cs
  20. 4
      src/ImageSharp/Formats/Png/PngDecoder.cs
  21. 2
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  22. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  23. 36
      src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs
  24. 2
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  25. 2
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  26. 2
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  27. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  28. 2
      src/ImageSharp/Formats/Webp/WebpDecoder.cs
  29. 2
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  30. 11
      src/ImageSharp/IImage.cs
  31. 34
      src/ImageSharp/IImageInfo.cs
  32. 108
      src/ImageSharp/Image.Decode.cs
  33. 233
      src/ImageSharp/Image.FromBytes.cs
  34. 375
      src/ImageSharp/Image.FromFile.cs
  35. 474
      src/ImageSharp/Image.FromStream.cs
  36. 2
      src/ImageSharp/Image.LoadPixelData.cs
  37. 32
      src/ImageSharp/Image.cs
  38. 4
      src/ImageSharp/ImageExtensions.cs
  39. 58
      src/ImageSharp/ImageInfo.cs
  40. 24
      src/ImageSharp/ImageInfoExtensions.cs
  41. 2
      src/ImageSharp/Image{TPixel}.cs
  42. 20
      src/ImageSharp/Metadata/ImageMetadata.cs
  43. 2
      src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
  44. 2
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  45. 38
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs
  46. 2
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs
  47. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  48. 2
      src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs
  49. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
  50. 4
      tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs
  51. 17
      tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs
  52. 16
      tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs
  53. 8
      tests/ImageSharp.Benchmarks/Processing/Diffuse.cs
  54. 4
      tests/ImageSharp.Benchmarks/Processing/Rotate.cs
  55. 4
      tests/ImageSharp.Benchmarks/Processing/Skew.cs
  56. 12
      tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
  57. 16
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  58. 33
      tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
  59. 72
      tests/ImageSharp.Tests/Formats/Bmp/ImageExtensionsTest.cs
  60. 19
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  61. 18
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  62. 6
      tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
  63. 72
      tests/ImageSharp.Tests/Formats/Gif/ImageExtensionsTest.cs
  64. 21
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  65. 72
      tests/ImageSharp.Tests/Formats/Jpg/ImageExtensionsTest.cs
  66. 54
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  67. 10
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  68. 72
      tests/ImageSharp.Tests/Formats/Pbm/ImageExtensionsTest.cs
  69. 23
      tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs
  70. 72
      tests/ImageSharp.Tests/Formats/Png/ImageExtensionsTest.cs
  71. 18
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  72. 48
      tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
  73. 72
      tests/ImageSharp.Tests/Formats/Tga/ImageExtensionsTest.cs
  74. 8
      tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs
  75. 50
      tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs
  76. 72
      tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
  77. 40
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  78. 28
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  79. 72
      tests/ImageSharp.Tests/Formats/WebP/ImageExtensionsTests.cs
  80. 14
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  81. 12
      tests/ImageSharp.Tests/Image/ImageRotationTests.cs
  82. 82
      tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs
  83. 150
      tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
  84. 28
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
  85. 38
      tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs
  86. 15
      tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs
  87. 36
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs
  88. 15
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs
  89. 54
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs
  90. 4
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs
  91. 31
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs
  92. 34
      tests/ImageSharp.Tests/Image/ImageTests.Save.cs
  93. 88
      tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
  94. 3
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  95. 72
      tests/ImageSharp.Tests/Image/ImageTests.cs
  96. 2
      tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs
  97. 8
      tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs
  98. 24
      tests/ImageSharp.Tests/ImageInfoTests.cs
  99. 2
      tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs
  100. 2
      tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs

18
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -19,9 +19,9 @@ public static class AdvancedImageExtensions
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="filePath">The target file path to save the image to.</param>
/// <exception cref="ArgumentNullException">The file path is null.</exception>
/// <exception cref="NotSupportedException">No encoder available for provided path.</exception>
/// <returns>The matching <see cref="IImageEncoder"/>.</returns>
/// <exception cref="ArgumentNullException">The file path is null.</exception>
/// <exception cref="UnknownImageFormatException">No encoder available for provided path.</exception>
public static IImageEncoder DetectEncoder(this Image source, string filePath)
{
Guard.NotNull(filePath, nameof(filePath));
@ -30,27 +30,27 @@ public static class AdvancedImageExtensions
if (!source.GetConfiguration().ImageFormatsManager.TryFindFormatByFileExtension(ext, out IImageFormat? format))
{
StringBuilder sb = new();
sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}'. Registered encoders include:");
sb = sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
sb = sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
throw new UnknownImageFormatException(sb.ToString());
}
IImageEncoder? encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
IImageEncoder? encoder = source.GetConfiguration().ImageFormatsManager.GetEncoder(format);
if (encoder is null)
{
StringBuilder sb = new();
sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
sb = sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
sb = sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
throw new UnknownImageFormatException(sb.ToString());
}
return encoder;

2
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -20,7 +20,7 @@ public sealed class BmpDecoder : SpecializedImageDecoder<BmpDecoderOptions>
public static BmpDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -209,7 +209,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
}
/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo 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);

18
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -215,15 +215,15 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
BmpInfoHeader infoHeader = new(
headerSize: infoHeaderSize,
height: height,
width: width,
bitsPerPixel: bpp,
height: height,
planes: 1,
bitsPerPixel: bpp,
imageSize: height * bytesPerLine,
clrUsed: 0,
clrImportant: 0,
xPelsPerMeter: hResolution,
yPelsPerMeter: vResolution);
yPelsPerMeter: vResolution,
clrUsed: 0,
clrImportant: 0);
if ((this.infoHeaderType is BmpInfoHeaderType.WinVersion4 or BmpInfoHeaderType.WinVersion5) && this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
{
@ -470,7 +470,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration);
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
@ -541,7 +541,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
using IMemoryOwner<byte> colorPaletteBuffer = this.memoryAllocator.Allocate<byte>(ColorPaletteSize4Bit, AllocationOptions.Clean);
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
@ -588,7 +588,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
using IMemoryOwner<byte> colorPaletteBuffer = this.memoryAllocator.Allocate<byte>(ColorPaletteSize2Bit, AllocationOptions.Clean);
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
@ -644,7 +644,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
using IMemoryOwner<byte> colorPaletteBuffer = this.memoryAllocator.Allocate<byte>(ColorPaletteSize1Bit, AllocationOptions.Clean);
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();

2
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -20,7 +20,7 @@ public sealed class GifDecoder : ImageDecoder
public static GifDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -175,7 +175,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
}
/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
try
{

4
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -99,12 +99,12 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
if (useGlobalTable)
{
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
}
else
{
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image.Frames.RootFrame);
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
}
}

6
src/ImageSharp/Formats/IImageDecoder.cs

@ -15,9 +15,9 @@ public interface IImageDecoder
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <returns>The <see cref="ImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public IImageInfo Identify(DecoderOptions options, Stream stream);
public ImageInfo Identify(DecoderOptions options, Stream stream);
/// <summary>
/// Reads the raw image information from the specified stream.
@ -27,7 +27,7 @@ public interface IImageDecoder
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Task{IImageInfo}"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Task<IImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default);
public Task<ImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.

4
src/ImageSharp/Formats/IImageDecoderInternals.cs

@ -41,10 +41,10 @@ internal interface IImageDecoderInternals
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="IImageInfo"/>.</returns>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <remarks>
/// Cancellable synchronous method. In case of cancellation,
/// an <see cref="OperationCanceledException"/> shall be thrown which will be handled on the call site.
/// </remarks>
IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken);
ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken);
}

117
src/ImageSharp/Formats/ImageDecoder.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
@ -17,49 +16,85 @@ public abstract class ImageDecoder : IImageDecoder
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableStream(
options,
stream,
s => this.Decode<TPixel>(options, s, default));
{
Image<TPixel> image = WithSeekableStream(
options,
stream,
s => this.Decode<TPixel>(options, s, default));
this.SetDecoderFormat(options.Configuration, image);
return image;
}
/// <inheritdoc/>
public Image Decode(DecoderOptions options, Stream stream)
=> WithSeekableStream(
options,
stream,
s => this.Decode(options, s, default));
{
Image image = WithSeekableStream(
options,
stream,
s => this.Decode(options, s, default));
this.SetDecoderFormat(options.Configuration, image);
return image;
}
/// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
public async Task<Image<TPixel>> DecodeAsync<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Decode<TPixel>(options, s, ct),
cancellationToken);
{
Image<TPixel> image = await WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Decode<TPixel>(options, s, ct),
cancellationToken).ConfigureAwait(false);
this.SetDecoderFormat(options.Configuration, image);
return image;
}
/// <inheritdoc/>
public Task<Image> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
public async Task<Image> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{
Image image = await WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Decode(options, s, ct),
cancellationToken);
cancellationToken).ConfigureAwait(false);
this.SetDecoderFormat(options.Configuration, image);
return image;
}
/// <inheritdoc/>
public IImageInfo Identify(DecoderOptions options, Stream stream)
=> WithSeekableStream(
options,
stream,
s => this.Identify(options, s, default));
public ImageInfo Identify(DecoderOptions options, Stream stream)
{
ImageInfo info = WithSeekableStream(
options,
stream,
s => this.Identify(options, s, default));
this.SetDecoderFormat(options.Configuration, info);
return info;
}
/// <inheritdoc/>
public Task<IImageInfo> 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.
@ -98,9 +133,9 @@ public abstract class ImageDecoder : IImageDecoder
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <returns>The <see cref="ImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
protected abstract ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// Performs a scaling operation against the decoded image. If the target size is not set, or the image size
@ -114,7 +149,7 @@ public abstract class ImageDecoder : IImageDecoder
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Size = options.TargetSize!.Value,
Sampler = options.Sampler,
Mode = ResizeMode.Max
};
@ -137,7 +172,7 @@ public abstract class ImageDecoder : IImageDecoder
}
Size targetSize = options.TargetSize.Value;
Size currentSize = image.Size();
Size currentSize = image.Size;
return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height;
}
@ -252,4 +287,20 @@ public abstract class ImageDecoder : IImageDecoder
memoryStream.Position = 0;
return await action(memoryStream, position, cancellationToken).ConfigureAwait(false);
}
internal void SetDecoderFormat(Configuration configuration, Image image)
{
if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format))
{
image.Metadata.DecodedImageFormat = format;
}
}
internal void SetDecoderFormat(Configuration configuration, ImageInfo info)
{
if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format))
{
info.Metadata.DecodedImageFormat = format;
}
}
}

2
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats;
/// </summary>
internal static class ImageDecoderUtilities
{
internal static IImageInfo Identify(
internal static ImageInfo Identify(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,

64
src/ImageSharp/Formats/ImageExtensions.Save.cs

@ -58,7 +58,7 @@ public static partial class ImageExtensions
public static void SaveAsBmp(this Image source, string path, BmpEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(BmpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -72,7 +72,7 @@ public static partial class ImageExtensions
public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(BmpFormat.Instance),
cancellationToken);
/// <summary>
@ -105,7 +105,7 @@ public static partial class ImageExtensions
public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(BmpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -119,7 +119,7 @@ public static partial class ImageExtensions
public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(BmpFormat.Instance),
cancellationToken);
/// <summary>
@ -160,7 +160,7 @@ public static partial class ImageExtensions
public static void SaveAsGif(this Image source, string path, GifEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(GifFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -174,7 +174,7 @@ public static partial class ImageExtensions
public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(GifFormat.Instance),
cancellationToken);
/// <summary>
@ -207,7 +207,7 @@ public static partial class ImageExtensions
public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(GifFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -221,7 +221,7 @@ public static partial class ImageExtensions
public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(GifFormat.Instance),
cancellationToken);
/// <summary>
@ -262,7 +262,7 @@ public static partial class ImageExtensions
public static void SaveAsJpeg(this Image source, string path, JpegEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(JpegFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -276,7 +276,7 @@ public static partial class ImageExtensions
public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(JpegFormat.Instance),
cancellationToken);
/// <summary>
@ -309,7 +309,7 @@ public static partial class ImageExtensions
public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(JpegFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -323,7 +323,7 @@ public static partial class ImageExtensions
public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(JpegFormat.Instance),
cancellationToken);
/// <summary>
@ -364,7 +364,7 @@ public static partial class ImageExtensions
public static void SaveAsPbm(this Image source, string path, PbmEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PbmFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -378,7 +378,7 @@ public static partial class ImageExtensions
public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
@ -411,7 +411,7 @@ public static partial class ImageExtensions
public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PbmFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -425,7 +425,7 @@ public static partial class ImageExtensions
public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
@ -466,7 +466,7 @@ public static partial class ImageExtensions
public static void SaveAsPng(this Image source, string path, PngEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PngFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -480,7 +480,7 @@ public static partial class ImageExtensions
public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PngFormat.Instance),
cancellationToken);
/// <summary>
@ -513,7 +513,7 @@ public static partial class ImageExtensions
public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PngFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -527,7 +527,7 @@ public static partial class ImageExtensions
public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(PngFormat.Instance),
cancellationToken);
/// <summary>
@ -568,7 +568,7 @@ public static partial class ImageExtensions
public static void SaveAsTga(this Image source, string path, TgaEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TgaFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -582,7 +582,7 @@ public static partial class ImageExtensions
public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TgaFormat.Instance),
cancellationToken);
/// <summary>
@ -615,7 +615,7 @@ public static partial class ImageExtensions
public static void SaveAsTga(this Image source, Stream stream, TgaEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TgaFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -629,7 +629,7 @@ public static partial class ImageExtensions
public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TgaFormat.Instance),
cancellationToken);
/// <summary>
@ -670,7 +670,7 @@ public static partial class ImageExtensions
public static void SaveAsWebp(this Image source, string path, WebpEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(WebpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -684,7 +684,7 @@ public static partial class ImageExtensions
public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(WebpFormat.Instance),
cancellationToken);
/// <summary>
@ -717,7 +717,7 @@ public static partial class ImageExtensions
public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(WebpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -731,7 +731,7 @@ public static partial class ImageExtensions
public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(WebpFormat.Instance),
cancellationToken);
/// <summary>
@ -772,7 +772,7 @@ public static partial class ImageExtensions
public static void SaveAsTiff(this Image source, string path, TiffEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TiffFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -786,7 +786,7 @@ public static partial class ImageExtensions
public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TiffFormat.Instance),
cancellationToken);
/// <summary>
@ -819,7 +819,7 @@ public static partial class ImageExtensions
public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TiffFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -833,7 +833,7 @@ public static partial class ImageExtensions
public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TiffFormat.Instance),
cancellationToken);
}

78
src/ImageSharp/Formats/ImageFormatManager.cs

@ -3,6 +3,8 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
namespace SixLabors.ImageSharp.Formats;
@ -91,9 +93,13 @@ public class ImageFormatManager
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <param name="format">The <see cref="IImageFormat"/> if found otherwise null</param>
/// <returns>False if no format was found</returns>
/// <param name="extension">The extension to return the format for.</param>
/// <param name="format">
/// When this method returns, contains the format that matches the given extension;
/// 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 bool TryFindFormatByFileExtension(string extension, [NotNullWhen(true)] out IImageFormat? format)
{
Guard.NotNullOrWhiteSpace(extension, nameof(extension));
@ -106,16 +112,30 @@ public class ImageFormatManager
format = this.imageFormats.FirstOrDefault(x =>
x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
return format != null;
return format is not null;
}
/// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat? FindFormatByMimeType(string mimeType)
=> this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
/// <param name="mimeType">The mime-type to return the format for.</param>
/// <param name="format">
/// When this method returns, contains the format that matches the given mime-type;
/// 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 bool TryFindFormatByMimeType(string mimeType, [NotNullWhen(true)] out IImageFormat? format)
{
format = this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
return format is not null;
}
internal bool TryFindFormatByDecoder(IImageDecoder decoder, [NotNullWhen(true)] out IImageFormat? format)
{
format = this.mimeTypeDecoders.FirstOrDefault(x => x.Value.GetType() == decoder.GetType()).Key;
return format is not null;
}
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
@ -163,32 +183,54 @@ public class ImageFormatManager
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder? FindDecoder(IImageFormat format)
/// <returns>The <see cref="IImageDecoder"/>.</returns>
/// <exception cref="UnknownImageFormatException">The format is not registered.</exception>
public IImageDecoder GetDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder? decoder)
? decoder
: null;
if (!this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder? decoder))
{
ThrowInvalidDecoder(this);
}
return decoder;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder? FindEncoder(IImageFormat format)
/// <returns>The <see cref="IImageEncoder"/>.</returns>
/// <exception cref="UnknownImageFormatException">The format is not registered.</exception>
public IImageEncoder GetEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder? encoder)
? encoder
: null;
if (!this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder? encoder))
{
ThrowInvalidDecoder(this);
}
return encoder;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize() => this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
[DoesNotReturn]
internal static void ThrowInvalidDecoder(ImageFormatManager manager)
{
StringBuilder sb = new();
sb = sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in manager.ImageDecoders)
{
sb = sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new UnknownImageFormatException(sb.ToString());
}
}

2
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -20,7 +20,7 @@ public sealed class JpegDecoder : SpecializedImageDecoder<JpegDecoderOptions>
public static JpegDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -225,7 +225,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
}
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ParseStream(stream, spectralConverter: null, cancellationToken);
this.InitExifProfile();

2
src/ImageSharp/Formats/Pbm/PbmDecoder.cs

@ -36,7 +36,7 @@ public sealed class PbmDecoder : ImageDecoder
public static PbmDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs

@ -83,7 +83,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
}
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ProcessHeader(stream);

2
src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs

@ -68,7 +68,7 @@ internal sealed class PbmEncoderCore : IImageEncoderInternals
this.SanitizeAndSetEncoderOptions(image);
byte signature = this.DeduceSignature();
this.WriteHeader(stream, signature, image.Size());
this.WriteHeader(stream, signature, image.Size);
this.WritePixels(stream, image.Frames.RootFrame);

15
src/ImageSharp/Formats/PixelTypeInfo.cs

@ -4,6 +4,9 @@
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
// TODO: Review this class as it's used to represent 2 different things.
// 1.The encoded image pixel format.
// 2. The pixel format of the decoded image.
namespace SixLabors.ImageSharp.Formats;
/// <summary>
@ -16,9 +19,7 @@ public class PixelTypeInfo
/// </summary>
/// <param name="bitsPerPixel">Color depth, in number of bits per pixel.</param>
public PixelTypeInfo(int bitsPerPixel)
{
this.BitsPerPixel = bitsPerPixel;
}
=> this.BitsPerPixel = bitsPerPixel;
/// <summary>
/// Initializes a new instance of the <see cref="PixelTypeInfo"/> class.
@ -43,12 +44,10 @@ public class PixelTypeInfo
public PixelAlphaRepresentation? AlphaRepresentation { get; }
internal static PixelTypeInfo Create<TPixel>()
where TPixel : unmanaged, IPixel<TPixel> =>
new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
where TPixel : unmanaged, IPixel<TPixel>
=> new(Unsafe.SizeOf<TPixel>() * 8);
internal static PixelTypeInfo Create<TPixel>(PixelAlphaRepresentation alpha)
where TPixel : unmanaged, IPixel<TPixel>
{
return new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8, alpha);
}
=> new(Unsafe.SizeOf<TPixel>() * 8, alpha);
}

4
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -20,7 +20,7 @@ public sealed class PngDecoder : ImageDecoder
public static PngDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@ -49,7 +49,7 @@ public sealed class PngDecoder : ImageDecoder
Guard.NotNull(stream, nameof(stream));
PngDecoderCore decoder = new(options, true);
IImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken);
ImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken);
stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata();

2
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -247,7 +247,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
}
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata();

2
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -1300,7 +1300,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(image.GetConfiguration());
frameQuantizer.BuildPalette(encoder.PixelSamplingStrategy, image);
return frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
return frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
}
/// <summary>

36
src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs

@ -17,34 +17,54 @@ public abstract class SpecializedImageDecoder<T> : ImageDecoder, ISpecializedIma
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(T options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableStream(
{
Image<TPixel> image = WithSeekableStream(
options.GeneralOptions,
stream,
s => this.Decode<TPixel>(options, s, default));
this.SetDecoderFormat(options.GeneralOptions.Configuration, image);
return image;
}
/// <inheritdoc/>
public Image Decode(T options, Stream stream)
=> WithSeekableStream(
{
Image image = WithSeekableStream(
options.GeneralOptions,
stream,
s => this.Decode(options, s, default));
this.SetDecoderFormat(options.GeneralOptions.Configuration, image);
return image;
}
/// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(T options, Stream stream, CancellationToken cancellationToken = default)
public async Task<Image<TPixel>> DecodeAsync<TPixel>(T options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableMemoryStreamAsync(
{
Image<TPixel> image = await WithSeekableMemoryStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => this.Decode<TPixel>(options, s, ct),
cancellationToken);
cancellationToken).ConfigureAwait(false);
this.SetDecoderFormat(options.GeneralOptions.Configuration, image);
return image;
}
/// <inheritdoc/>
public Task<Image> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
public async Task<Image> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken = default)
{
Image image = await WithSeekableMemoryStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => this.Decode(options, s, ct),
cancellationToken);
cancellationToken).ConfigureAwait(false);
this.SetDecoderFormat(options.GeneralOptions.Configuration, image);
return image;
}
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}" /> of a specific pixel type.

2
src/ImageSharp/Formats/Tga/TgaDecoder.cs

@ -20,7 +20,7 @@ public sealed class TgaDecoder : ImageDecoder
public static TgaDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -647,7 +647,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ReadFileHeader(stream);
return new ImageInfo(

2
src/ImageSharp/Formats/Tiff/TiffDecoder.cs

@ -20,7 +20,7 @@ public class TiffDecoder : ImageDecoder
public static TiffDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -213,7 +213,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
}
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.inputStream = stream;
DirectoryReader reader = new(stream, this.configuration.MemoryAllocator);

2
src/ImageSharp/Formats/Webp/WebpDecoder.cs

@ -20,7 +20,7 @@ public sealed class WebpDecoder : ImageDecoder
public static WebpDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

2
src/ImageSharp/Formats/Webp/WebpDecoderCore.cs

@ -145,7 +145,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
}
/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.currentStream = stream;

11
src/ImageSharp/IImage.cs

@ -1,11 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp;
/// <summary>
/// Encapsulates the properties and methods that describe an image.
/// </summary>
public interface IImage : IImageInfo, IDisposable
{
}

34
src/ImageSharp/IImageInfo.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp;
/// <summary>
/// Encapsulates properties that describe basic image information including dimensions, pixel type information
/// and additional metadata.
/// </summary>
public interface IImageInfo
{
/// <summary>
/// Gets information about the image pixels.
/// </summary>
PixelTypeInfo PixelType { get; }
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the metadata of the image.
/// </summary>
ImageMetadata Metadata { get; }
}

108
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,6 +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>
/// <exception cref="UnknownImageFormatException">The input format is not recognized.</exception>
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
@ -52,7 +52,7 @@ public abstract partial class Image
int headerSize = (int)Math.Min(configuration.MaxHeaderSize, stream.Length);
if (headerSize <= 0)
{
return null;
ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
}
// Header sizes are so small, that headersBuffer will be always stackalloc-ed in practice,
@ -77,19 +77,21 @@ 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;
}
}
return format;
if (format is null)
{
ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
}
return format!;
}
/// <summary>
@ -97,15 +99,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"/>.</returns>
private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream)
{
format = InternalDetectFormat(options.Configuration, stream);
return format != null
? options.Configuration.ImageFormatsManager.FindDecoder(format)
: null;
IImageFormat format = InternalDetectFormat(options.Configuration, stream);
return options.Configuration.ImageFormatsManager.GetDecoder(format);
}
/// <summary>
@ -117,60 +115,36 @@ 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);
if (decoder is null)
{
return (null, null);
}
Image<TPixel> img = decoder.Decode<TPixel>(options, stream);
return (img, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.Decode<TPixel>(options, stream);
}
private static async Task<(Image<TPixel> Image, IImageFormat Format)> DecodeAsync<TPixel>(
private static Task<Image<TPixel>> DecodeAsync<TPixel>(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
Image<TPixel> img = await decoder.DecodeAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.DecodeAsync<TPixel>(options, stream, cancellationToken);
}
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);
if (decoder is null)
{
return (null, null);
}
Image img = decoder.Decode(options, stream);
return (img, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.Decode(options, stream);
}
private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(
private static Task<Image> DecodeAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.DecodeAsync(options, stream, cancellationToken);
}
/// <summary>
@ -178,14 +152,11 @@ public abstract partial class Image
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream.</param>
/// <returns>
/// The <see cref="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentify(DecoderOptions options, Stream stream)
/// <returns>The <see cref="ImageInfo"/>.</returns>
private static ImageInfo InternalIdentify(DecoderOptions options, Stream stream)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageInfo info = decoder?.Identify(options, stream);
return (info, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.Identify(options, stream);
}
/// <summary>
@ -194,22 +165,13 @@ public abstract partial class Image
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>
/// The <see cref="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static async Task<(IImageInfo ImageInfo, IImageFormat Format)> InternalIdentifyAsync(
/// <returns>The <see cref="ImageInfo"/>.</returns>
private static Task<ImageInfo> InternalIdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
IImageInfo info = await decoder.IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (info, format);
IImageDecoder decoder = DiscoverDecoder(options, stream);
return decoder.IdentifyAsync(options, stream, cancellationToken);
}
}

233
src/ImageSharp/Image.FromBytes.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
@ -15,215 +14,129 @@ public abstract partial class Image
/// <summary>
/// By reading the header on the provided byte span this calculates the images format.
/// </summary>
/// <param name="data">The byte span containing encoded image data to read the header from.</param>
/// <param name="format">The format or null if none found.</param>
/// <returns>returns true when format was detected otherwise false.</returns>
public static bool TryDetectFormat(ReadOnlySpan<byte> data, [NotNullWhen(true)] out IImageFormat? format)
=> TryDetectFormat(DecoderOptions.Default, data, out format);
/// <param name="buffer">The byte span containing encoded image data to read the header from.</param>
/// <returns>The <see cref="IImageFormat"/>.</returns>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static IImageFormat DetectFormat(ReadOnlySpan<byte> buffer)
=> DetectFormat(DecoderOptions.Default, buffer);
/// <summary>
/// By reading the header on the provided byte span this calculates the images format.
/// </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 mime type or null if none found.</param>
/// <param name="buffer">The byte span containing encoded image data to read the header from.</param>
/// <returns>The <see cref="IImageFormat"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <returns>returns true when format was detected otherwise false.</returns>
public static bool TryDetectFormat(DecoderOptions options, ReadOnlySpan<byte> data, [NotNullWhen(true)] out IImageFormat? format)
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static unsafe IImageFormat DetectFormat(DecoderOptions options, ReadOnlySpan<byte> buffer)
{
Guard.NotNull(options, nameof(options.Configuration));
Configuration configuration = options.Configuration;
int maxHeaderSize = configuration.MaxHeaderSize;
if (maxHeaderSize <= 0)
fixed (byte* ptr = buffer)
{
format = null;
return false;
using UnmanagedMemoryStream stream = new(ptr, buffer.Length);
return DetectFormat(options, stream);
}
foreach (IImageFormatDetector detector in configuration.ImageFormatsManager.FormatDetectors)
{
if (detector.TryDetectFormat(data, out format))
{
return true;
}
}
format = default;
return false;
}
/// <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="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(ReadOnlySpan<byte> data) => Identify(data, out IImageFormat _);
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </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>
/// <exception cref="ArgumentNullException">The data is null.</exception>
/// <exception cref="NotSupportedException">The data is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(ReadOnlySpan<byte> data, out IImageFormat format)
=> Identify(DecoderOptions.Default, data, out format);
/// <param name="buffer">The byte array containing encoded image data to read the header from.</param>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static ImageInfo Identify(ReadOnlySpan<byte> buffer)
=> Identify(DecoderOptions.Default, buffer);
/// <summary>
/// Reads the raw image information from the specified span of bytes without fully decoding it.
/// </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>
/// <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="IImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static unsafe IImageInfo Identify(DecoderOptions options, ReadOnlySpan<byte> data, out IImageFormat format)
/// <param name="buffer">The byte span containing encoded image data to read the header from.</param>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static unsafe ImageInfo Identify(DecoderOptions options, ReadOnlySpan<byte> buffer)
{
fixed (byte* ptr = data)
fixed (byte* ptr = buffer)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
return Identify(options, stream, out format);
using UnmanagedMemoryStream stream = new(ptr, buffer.Length);
return Identify(options, stream);
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="data">The byte span containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(ReadOnlySpan<byte> data)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// Creates a new instance of the <see cref="Image"/> class from the given byte span.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="data">The byte span containing image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(ReadOnlySpan<byte> data, out IImageFormat format)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, data, out format);
/// <param name="buffer">The byte span containing encoded image data.</param>
/// <returns><see cref="Image"/>.</returns>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(ReadOnlySpan<byte> buffer)
=> Load(DecoderOptions.Default, buffer);
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// Creates a new instance of the <see cref="Image"/> class from the given byte span.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="data">The byte span containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="buffer">The byte span containing encoded image data.</param>
/// <returns><see cref="Image"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(DecoderOptions options, ReadOnlySpan<byte> data)
where TPixel : unmanaged, IPixel<TPixel>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static unsafe Image Load(DecoderOptions options, ReadOnlySpan<byte> buffer)
{
fixed (byte* ptr = data)
fixed (byte* ptr = buffer)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
return Load<TPixel>(options, stream);
using UnmanagedMemoryStream stream = new(ptr, buffer.Length);
return Load(options, stream);
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given byte span.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="data">The byte span containing image data.</param>
/// <param name="format">The <see cref="IImageFormat"/> of the decoded image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(
DecoderOptions options,
ReadOnlySpan<byte> data,
out IImageFormat format)
/// <param name="data">The byte span containing encoded image data.</param>
/// <returns><see cref="Image{TPixel}"/>.</returns>
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image<TPixel> Load<TPixel>(ReadOnlySpan<byte> data)
where TPixel : unmanaged, IPixel<TPixel>
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
return Load<TPixel>(options, stream, out format);
}
}
/// <summary>
/// Load a new instance of <see cref="Image"/> from the given encoded byte span.
/// </summary>
/// <param name="data">The byte span containing image data.</param>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(ReadOnlySpan<byte> data)
=> Load(DecoderOptions.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte span containing image data.</param>
/// <param name="format">The detected format.</param>
/// <exception cref="ArgumentNullException">The decoder is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(ReadOnlySpan<byte> data, out IImageFormat format)
=> Load(DecoderOptions.Default, data, out format);
/// <summary>
/// Decodes a new instance of <see cref="Image"/> from the given encoded byte span.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="data">The byte span containing image data.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(DecoderOptions options, ReadOnlySpan<byte> data)
=> Load(options, data, out _);
=> Load<TPixel>(DecoderOptions.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image"/> from the given encoded byte span.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given byte span.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="data">The byte span containing image data.</param>
/// <param name="format">The <see cref="IImageFormat"/> of the decoded image.</param>>
/// <param name="data">The byte span containing encoded image data.</param>
/// <returns><see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static unsafe Image Load(
DecoderOptions options,
ReadOnlySpan<byte> data,
out IImageFormat format)
/// <exception cref="NotSupportedException">The image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static unsafe Image<TPixel> Load<TPixel>(DecoderOptions options, ReadOnlySpan<byte> data)
where TPixel : unmanaged, IPixel<TPixel>
{
fixed (byte* ptr = data)
{
using var stream = new UnmanagedMemoryStream(ptr, data.Length);
return Load(options, stream, out format);
using UnmanagedMemoryStream stream = new(ptr, data.Length);
return Load<TPixel>(options, stream);
}
}
}

375
src/ImageSharp/Image.FromFile.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
@ -13,298 +12,238 @@ 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.
/// </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>
public static bool TryDetectFormat(string filePath, [NotNullWhen(true)] out IImageFormat? format)
=> TryDetectFormat(DecoderOptions.Default, filePath, out format);
/// <param name="path">The image file to open and to read the header from.</param>
/// <returns>The <see cref="IImageFormat"/>.</returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static IImageFormat DetectFormat(string path)
=> DetectFormat(DecoderOptions.Default, path);
/// <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.
/// </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>
public static bool TryDetectFormat(DecoderOptions options, string filePath, [NotNullWhen(true)] out IImageFormat? format)
/// <param name="path">The image file to open and to read the header from.</param>
/// <returns>The <see cref="IImageFormat"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static IImageFormat DetectFormat(DecoderOptions options, string path)
{
Guard.NotNull(options, nameof(options));
using Stream file = options.Configuration.FileSystem.OpenRead(filePath);
return TryDetectFormat(options, file, out format);
using Stream file = options.Configuration.FileSystem.OpenRead(path);
return DetectFormat(options, file);
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// Detects the encoded image format type from the specified file.
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo 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="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(string filePath, out IImageFormat format)
=> Identify(DecoderOptions.Default, filePath, out format);
/// <param name="path">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</returns>
public static Task<IImageFormat> DetectFormatAsync(
string path,
CancellationToken cancellationToken = default)
=> DetectFormatAsync(DecoderOptions.Default, path, 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.
/// </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="IImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(DecoderOptions options, string filePath, out IImageFormat format)
/// <param name="path">The image file to open and to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static async Task<IImageFormat> DetectFormatAsync(
DecoderOptions options,
string path,
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(path);
return await DetectFormatAsync(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="IImageInfo"/> property set to null if suitable info detector is not found.
/// </returns>
public static Task<IImageInfo> IdentifyAsync(string filePath, CancellationToken cancellationToken = default)
=> IdentifyAsync(DecoderOptions.Default, filePath, cancellationToken);
/// <param name="path">The image file to open and to read the header from.</param>
/// <returns><see langword="true"/> if the information can be read; otherwise, <see langword="false"/></returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static ImageInfo Identify(string path)
=> Identify(DecoderOptions.Default, path);
/// <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.
/// </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="IImageInfo"/> property set to null if suitable info detector is not found.
/// </returns>
public static async Task<IImageInfo> IdentifyAsync(
DecoderOptions options,
string filePath,
CancellationToken cancellationToken = default)
/// <param name="path">The image file to open and to read the header from.</param>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static ImageInfo Identify(DecoderOptions options, string path)
{
(IImageInfo 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(path);
return Identify(options, stream);
}
/// <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="path">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>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <returns>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="IImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{ImageInfo}"/> representing the asynchronous operation.
/// </returns>
public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
string filePath,
CancellationToken cancellationToken = default)
=> IdentifyWithFormatAsync(DecoderOptions.Default, filePath, cancellationToken);
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<ImageInfo> IdentifyAsync(string path, CancellationToken cancellationToken = default)
=> IdentifyAsync(DecoderOptions.Default, path, cancellationToken);
/// <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="filePath">The image file to open and to read the header from.</param>
/// <param name="path">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="IImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{ImageInfo}"/> representing the asynchronous operation.
/// </returns>
public static async Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static async Task<ImageInfo> IdentifyAsync(
DecoderOptions options,
string filePath,
string path,
CancellationToken cancellationToken = default)
{
Guard.NotNull(options, nameof(options));
using Stream stream = options.Configuration.FileSystem.OpenRead(filePath);
return await IdentifyWithFormatAsync(options, stream, cancellationToken)
.ConfigureAwait(false);
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
return await IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// Creates a new instance of the <see cref="Image"/> class from the given file path.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The <see cref="Image"/>.</returns>
/// <returns><see cref="Image"/>.</returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image Load(string path)
=> Load(DecoderOptions.Default, path);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image Load(string path, out IImageFormat format)
=> Load(DecoderOptions.Default, path, out format);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// Creates a new instance of the <see cref="Image"/> class from the given file path.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns><see cref="Image"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>The <see cref="Image"/>.</returns>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image Load(DecoderOptions options, string path)
=> Load(options, path, out _);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static async Task<Image> LoadAsync(
DecoderOptions options,
string path,
CancellationToken cancellationToken = default)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(path, nameof(path));
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
(Image img, _) = await LoadWithFormatAsync(options, stream, cancellationToken)
.ConfigureAwait(false);
return img;
return Load(options, stream);
}
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// Creates a new instance of the <see cref="Image"/> class from the given file path.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="ArgumentNullException">The decoder is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image> LoadAsync(string path, CancellationToken cancellationToken = default)
=> LoadAsync(DecoderOptions.Default, path, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static Task<Image<TPixel>> LoadAsync<TPixel>(string path, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> LoadAsync<TPixel>(DecoderOptions.Default, path, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// Creates a new instance of the <see cref="Image"/> class from the given file path.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static async Task<Image<TPixel>> LoadAsync<TPixel>(
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static async Task<Image> LoadAsync(
DecoderOptions options,
string path,
CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(options, nameof(options));
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
(Image<TPixel> img, _) =
await LoadWithFormatAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
return img;
return await LoadAsync(options, stream, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given file path.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <returns><see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image<TPixel> Load<TPixel>(string path)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, path);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given file path.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(string path, out IImageFormat format)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, path, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns><see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image<TPixel> Load<TPixel>(DecoderOptions options, string path)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -316,47 +255,43 @@ public abstract partial class Image
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given file path.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(DecoderOptions options, string path, out IImageFormat format)
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image<TPixel>> LoadAsync<TPixel>(string path, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(path, nameof(path));
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
return Load<TPixel>(options, stream, out format);
}
=> LoadAsync<TPixel>(DecoderOptions.Default, path, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// The pixel type is selected by the decoder.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given file path.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="NotSupportedException">Image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image Load(DecoderOptions options, string path, out IImageFormat format)
/// <exception cref="NotSupportedException">The file stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static async Task<Image<TPixel>> LoadAsync<TPixel>(
DecoderOptions options,
string path,
CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(path, nameof(path));
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
return Load(options, stream, out format);
return await LoadAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
}
}

474
src/ImageSharp/Image.FromStream.cs

@ -1,9 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
@ -16,54 +13,62 @@ 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.
/// </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>
/// <returns>The <see cref="IImageFormat"/>.</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);
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static IImageFormat DetectFormat(Stream stream)
=> DetectFormat(DecoderOptions.Default, stream);
/// <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.
/// </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>
/// <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;
}
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static IImageFormat DetectFormat(DecoderOptions options, Stream stream)
=> WithSeekableStream(options, stream, s => InternalDetectFormat(options.Configuration, s));
/// <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.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</returns>
/// <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)
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<IImageFormat> DetectFormatAsync(
Stream stream,
CancellationToken cancellationToken = default)
=> DetectFormatAsync(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.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</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>A <see cref="Task{IImageFormat}"/> representing the asynchronous operation.</returns>
public static Task<IImageFormat> DetectFormatAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<IImageFormat> DetectFormatAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
=> WithSeekableStreamAsync(
options,
stream,
@ -74,136 +79,60 @@ public abstract partial class Image
/// 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>
/// <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="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(Stream stream)
=> Identify(stream, out IImageFormat _);
/// <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<IImageInfo> IdentifyAsync(Stream stream, CancellationToken cancellationToken = default)
=> IdentifyAsync(DecoderOptions.Default, stream, 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>
/// <param name="format">The format type of the decoded image.</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="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(Stream stream, out IImageFormat format)
=> Identify(DecoderOptions.Default, stream, out format);
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static ImageInfo Identify(Stream stream)
=> Identify(DecoderOptions.Default, stream);
/// <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>
/// <returns>The <see cref="ImageInfo"/>.</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="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(DecoderOptions options, Stream stream)
=> Identify(options, stream, out _);
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static ImageInfo Identify(DecoderOptions options, Stream stream)
=> WithSeekableStream(options, stream, s => InternalIdentify(options, s));
/// <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.
/// The <see cref="Task{ImageInfo}"/> representing the asynchronous operation.
/// </returns>
public static async Task<IImageInfo> IdentifyAsync(
DecoderOptions options,
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<ImageInfo> IdentifyAsync(
Stream stream,
CancellationToken cancellationToken = default)
{
(IImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false);
return res.ImageInfo;
}
=> IdentifyAsync(DecoderOptions.Default, stream, cancellationToken);
/// <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="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(DecoderOptions options, Stream stream, out IImageFormat format)
{
(IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentify(options, s));
format = data.Format;
return data.ImageInfo;
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <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>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="IImageInfo"/> property set to null if suitable info detector is not found.
/// The <see cref="Task{ImageInfo}"/> representing the asynchronous operation.
/// </returns>
public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
Stream stream,
CancellationToken cancellationToken = default)
=> IdentifyWithFormatAsync(DecoderOptions.Default, stream, cancellationToken);
/// <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>
/// The <see cref="Task{ValueTuple}"/> representing the asynchronous operation with the parameter type
/// <see cref="IImageInfo"/> property set to null if suitable info detector is not found.
/// </returns>
public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<ImageInfo> IdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
@ -214,301 +143,130 @@ public abstract partial class Image
cancellationToken);
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is selected by the decoder.
/// Creates a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <returns><see cref="Image"/>.</returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(Stream stream, out IImageFormat format)
=> Load(DecoderOptions.Default, stream, out format);
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image Load(Stream stream)
=> Load(DecoderOptions.Default, stream);
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is selected by the decoder.
/// </summary>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{ValueTuple}"/> representing the asynchronous operation.</returns>
public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream, CancellationToken cancellationToken = default)
=> LoadWithFormatAsync(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is selected by the decoder.
/// Creates a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <returns><see cref="Image"/>.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Load(Stream stream) => Load(DecoderOptions.Default, stream);
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image Load(DecoderOptions options, Stream stream)
=> WithSeekableStream(options, stream, s => Decode(options, s));
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is selected by the decoder.
/// Creates a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image> LoadAsync(Stream stream, CancellationToken cancellationToken = default)
=> LoadAsync(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A new <see cref="Image"/>.</returns>
public static Image Load(DecoderOptions options, Stream stream)
=> Load(options, stream, out _);
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// Creates a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is automatically determined by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static async Task<Image> LoadAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> (await LoadWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false)).Image;
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image> LoadAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
=> WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <returns><see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image<TPixel> Load<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, stream);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static Task<Image<TPixel>> LoadAsync<TPixel>(Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> LoadAsync<TPixel>(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="format">The format type of the decoded image.</param>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Stream stream, out IImageFormat format)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(DecoderOptions.Default, stream, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task{ValueTuple}"/> representing the asynchronous operation.</returns>
public static Task<(Image<TPixel> Image, IImageFormat Format)> LoadWithFormatAsync<TPixel>(Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> LoadWithFormatAsync<TPixel>(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <returns><see cref="Image{TPixel}"/>.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Image<TPixel> Load<TPixel>(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> Load<TPixel>(options, stream, out IImageFormat _);
=> WithSeekableStream(options, stream, s => Decode<TPixel>(options, s));
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
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));
format = data.Format;
if (data.Image is null)
{
ThrowNotLoaded(options);
}
return data.Image;
}
/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{ValueTuple}"/> representing the asynchronous operation.</returns>
public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
{
(Image Image, IImageFormat Format) data =
await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
{
ThrowNotLoaded(options);
}
return data;
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException">The options are null.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException">The stream is null.</exception>
/// <exception cref="NotSupportedException">The stream is not readable or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task{ValueTuple}"/> representing the asynchronous operation.</returns>
public static async Task<(Image<TPixel> Image, IImageFormat Format)> LoadWithFormatAsync<TPixel>(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image<TPixel>> LoadAsync<TPixel>(Stream stream, 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);
if (data.Image is null)
{
ThrowNotLoaded(options);
}
return data;
}
=> LoadAsync<TPixel>(DecoderOptions.Default, stream, cancellationToken);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// Creates a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task<Image<TPixel>> LoadAsync<TPixel>(
/// <exception cref="InvalidImageContentException">The encoded image contains invalid content.</exception>
/// <exception cref="UnknownImageFormatException">The encoded image format is unknown.</exception>
public static Task<Image<TPixel>> LoadAsync<TPixel>(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
{
(Image<TPixel> img, _) = await LoadWithFormatAsync<TPixel>(options, stream, cancellationToken)
.ConfigureAwait(false);
return img;
}
/// <summary>
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
/// The pixel format is selected by the decoder.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream containing image information.</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 or the image format is not supported.</exception>
/// <exception cref="UnknownImageFormatException">Image format not recognised.</exception>
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <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)
{
ThrowNotLoaded(options);
}
return img;
}
=> WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync<TPixel>(options, s, ct), cancellationToken);
/// <summary>
/// Performs the given action against the stream ensuring that it is seekable.
@ -591,18 +349,4 @@ public abstract partial class Image
return await action(memoryStream, cancellationToken).ConfigureAwait(false);
}
[DoesNotReturn]
private static void ThrowNotLoaded(DecoderOptions options)
{
StringBuilder sb = new();
sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in options.Configuration.ImageFormatsManager.ImageDecoders)
{
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new UnknownImageFormatException(sb.ToString());
}
}

2
src/ImageSharp/Image.LoadPixelData.cs

@ -72,7 +72,7 @@ public abstract partial class Image
int count = width * height;
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new Image<TPixel>(configuration, width, height);
Image<TPixel> image = new(configuration, width, height);
data = data[..count];
data.CopyTo(image.Frames.RootFrame.PixelBuffer.FastMemoryGroup);

32
src/ImageSharp/Image.cs

@ -14,11 +14,9 @@ namespace SixLabors.ImageSharp;
/// For the non-generic <see cref="Image"/> type, the pixel type is only known at runtime.
/// <see cref="Image"/> is always implemented by a pixel-specific <see cref="Image{TPixel}"/> instance.
/// </summary>
public abstract partial class Image : IImage, IConfigurationProvider
public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProvider
{
private bool isDisposed;
private Size size;
private readonly Configuration configuration;
/// <summary>
@ -27,16 +25,12 @@ public abstract partial class Image : IImage, IConfigurationProvider
/// <param name="configuration">
/// The configuration which allows altering default behaviour or extending the library.
/// </param>
/// <param name="pixelType">The <see cref="PixelTypeInfo"/>.</param>
/// <param name="metadata">The <see cref="ImageMetadata"/>.</param>
/// <param name="size">The <see cref="size"/>.</param>
/// <param name="pixelType">The pixel type information.</param>
/// <param name="metadata">The image metadata.</param>
/// <param name="size">The size in px units.</param>
protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = pixelType;
this.size = size;
this.Metadata = metadata ?? new ImageMetadata();
}
: base(pixelType, size, metadata)
=> this.configuration = configuration ?? Configuration.Default;
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
@ -61,18 +55,6 @@ public abstract partial class Image : IImage, IConfigurationProvider
/// </summary>
protected abstract ImageFrameCollection NonGenericFrameCollection { get; }
/// <inheritdoc/>
public PixelTypeInfo PixelType { get; }
/// <inheritdoc />
public int Width => this.size.Width;
/// <inheritdoc />
public int Height => this.size.Height;
/// <inheritdoc/>
public ImageMetadata Metadata { get; }
/// <summary>
/// Gets the frames of the image as (non-generic) <see cref="ImageFrameCollection"/>.
/// </summary>
@ -148,7 +130,7 @@ public abstract partial class Image : IImage, IConfigurationProvider
/// Update the size of the image after mutation.
/// </summary>
/// <param name="size">The <see cref="Size"/>.</param>
protected void UpdateSize(Size size) => this.size = size;
protected void UpdateSize(Size size) => this.Size = size;
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.

4
src/ImageSharp/ImageExtensions.cs

@ -95,7 +95,7 @@ public static partial class ImageExtensions
throw new NotSupportedException("Cannot write to the stream.");
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.GetEncoder(format);
if (encoder is null)
{
@ -139,7 +139,7 @@ public static partial class ImageExtensions
throw new NotSupportedException("Cannot write to the stream.");
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.GetEncoder(format);
if (encoder is null)
{

58
src/ImageSharp/ImageInfo.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
@ -9,32 +9,60 @@ namespace SixLabors.ImageSharp;
/// <summary>
/// Contains information about the image including dimensions, pixel type information and additional metadata
/// </summary>
internal sealed class ImageInfo : IImageInfo
public class ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageInfo"/> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
/// <param name="pixelType">The pixel type information.</param>
/// <param name="width">The width of the image in px units.</param>
/// <param name="height">The height of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata)
: this(pixelType, new(width, height), metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageInfo"/> class.
/// </summary>
/// <param name="pixelType">The pixel type information.</param>
/// <param name="size">The size of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata metadata)
{
this.PixelType = pixelType;
this.Width = width;
this.Height = height;
this.Metadata = metadata;
this.Metadata = metadata ?? new ImageMetadata();
this.Size = size;
}
/// <inheritdoc />
/// <summary>
/// Gets information about the image pixels.
/// </summary>
public PixelTypeInfo PixelType { get; }
/// <inheritdoc />
public int Width { get; }
/// <summary>
/// Gets the image width in px units.
/// </summary>
public int Width => this.Size.Width;
/// <inheritdoc />
public int Height { get; }
/// <summary>
/// Gets the image height in px units.
/// </summary>
public int Height => this.Size.Height;
/// <inheritdoc />
/// <summary>
/// Gets any metadata associated wit The image.
/// </summary>
public ImageMetadata Metadata { get; }
/// <summary>
/// Gets the size of the image in px units.
/// </summary>
public Size Size { get; internal set; }
/// <summary>
/// Gets the bounds of the image.
/// </summary>
public Rectangle Bounds => new(0, 0, this.Width, this.Height);
}

24
src/ImageSharp/ImageInfoExtensions.cs

@ -1,24 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp;
/// <summary>
/// Extension methods that allow the addition of geometry calculating methods to the <see cref="IImageInfo"/> type
/// </summary>
public static class ImageInfoExtensions
{
/// <summary>
/// Gets the bounds of the image.
/// </summary>
/// <param name="info">The image info</param>
/// <returns>The <see cref="Size"/></returns>
public static Size Size(this IImageInfo info) => new Size(info.Width, info.Height);
/// <summary>
/// Gets the bounds of the image.
/// </summary>
/// <param name="info">The image info</param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle Bounds(this IImageInfo info) => new Rectangle(0, 0, info.Width, info.Height);
}

2
src/ImageSharp/Image{TPixel}.cs

@ -411,7 +411,7 @@ public sealed class Image<TPixel> : Image
this.frames[i].SwapOrCopyPixelsBufferFrom(sourceFrames[i]);
}
this.UpdateSize(pixelSource.Size());
this.UpdateSize(pixelSource.Size);
}
private static Size ValidateFramesAndGetSize(IEnumerable<ImageFrame<TPixel>> frames)

20
src/ImageSharp/Metadata/ImageMetadata.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.Metadata.Profiles.Exif;
@ -69,6 +68,10 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
this.IccProfile = other.IccProfile?.DeepClone();
this.IptcProfile = other.IptcProfile?.DeepClone();
this.XmpProfile = other.XmpProfile?.DeepClone();
// NOTE: This clone is actually shallow but we share the same format
// instances for all images in the configuration.
this.DecodedImageFormat = other.DecodedImageFormat;
}
/// <summary>
@ -137,22 +140,27 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
/// <summary>
/// Gets or sets the Exif profile.
/// </summary>
public ExifProfile ExifProfile { get; set; }
public ExifProfile? ExifProfile { get; set; }
/// <summary>
/// Gets or sets the XMP profile.
/// </summary>
public XmpProfile XmpProfile { get; set; }
public XmpProfile? XmpProfile { get; set; }
/// <summary>
/// Gets or sets the ICC profile.
/// </summary>
public IccProfile IccProfile { get; set; }
public IccProfile? IccProfile { get; set; }
/// <summary>
/// Gets or sets the IPTC profile.
/// </summary>
public IptcProfile IptcProfile { get; set; }
public IptcProfile? IptcProfile { get; set; }
/// <summary>
/// Gets the original format, if any, the image was decode from.
/// </summary>
public IImageFormat? DecodedImageFormat { get; internal set; }
/// <summary>
/// Gets the metadata value associated with the specified key.
@ -165,7 +173,7 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
public TFormatMetadata GetFormatMetadata<TFormatMetadata>(IImageFormat<TFormatMetadata> key)
where TFormatMetadata : class, IDeepCloneable
{
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta))
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta))
{
return (TFormatMetadata)meta;
}

2
src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs

@ -95,5 +95,5 @@ internal class DefaultImageProcessorContext<TPixel> : IInternalImageProcessingCo
return this;
}
private Rectangle GetCurrentBounds() => this.destination?.Bounds() ?? this.source.Bounds();
private Rectangle GetCurrentBounds() => this.destination?.Bounds ?? this.source.Bounds;
}

2
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -78,7 +78,7 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
int locationY = this.Location.Y;
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
Rectangle bounds = targetImage.Bounds;
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right);

38
src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs

@ -27,9 +27,7 @@ internal class EntropyCropProcessor<TPixel> : ImageProcessor<TPixel>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public EntropyCropProcessor(Configuration configuration, EntropyCropProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.definition = definition;
}
=> this.definition = definition;
/// <inheritdoc/>
protected override void BeforeImageApply()
@ -38,7 +36,7 @@ internal class EntropyCropProcessor<TPixel> : ImageProcessor<TPixel>
// TODO: This is clunky. We should add behavior enum to ExtractFrame.
// All frames have be the same size so we only need to calculate the correct dimensions for the first frame
using (var temp = new Image<TPixel>(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() }))
using (Image<TPixel> temp = new(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() }))
{
Configuration configuration = this.Source.GetConfiguration();
@ -52,7 +50,7 @@ internal class EntropyCropProcessor<TPixel> : ImageProcessor<TPixel>
rectangle = GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0);
}
new CropProcessor(rectangle, this.Source.Size()).Execute(this.Configuration, this.Source, this.SourceRectangle);
new CropProcessor(rectangle, this.Source.Size).Execute(this.Configuration, this.Source, this.SourceRectangle);
base.BeforeImageApply();
}
@ -77,7 +75,7 @@ internal class EntropyCropProcessor<TPixel> : ImageProcessor<TPixel>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
=> new Rectangle(
=> new(
topLeft.X,
topLeft.Y,
bottomRight.X - topLeft.X,
@ -99,29 +97,13 @@ internal class EntropyCropProcessor<TPixel> : ImageProcessor<TPixel>
int height = bitmap.Height;
Point topLeft = default;
Point bottomRight = default;
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc;
// Determine which channel to check against
switch (channel)
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc = channel switch
{
case RgbaComponent.R:
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon;
break;
case RgbaComponent.G:
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon;
break;
case RgbaComponent.B:
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon;
break;
default:
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon;
break;
}
RgbaComponent.R => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon,
RgbaComponent.G => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon,
RgbaComponent.B => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon,
_ => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon,
};
int GetMinY(ImageFrame<TPixel> pixels)
{
for (int y = 0; y < height; y++)

2
src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs

@ -43,7 +43,7 @@ internal class RotateProcessor<TPixel> : AffineTransformProcessor<TPixel>
/// <inheritdoc/>
protected override void AfterImageApply(Image<TPixel> destination)
{
ExifProfile profile = destination.Metadata.ExifProfile;
ExifProfile? profile = destination.Metadata.ExifProfile;
if (profile is null)
{
return;

2
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -82,7 +82,7 @@ internal class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResampling
return;
}
var interest = Rectangle.Intersect(destinationRectangle, destination.Bounds());
var interest = Rectangle.Intersect(destinationRectangle, destination.Bounds);
if (sampler is NearestNeighborResampler)
{

2
src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs

@ -19,7 +19,7 @@ internal static class TransformProcessorHelpers
public static void UpdateDimensionalMetadata<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
ExifProfile profile = image.Metadata.ExifProfile;
ExifProfile? profile = image.Metadata.ExifProfile;
if (profile is null)
{
return;

2
tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs

@ -22,7 +22,7 @@ public class IdentifyJpeg
public void ReadImages() => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath);
[Benchmark]
public IImageInfo Identify()
public ImageInfo Identify()
{
using MemoryStream memoryStream = new(this.jpegBytes);
return JpegDecoder.Instance.Identify(DecoderOptions.Default, memoryStream);

4
tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs

@ -56,8 +56,8 @@ public class DecodeFilteredPng
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Size LoadPng(byte[] bytes)
{
using var image = Image.Load<Rgba32>(bytes);
return image.Size();
using Image<Rgba32> image = Image.Load<Rgba32>(bytes);
return image.Size;
}
private static string TestImageFullPath(string path)

17
tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs

@ -22,26 +22,21 @@ public class DecodePng
[GlobalSetup]
public void ReadImages()
{
if (this.pngBytes == null)
{
this.pngBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
=> this.pngBytes ??= File.ReadAllBytes(this.TestImageFullPath);
[Benchmark(Baseline = true, Description = "System.Drawing Png")]
public SDSize PngSystemDrawing()
{
using var memoryStream = new MemoryStream(this.pngBytes);
using var image = SDImage.FromStream(memoryStream);
using MemoryStream memoryStream = new(this.pngBytes);
using SDImage image = SDImage.FromStream(memoryStream);
return image.Size;
}
[Benchmark(Description = "ImageSharp Png")]
public Size PngImageSharp()
{
using var memoryStream = new MemoryStream(this.pngBytes);
using var image = Image.Load<Rgba32>(memoryStream);
return image.Size();
using MemoryStream memoryStream = new(this.pngBytes);
using Image<Rgba32> image = Image.Load<Rgba32>(memoryStream);
return image.Size;
}
}

16
tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs

@ -69,20 +69,16 @@ public class DecodeTiff
[Benchmark(Baseline = true, Description = "System.Drawing Tiff")]
public SDSize TiffSystemDrawing()
{
using (var memoryStream = new MemoryStream(this.data))
using (var image = SDImage.FromStream(memoryStream))
{
return image.Size;
}
using MemoryStream memoryStream = new(this.data);
using SDImage image = SDImage.FromStream(memoryStream);
return image.Size;
}
[Benchmark(Description = "ImageSharp Tiff")]
public Size TiffCore()
{
using (var ms = new MemoryStream(this.data))
using (var image = Image.Load<Rgba32>(ms))
{
return image.Size();
}
using MemoryStream ms = new(this.data);
using Image<Rgba32> image = Image.Load<Rgba32>(ms);
return image.Size;
}
}

8
tests/ImageSharp.Benchmarks/Processing/Diffuse.cs

@ -13,19 +13,19 @@ public class Diffuse
[Benchmark]
public Size DoDiffuse()
{
using var image = new Image<Rgba32>(Configuration.Default, 800, 800, Color.BlanchedAlmond);
using Image<Rgba32> image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond);
image.Mutate(x => x.Dither(KnownDitherings.FloydSteinberg));
return image.Size();
return image.Size;
}
[Benchmark]
public Size DoDither()
{
using var image = new Image<Rgba32>(Configuration.Default, 800, 800, Color.BlanchedAlmond);
using Image<Rgba32> image = new(Configuration.Default, 800, 800, Color.BlanchedAlmond);
image.Mutate(x => x.Dither());
return image.Size();
return image.Size;
}
}

4
tests/ImageSharp.Benchmarks/Processing/Rotate.cs

@ -13,10 +13,10 @@ public class Rotate
[Benchmark]
public Size DoRotate()
{
using var image = new Image<Rgba32>(Configuration.Default, 400, 400, Color.BlanchedAlmond);
using Image<Rgba32> image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond);
image.Mutate(x => x.Rotate(37.5F));
return image.Size();
return image.Size;
}
}

4
tests/ImageSharp.Benchmarks/Processing/Skew.cs

@ -13,10 +13,10 @@ public class Skew
[Benchmark]
public Size DoSkew()
{
using var image = new Image<Rgba32>(Configuration.Default, 400, 400, Color.BlanchedAlmond);
using Image<Rgba32> image = new(Configuration.Default, 400, 400, Color.BlanchedAlmond);
image.Mutate(x => x.Skew(20, 10));
return image.Size();
return image.Size;
}
}

12
tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs

@ -30,7 +30,7 @@ public class AdvancedImageExtensionsTests
IMemoryGroup<TPixel> memoryGroup = image.GetPixelMemoryGroup();
// Assert:
VerifyMemoryGroupDataMatchesTestPattern(provider, memoryGroup, image.Size());
VerifyMemoryGroupDataMatchesTestPattern(provider, memoryGroup, image.Size);
}
[Theory]
@ -57,23 +57,23 @@ public class AdvancedImageExtensionsTests
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image0 = provider.GetImage();
var targetBuffer = new TPixel[image0.Width * image0.Height];
TPixel[] targetBuffer = new TPixel[image0.Width * image0.Height];
Assert.True(image0.DangerousTryGetSinglePixelMemory(out Memory<TPixel> sourceBuffer));
sourceBuffer.CopyTo(targetBuffer);
var managerOfExternalMemory = new TestMemoryManager<TPixel>(targetBuffer);
TestMemoryManager<TPixel> managerOfExternalMemory = new(targetBuffer);
Memory<TPixel> externalMemory = managerOfExternalMemory.Memory;
using (var image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
using (Image<TPixel> image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
{
VerifyMemoryGroupDataMatchesTestPattern(provider, image1.GetPixelMemoryGroup(), image1.Size());
VerifyMemoryGroupDataMatchesTestPattern(provider, image1.GetPixelMemoryGroup(), image1.Size);
}
// Make sure externalMemory works after destruction:
VerifyMemoryGroupDataMatchesTestPattern(provider, image0.GetPixelMemoryGroup(), image0.Size());
VerifyMemoryGroupDataMatchesTestPattern(provider, image0.GetPixelMemoryGroup(), image0.Size);
}
private static void VerifyMemoryGroupDataMatchesTestPattern<TPixel>(

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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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))
{
IImageInfo 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);
ImageInfo imageInfo = Image.Identify(stream);
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);
}
}

72
tests/ImageSharp.Tests/Formats/Bmp/ImageExtensionsTest.cs

@ -15,15 +15,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsBmp_Path.bmp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsBmp(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is BmpFormat);
}
[Fact]
@ -32,15 +30,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsBmpAsync_Path.bmp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsBmpAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is BmpFormat);
}
[Fact]
@ -49,15 +45,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsBmp_Path_Encoder.bmp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsBmp(file, new BmpEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is BmpFormat);
}
[Fact]
@ -66,86 +60,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsBmpAsync_Path_Encoder.bmp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsBmpAsync(file, new BmpEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is BmpFormat);
}
[Fact]
public void SaveAsBmp_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsBmp(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is BmpFormat);
}
[Fact]
public async Task SaveAsBmpAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsBmpAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is BmpFormat);
}
[Fact]
public void SaveAsBmp_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsBmp(memoryStream, new BmpEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is BmpFormat);
}
[Fact]
public async Task SaveAsBmpAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsBmpAsync(memoryStream, new BmpEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/bmp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is BmpFormat);
}
}

19
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;
@ -208,10 +207,10 @@ public class GeneralFormatTests
foreach (TestFile file in Files)
{
byte[] serialized;
using (Image image = Image.Load(file.Bytes, out IImageFormat mimeType))
using (Image image = Image.Load(file.Bytes))
using (MemoryStream memoryStream = new())
{
image.Save(memoryStream, mimeType);
image.Save(memoryStream, image.Metadata.DecodedImageFormat);
memoryStream.Flush();
serialized = memoryStream.ToArray();
}
@ -257,27 +256,21 @@ public class GeneralFormatTests
image.Save(memoryStream, format);
memoryStream.Position = 0;
IImageInfo imageInfo = Image.Identify(memoryStream);
ImageInfo imageInfo = Image.Identify(memoryStream);
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]
public void IdentifyReturnsNullWithInvalidStream()
public void Identify_UnknownImageFormatException_WithInvalidStream()
{
byte[] invalid = new byte[10];
using MemoryStream memoryStream = new(invalid);
IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format);
Assert.Null(imageInfo);
Assert.Null(format);
Assert.Throws<UnknownImageFormatException>(() => Image.Identify(invalid));
}
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);
ImageInfo imageInfo = Image.Identify(stream);
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++)
{

6
tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs

@ -113,7 +113,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -126,7 +126,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
ImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -165,7 +165,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
GifMetadata meta = image.Metadata.GetGifMetadata();
Assert.Equal(repeatCount, meta.RepeatCount);
}

72
tests/ImageSharp.Tests/Formats/Gif/ImageExtensionsTest.cs

@ -15,15 +15,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsGif_Path.gif");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsGif(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is GifFormat);
}
[Fact]
@ -32,15 +30,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsGifAsync_Path.gif");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsGifAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is GifFormat);
}
[Fact]
@ -49,15 +45,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsGif_Path_Encoder.gif");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsGif(file, new GifEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is GifFormat);
}
[Fact]
@ -66,86 +60,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsGifAsync_Path_Encoder.gif");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsGifAsync(file, new GifEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is GifFormat);
}
[Fact]
public void SaveAsGif_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsGif(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is GifFormat);
}
[Fact]
public async Task SaveAsGifAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsGifAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is GifFormat);
}
[Fact]
public void SaveAsGif_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsGif(memoryStream, new GifEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is GifFormat);
}
[Fact]
public async Task SaveAsGifAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsGifAsync(memoryStream, new GifEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/gif", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is GifFormat);
}
}

21
tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs

@ -74,12 +74,12 @@ public class ImageFormatManagerTests
{
IImageEncoder encoder1 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1);
IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
IImageEncoder found = this.FormatsManagerEmpty.GetEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder1, found);
IImageEncoder encoder2 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2);
IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
IImageEncoder found2 = this.FormatsManagerEmpty.GetEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}
@ -89,12 +89,12 @@ public class ImageFormatManagerTests
{
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
IImageDecoder found = this.FormatsManagerEmpty.GetDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
IImageDecoder found2 = this.FormatsManagerEmpty.GetDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2);
}
@ -120,15 +120,10 @@ public class ImageFormatManagerTests
jpegImage = buffer.ToArray();
}
byte[] invalidImage = { 1, 2, 3 };
bool resultValidImage = Image.TryDetectFormat(jpegImage, out IImageFormat format);
IImageFormat format = Image.DetectFormat(jpegImage);
Assert.IsType<JpegFormat>(format);
bool resultInvalidImage = Image.TryDetectFormat(invalidImage, out IImageFormat formatInvalid);
Assert.True(resultValidImage);
Assert.Equal(format, JpegFormat.Instance);
Assert.False(resultInvalidImage);
Assert.True(formatInvalid is null);
byte[] invalidImage = { 1, 2, 3 };
Assert.Throws<UnknownImageFormatException>(() => Image.DetectFormat(invalidImage));
}
}

72
tests/ImageSharp.Tests/Formats/Jpg/ImageExtensionsTest.cs

@ -16,15 +16,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsJpeg_Path.jpg");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsJpeg(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is JpegFormat);
}
[Fact]
@ -33,15 +31,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsJpegAsync_Path.jpg");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsJpegAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is JpegFormat);
}
[Fact]
@ -50,15 +46,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsJpeg_Path_Encoder.jpg");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsJpeg(file, new JpegEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is JpegFormat);
}
[Fact]
@ -67,86 +61,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsJpegAsync_Path_Encoder.jpg");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsJpegAsync(file, new JpegEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is JpegFormat);
}
[Fact]
public void SaveAsJpeg_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsJpeg(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is JpegFormat);
}
[Fact]
public async Task SaveAsJpegAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsJpegAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is JpegFormat);
}
[Fact]
public void SaveAsJpeg_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsJpeg(memoryStream, new JpegEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is JpegFormat);
}
[Fact]
public async Task SaveAsJpegAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsJpegAsync(memoryStream, new JpegEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/jpeg", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is JpegFormat);
}
}

54
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs

@ -75,8 +75,8 @@ public partial class JpegDecoderTests
[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 image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
@ -88,9 +88,9 @@ public partial class JpegDecoderTests
[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);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -101,9 +101,9 @@ public partial class JpegDecoderTests
[MemberData(nameof(RatioFiles))]
public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = await JpegDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = await JpegDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -114,9 +114,9 @@ public partial class JpegDecoderTests
[MemberData(nameof(QualityFiles))]
public void Identify_VerifyQuality(string imagePath, int quality)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
}
@ -125,8 +125,8 @@ public partial class JpegDecoderTests
[MemberData(nameof(QualityFiles))]
public void Decode_VerifyQuality(string imagePath, int quality)
{
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 image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
@ -136,8 +136,8 @@ public partial class JpegDecoderTests
[MemberData(nameof(QualityFiles))]
public async Task Decode_VerifyQualityAsync(string imagePath, int quality)
{
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 image = await JpegDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
@ -153,9 +153,9 @@ public partial class JpegDecoderTests
[InlineData(TestImages.Jpeg.Baseline.Jpeg411, JpegEncodingColor.YCbCrRatio411)]
public void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor expectedColorType)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(expectedColorType, meta.ColorType);
}
@ -174,13 +174,13 @@ public partial class JpegDecoderTests
Assert.Equal(expectedColorType, meta.ColorType);
}
private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action<IImageInfo> test)
private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action<ImageInfo> test)
{
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);
if (useIdentify)
{
IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream);
ImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream);
test(imageInfo);
}
else
@ -318,10 +318,10 @@ public partial class JpegDecoderTests
[Fact]
public void EncodedStringTags_WriteAndRead()
{
using var memoryStream = new MemoryStream();
using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora)))
using MemoryStream memoryStream = new();
using (Image image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora)))
{
var exif = new ExifProfile();
ExifProfile exif = new();
exif.SetValue(ExifTag.GPSDateStamp, "2022-01-06");
@ -343,7 +343,7 @@ public partial class JpegDecoderTests
}
memoryStream.Seek(0, SeekOrigin.Begin);
using (var image = Image.Load(memoryStream))
using (Image image = Image.Load(memoryStream))
{
ExifProfile exif = image.Metadata.ExifProfile;
VerifyEncodedStrings(exif);
@ -353,7 +353,7 @@ public partial class JpegDecoderTests
[Fact]
public void EncodedStringTags_Read()
{
using var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings));
using Image image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings));
ExifProfile exif = image.Metadata.ExifProfile;
VerifyEncodedStrings(exif);
}

10
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -35,7 +35,7 @@ public partial class JpegDecoderTests
if (!CustomToleranceValues.TryGetValue(file, out float tolerance))
{
bool baseline = file.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) >= 0;
bool baseline = file.Contains("baseline", StringComparison.OrdinalIgnoreCase);
tolerance = baseline ? BaselineTolerance : ProgressiveTolerance;
}
@ -68,9 +68,9 @@ public partial class JpegDecoderTests
{
JpegDecoderOptions options = new();
byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes;
using var ms = new MemoryStream(bytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms);
using var decoder = new JpegDecoderCore(options);
using MemoryStream ms = new(bytes);
using BufferedReadStream bufferedStream = new(Configuration.Default, ms);
using JpegDecoderCore decoder = new(options);
using Image<Rgba32> image = decoder.Decode<Rgba32>(bufferedStream, cancellationToken: default);
// I don't know why these numbers are different. All I know is that the decoder works
@ -85,7 +85,7 @@ public partial class JpegDecoderTests
public void Decode_NonGeneric_CreatesRgb24Image()
{
string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
using var image = Image.Load(file);
using Image image = Image.Load(file);
Assert.IsType<Image<Rgb24>>(image);
}

72
tests/ImageSharp.Tests/Formats/Pbm/ImageExtensionsTest.cs

@ -15,15 +15,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPbm_Path.pbm");
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
image.SaveAsPbm(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PbmFormat);
}
[Fact]
@ -32,15 +30,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPbmAsync_Path.pbm");
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
await image.SaveAsPbmAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PbmFormat);
}
[Fact]
@ -49,15 +45,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPbm_Path_Encoder.pbm");
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
image.SaveAsPbm(file, new PbmEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PbmFormat);
}
[Fact]
@ -66,86 +60,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPbmAsync_Path_Encoder.pbm");
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
await image.SaveAsPbmAsync(file, new PbmEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PbmFormat);
}
[Fact]
public void SaveAsPbm_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
image.SaveAsPbm(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PbmFormat);
}
[Fact]
public async Task SaveAsPbmAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
await image.SaveAsPbmAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PbmFormat);
}
[Fact]
public void SaveAsPbm_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
image.SaveAsPbm(memoryStream, new PbmEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PbmFormat);
}
[Fact]
public async Task SaveAsPbmAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<L8>(10, 10))
using (Image<L8> image = new(10, 10))
{
await image.SaveAsPbmAsync(memoryStream, new PbmEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/x-portable-pixmap", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PbmFormat);
}
}

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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
PbmMetadata bitmapMetadata = imageInfo.Metadata.GetPbmMetadata();
Assert.NotNull(bitmapMetadata);

72
tests/ImageSharp.Tests/Formats/Png/ImageExtensionsTest.cs

@ -16,15 +16,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPng_Path.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsPng(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
@ -33,15 +31,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsPngAsync_Path.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsPngAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
@ -50,15 +46,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPng_Path_Encoder.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsPng(file, new PngEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
@ -67,86 +61,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsPngAsync_Path_Encoder.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsPngAsync(file, new PngEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
public void SaveAsPng_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsPng(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PngFormat);
}
[Fact]
public async Task SaveAsPngAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsPngAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PngFormat);
}
[Fact]
public void SaveAsPng_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsPng(memoryStream, new PngEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PngFormat);
}
[Fact]
public async Task SaveAsPngAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsPngAsync(memoryStream, new PngEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is PngFormat);
}
}

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);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
PngMetadata metadata = imageInfo.Metadata.GetPngMetadata();
Assert.True(metadata.HasTransparency);
}

48
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,9 +217,9 @@ 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);
IImageInfo image = PngDecoder.Instance.Identify(DecoderOptions.Default, stream);
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);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.NotNull(imageInfo.Metadata.ExifProfile);

72
tests/ImageSharp.Tests/Formats/Tga/ImageExtensionsTest.cs

@ -15,15 +15,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTga_Path.tga");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTga(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TgaFormat);
}
[Fact]
@ -32,15 +30,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTgaAsync_Path.tga");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTgaAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TgaFormat);
}
[Fact]
@ -49,15 +45,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTga_Path_Encoder.tga");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTga(file, new TgaEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TgaFormat);
}
[Fact]
@ -66,86 +60,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTgaAsync_Path_Encoder.tga");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTgaAsync(file, new TgaEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TgaFormat);
}
[Fact]
public void SaveAsTga_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTga(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TgaFormat);
}
[Fact]
public async Task SaveAsTgaAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTgaAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TgaFormat);
}
[Fact]
public void SaveAsTga_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTga(memoryStream, new TgaEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TgaFormat);
}
[Fact]
public async Task SaveAsTgaAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTgaAsync(memoryStream, new TgaEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tga", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TgaFormat);
}
}

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

@ -22,11 +22,11 @@ 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>(() =>
{
using (Image.Load(DecoderOptions.Default, stream, out IImageFormat _))
using (Image.Load(DecoderOptions.Default, stream))
{
}
});
@ -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);
IImageInfo info = Image.Identify(stream);
ImageInfo info = Image.Identify(stream);
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))
{
IImageInfo 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);
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);
}
[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))
{
IImageInfo info = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo info = Image.Identify(stream);
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]

72
tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs

@ -16,15 +16,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTiff_Path.tiff");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTiff(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TiffFormat);
}
[Fact]
@ -33,15 +31,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTiffAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TiffFormat);
}
[Fact]
@ -50,15 +46,13 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTiff(file, new TiffEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TiffFormat);
}
[Fact]
@ -67,86 +61,76 @@ public class ImageExtensionsTest
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTiffAsync(file, new TiffEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is TiffFormat);
}
[Fact]
public void SaveAsTiff_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTiff(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TiffFormat);
}
[Fact]
public async Task SaveAsTiffAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTiffAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TiffFormat);
}
[Fact]
public void SaveAsTiff_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsTiff(memoryStream, new TiffEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TiffFormat);
}
[Fact]
public async Task SaveAsTiffAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsTiffAsync(memoryStream, new TiffEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is TiffFormat);
}
}

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))
{
IImageInfo 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);
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);
}
[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))
{
IImageInfo info = Image.Identify(stream);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo info = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
ImageInfo imageInfo = Image.Identify(stream);
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);
IImageInfo imageInfo = Image.Identify(stream);
ImageInfo imageInfo = Image.Identify(stream);
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;

72
tests/ImageSharp.Tests/Formats/WebP/ImageExtensionsTests.cs

@ -16,15 +16,13 @@ public class ImageExtensionsTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTests));
string file = Path.Combine(dir, "SaveAsWebp_Path.webp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsWebp(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is WebpFormat);
}
[Fact]
@ -33,15 +31,13 @@ public class ImageExtensionsTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTests));
string file = Path.Combine(dir, "SaveAsWebpAsync_Path.webp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsWebpAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is WebpFormat);
}
[Fact]
@ -50,15 +46,13 @@ public class ImageExtensionsTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsWebp_Path_Encoder.webp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsWebp(file, new WebpEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is WebpFormat);
}
[Fact]
@ -67,86 +61,76 @@ public class ImageExtensionsTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsWebpAsync_Path_Encoder.webp");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsWebpAsync(file, new WebpEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is WebpFormat);
}
[Fact]
public void SaveAsWebp_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsWebp(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is WebpFormat);
}
[Fact]
public async Task SaveAsWebpAsync_StreamAsync()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsWebpAsync(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is WebpFormat);
}
[Fact]
public void SaveAsWebp_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsWebp(memoryStream, new WebpEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is WebpFormat);
}
[Fact]
public async Task SaveAsWebpAsync_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsWebpAsync(memoryStream, new WebpEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/webp", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is WebpFormat);
}
}

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);
IImageInfo imageInfo = Image.Identify(stream);
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
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);

12
tests/ImageSharp.Tests/Image/ImageRotationTests.cs

@ -45,12 +45,10 @@ public class ImageRotationTests
private static (Size Original, Size Rotated) Rotate(int angle)
{
var file = TestFile.Create(TestImages.Bmp.Car);
using (var image = Image.Load<Rgba32>(file.FullPath))
{
Size original = image.Size();
image.Mutate(x => x.Rotate(angle));
return (original, image.Size());
}
TestFile file = TestFile.Create(TestImages.Bmp.Car);
using Image<Rgba32> image = Image.Load<Rgba32>(file.FullPath);
Size original = image.Size;
image.Mutate(x => x.Rotate(angle));
return (original, image.Size);
}
}

82
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,10 +34,8 @@ public partial class ImageTests
[Fact]
public void FromBytes_GlobalConfiguration()
{
bool result = Image.TryDetectFormat(this.ActualImageSpan, out IImageFormat type);
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
IImageFormat format = Image.DetectFormat(ActualImageSpan);
Assert.Equal(ExpectedGlobalFormat, format);
}
[Fact]
@ -48,19 +46,22 @@ public partial class ImageTests
Configuration = this.LocalConfiguration
};
bool result = Image.TryDetectFormat(options, this.ByteArray, out IImageFormat type);
Assert.True(result);
Assert.Equal(this.LocalImageFormat, type);
IImageFormat format = Image.DetectFormat(options, this.ByteArray);
Assert.Equal(this.LocalImageFormat, format);
}
[Fact]
public void FromFileSystemPath_GlobalConfiguration()
{
bool result = Image.TryDetectFormat(ActualImagePath, out IImageFormat type);
IImageFormat format = Image.DetectFormat(ActualImagePath);
Assert.Equal(ExpectedGlobalFormat, format);
}
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
[Fact]
public async Task FromFileSystemPathAsync_GlobalConfiguration()
{
IImageFormat format = await Image.DetectFormatAsync(ActualImagePath);
Assert.Equal(ExpectedGlobalFormat, format);
}
[Fact]
@ -71,22 +72,29 @@ public partial class ImageTests
Configuration = this.LocalConfiguration
};
bool result = Image.TryDetectFormat(options, this.MockFilePath, out IImageFormat type);
IImageFormat format = Image.DetectFormat(options, this.MockFilePath);
Assert.Equal(this.LocalImageFormat, format);
}
Assert.True(result);
Assert.Equal(this.LocalImageFormat, type);
[Fact]
public async Task FromFileSystemPathAsync_CustomConfiguration()
{
DecoderOptions options = new()
{
Configuration = this.LocalConfiguration
};
IImageFormat format = await Image.DetectFormatAsync(options, this.MockFilePath);
Assert.Equal(this.LocalImageFormat, format);
}
[Fact]
public void FromStream_GlobalConfiguration()
{
using (var stream = new MemoryStream(this.ActualImageBytes))
{
bool result = Image.TryDetectFormat(stream, out IImageFormat type);
using MemoryStream stream = new(ActualImageBytes);
IImageFormat format = Image.DetectFormat(stream);
Assert.True(result);
Assert.Equal(ExpectedGlobalFormat, type);
}
Assert.Equal(ExpectedGlobalFormat, format);
}
[Fact]
@ -97,34 +105,27 @@ public partial class ImageTests
Configuration = this.LocalConfiguration
};
bool result = Image.TryDetectFormat(options, this.DataStream, out IImageFormat type);
Assert.True(result);
Assert.Equal(this.LocalImageFormat, type);
IImageFormat format = Image.DetectFormat(options, this.DataStream);
Assert.Equal(this.LocalImageFormat, format);
}
[Fact]
public void WhenNoMatchingFormatFound_ReturnsNull()
public void WhenNoMatchingFormatFound_Throws_UnknownImageFormatException()
{
DecoderOptions options = new()
{
Configuration = new()
};
bool result = Image.TryDetectFormat(options, this.DataStream, out IImageFormat type);
Assert.False(result);
Assert.Null(type);
Assert.Throws<UnknownImageFormatException>(() => Image.DetectFormat(options, this.DataStream));
}
[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);
IImageFormat format = await Image.DetectFormatAsync(new AsyncStreamWrapper(stream, () => false));
Assert.Equal(ExpectedGlobalFormat, format);
}
[Fact]
@ -135,20 +136,19 @@ public partial class ImageTests
Configuration = this.LocalConfiguration
};
IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Equal(this.LocalImageFormat, type);
IImageFormat format = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Equal(this.LocalImageFormat, format);
}
[Fact]
public async Task WhenNoMatchingFormatFoundAsync_ReturnsNull()
public Task WhenNoMatchingFormatFoundAsync_Throws_UnknownImageFormatException()
{
DecoderOptions options = new()
{
Configuration = new()
};
IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false));
Assert.Null(type);
return Assert.ThrowsAsync<UnknownImageFormatException>(async () => await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false)));
}
}
}

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

@ -21,47 +21,39 @@ public partial class ImageTests
private static byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private IImageInfo LocalImageInfo => this.localImageInfoMock.Object;
private IImageFormat LocalImageFormat => this.localImageFormatMock.Object;
private static IImageFormat ExpectedGlobalFormat
{
get
{
Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension("bmp", out var format);
Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension("bmp", out IImageFormat format);
return format!;
}
}
[Fact]
public void FromBytes_GlobalConfiguration()
{
IImageInfo info = Image.Identify(ActualImageBytes, out IImageFormat type);
Assert.Equal(ExpectedImageSize, info.Size());
Assert.Equal(ExpectedGlobalFormat, type);
ImageInfo info = Image.Identify(ActualImageBytes);
Assert.Equal(ExpectedImageSize, info.Size);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public void FromBytes_CustomConfiguration()
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
IImageInfo info = Image.Identify(options, this.ByteArray, out IImageFormat type);
ImageInfo info = Image.Identify(options, this.ByteArray);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromFileSystemPath_GlobalConfiguration()
{
IImageInfo info = Image.Identify(ActualImagePath, out IImageFormat type);
ImageInfo info = Image.Identify(ActualImagePath);
Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
@ -69,27 +61,26 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
IImageInfo info = Image.Identify(options, this.MockFilePath, out IImageFormat type);
ImageInfo info = Image.Identify(options, this.MockFilePath);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromStream_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
IImageInfo info = Image.Identify(stream, out IImageFormat type);
using MemoryStream stream = new(ActualImageBytes);
ImageInfo info = Image.Identify(stream);
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);
IImageInfo info = Image.Identify(stream);
using MemoryStream stream = new(ActualImageBytes);
ImageInfo info = Image.Identify(stream);
Assert.NotNull(info);
}
@ -97,22 +88,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);
IImageInfo info = Image.Identify(nonSeekableStream, out IImageFormat type);
ImageInfo info = Image.Identify(nonSeekableStream);
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);
IImageInfo info = Image.Identify(nonSeekableStream);
ImageInfo info = Image.Identify(nonSeekableStream);
Assert.NotNull(info);
}
@ -122,10 +113,9 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
IImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type);
ImageInfo info = Image.Identify(options, this.DataStream);
Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
@ -133,27 +123,24 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
IImageInfo info = Image.Identify(options, this.DataStream);
ImageInfo info = Image.Identify(options, this.DataStream);
Assert.Equal(this.LocalImageInfo, info);
}
[Fact]
public void WhenNoMatchingFormatFound_ReturnsNull()
public void WhenNoMatchingFormatFound_Throws_UnknownImageFormatException()
{
DecoderOptions options = new() { Configuration = new() };
IImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type);
Assert.Null(info);
Assert.Null(type);
Assert.Throws<UnknownImageFormatException>(() => Image.Identify(options, this.DataStream));
}
[Fact]
public void FromStream_ZeroLength_ReturnsNull()
public void FromStream_ZeroLength_Throws_UnknownImageFormatException()
{
// 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,
@ -168,39 +155,39 @@ public partial class ImageTests
0x00, 0x00, 0x00, 0x00
}));
using Stream stream = zipFile.Entries[0].Open();
IImageInfo info = Image.Identify(stream);
Assert.Null(info);
Assert.Throws<UnknownImageFormatException>(() => Image.Identify(stream));
}
[Fact]
public async Task FromStreamAsync_GlobalConfiguration_NoFormat()
{
using var stream = new MemoryStream(ActualImageBytes);
var asyncStream = new AsyncStreamWrapper(stream, () => false);
IImageInfo info = await Image.IdentifyAsync(asyncStream);
using MemoryStream stream = new(ActualImageBytes);
AsyncStreamWrapper asyncStream = new(stream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
Assert.NotNull(info);
}
[Fact]
public async Task FromStreamAsync_GlobalConfiguration()
{
using var stream = new MemoryStream(ActualImageBytes);
var asyncStream = new AsyncStreamWrapper(stream, () => false);
(IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream);
using MemoryStream stream = new(ActualImageBytes);
AsyncStreamWrapper asyncStream = new(stream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.Equal(ExpectedImageSize, info.Size);
Assert.Equal(ExpectedGlobalFormat, info.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);
IImageInfo info = await Image.IdentifyAsync(asyncStream);
AsyncStreamWrapper asyncStream = new(nonSeekableStream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
Assert.NotNull(info);
}
@ -208,21 +195,21 @@ public partial class ImageTests
[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);
(IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream);
AsyncStreamWrapper asyncStream = new(nonSeekableStream, () => false);
ImageInfo info = await Image.IdentifyAsync(asyncStream);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.Equal(ExpectedImageSize, info.Size);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public async Task FromStreamAsync_ZeroLength_ReturnsNull()
public async Task FromStreamAsync_ZeroLength_Throws_UnknownImageFormatException()
{
// 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,
@ -237,8 +224,8 @@ public partial class ImageTests
0x00, 0x00, 0x00, 0x00
}));
using Stream stream = zipFile.Entries[0].Open();
IImageInfo info = await Image.IdentifyAsync(stream);
Assert.Null(info);
await Assert.ThrowsAsync<UnknownImageFormatException>(async () => await Image.IdentifyAsync(stream));
}
[Fact]
@ -246,7 +233,8 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
IImageInfo info = await Image.IdentifyAsync(options, this.MockFilePath);
ImageInfo info = await Image.IdentifyAsync(options, this.MockFilePath);
Assert.Equal(this.LocalImageInfo, info);
}
@ -255,27 +243,26 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
(IImageInfo ImageInfo, IImageFormat Format) info =
await Image.IdentifyWithFormatAsync(options, this.MockFilePath);
Assert.NotNull(info.ImageInfo);
Assert.Equal(this.LocalImageFormat, info.Format);
ImageInfo info = await Image.IdentifyAsync(options, this.MockFilePath);
Assert.NotNull(info);
}
[Fact]
public async Task IdentifyWithFormatAsync_FromPath_GlobalConfiguration()
{
(IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(ActualImagePath);
ImageInfo info = await Image.IdentifyAsync(ActualImagePath);
Assert.Equal(ExpectedImageSize, res.ImageInfo.Size());
Assert.Equal(ExpectedGlobalFormat, res.Format);
Assert.Equal(ExpectedImageSize, info.Size);
Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat);
}
[Fact]
public async Task FromPathAsync_GlobalConfiguration()
{
IImageInfo info = await Image.IdentifyAsync(ActualImagePath);
ImageInfo info = await Image.IdentifyAsync(ActualImagePath);
Assert.Equal(ExpectedImageSize, info.Size());
Assert.Equal(ExpectedImageSize, info.Size);
}
[Fact]
@ -283,24 +270,19 @@ public partial class ImageTests
{
DecoderOptions options = new() { Configuration = this.LocalConfiguration };
var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false);
(IImageInfo ImageInfo, IImageFormat Format)
info = await Image.IdentifyWithFormatAsync(options, asyncStream);
AsyncStreamWrapper asyncStream = new(this.DataStream, () => false);
ImageInfo info = await Image.IdentifyAsync(options, asyncStream);
Assert.Equal(this.LocalImageInfo, info.ImageInfo);
Assert.Equal(this.LocalImageFormat, info.Format);
Assert.Equal(this.LocalImageInfo, info);
}
[Fact]
public async Task WhenNoMatchingFormatFoundAsync_ReturnsNull()
public Task WhenNoMatchingFormatFoundAsync_Throws_UnknownImageFormatException()
{
DecoderOptions options = new() { Configuration = new() };
var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false);
(IImageInfo ImageInfo, IImageFormat Format)
info = await Image.IdentifyWithFormatAsync(options, asyncStream);
Assert.Null(info.ImageInfo);
AsyncStreamWrapper asyncStream = new(this.DataStream, () => false);
return Assert.ThrowsAsync<UnknownImageFormatException>(async () => await Image.IdentifyAsync(options, asyncStream));
}
}
}

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

@ -5,6 +5,7 @@ using Moq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests;
@ -25,13 +26,11 @@ public partial class ImageTests
protected Mock<IImageFormat> localImageFormatMock;
protected Mock<IImageInfo> localImageInfoMock;
protected readonly string MockFilePath = Guid.NewGuid().ToString();
internal readonly Mock<IFileSystem> LocalFileSystemMock = new Mock<IFileSystem>();
internal readonly Mock<IFileSystem> LocalFileSystemMock = new();
protected readonly TestFileSystem topLevelFileSystem = new TestFileSystem();
protected readonly TestFileSystem topLevelFileSystem = new();
public Configuration LocalConfiguration { get; }
@ -51,26 +50,29 @@ public partial class ImageTests
protected byte[] ByteArray => ((MemoryStream)this.DataStream).ToArray();
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.LocalImageInfo = new(new PixelTypeInfo(8), new(1, 1), new ImageMetadata());
this.localImageInfoMock = new Mock<IImageInfo>();
this.localImageFormatMock = new Mock<IImageFormat>();
this.localDecoder = new Mock<IImageDecoder>();
this.localDecoder.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Returns(this.localImageInfoMock.Object);
.Returns(this.LocalImageInfo);
this.localDecoder.Setup(x => x.IdentifyAsync(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(this.localImageInfoMock.Object));
.Returns(Task.FromResult(this.LocalImageInfo));
this.localDecoder
.Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Callback<DecoderOptions, Stream>((c, s) =>
.Callback<DecoderOptions, Stream>((_, s) =>
{
using var ms = new MemoryStream();
using MemoryStream ms = new();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})
@ -78,9 +80,9 @@ public partial class ImageTests
this.localDecoder
.Setup(x => x.Decode(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Callback<DecoderOptions, Stream>((c, s) =>
.Callback<DecoderOptions, Stream>((_, s) =>
{
using var ms = new MemoryStream();
using MemoryStream ms = new();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})
@ -90,7 +92,7 @@ public partial class ImageTests
.Setup(x => x.DecodeAsync<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((_, s, _) =>
{
using var ms = new MemoryStream();
using MemoryStream ms = new();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})
@ -100,7 +102,7 @@ public partial class ImageTests
.Setup(x => x.DecodeAsync(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((_, s, _) =>
{
using var ms = new MemoryStream();
using MemoryStream ms = new();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})

38
tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs

@ -18,10 +18,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Rgb24>(options, this.MockFilePath);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
using (Image<Rgb24> img = Image.Load<Rgb24>(options, this.MockFilePath))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
}
this.TestFormat.VerifySpecificDecodeCall<Rgb24>(this.Marker, this.TopLevelConfiguration);
}
@ -34,10 +35,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.MockFilePath);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
using (Image img = Image.Load(options, this.MockFilePath))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}
@ -50,10 +52,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Rgba32>(options, this.MockFilePath, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image<Rgba32> img = Image.Load<Rgba32>(options, this.MockFilePath))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifySpecificDecodeCall<Rgba32>(this.Marker, this.TopLevelConfiguration);
}
@ -66,17 +69,18 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.MockFilePath, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image img = Image.Load(options, this.MockFilePath))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Fact]
public void WhenFileNotFound_Throws()
=> Assert.Throws<System.IO.FileNotFoundException>(
=> Assert.Throws<FileNotFoundException>(
() =>
{
DecoderOptions options = new()

15
tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
@ -13,19 +12,19 @@ public partial class ImageTests
{
private string Path { get; } = TestFile.GetInputFileFullPath(TestImages.Bmp.Bit8);
private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size());
private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size);
[Fact]
public void Path_Specific()
{
using var img = Image.Load<Rgba32>(this.Path);
using Image<Rgba32> img = Image.Load<Rgba32>(this.Path);
VerifyDecodedImage(img);
}
[Fact]
public void Path_Agnostic()
{
using var img = Image.Load(this.Path);
using Image img = Image.Load(this.Path);
VerifyDecodedImage(img);
}
@ -53,17 +52,17 @@ public partial class ImageTests
[Fact]
public void Path_OutFormat_Specific()
{
using var img = Image.Load<Rgba32>(this.Path, out IImageFormat format);
using Image<Rgba32> img = Image.Load<Rgba32>(this.Path);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
[Fact]
public void Path_OutFormat_Agnostic()
{
using var img = Image.Load(this.Path, out IImageFormat format);
using Image img = Image.Load(this.Path);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
[Fact]

36
tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs

@ -20,10 +20,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Rgb24>(options, this.ByteSpan);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
using (Image<Rgb24> img = Image.Load<Rgb24>(options, this.ByteSpan))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
}
this.TestFormat.VerifySpecificDecodeCall<Rgb24>(this.Marker, this.TopLevelConfiguration);
}
@ -36,10 +37,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.ByteSpan);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
using (Image img = Image.Load(options, this.ByteSpan))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}
@ -52,10 +54,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Bgr24>(options, this.ByteSpan, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image<Bgr24> img = Image.Load<Bgr24>(options, this.ByteSpan))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifySpecificDecodeCall<Bgr24>(this.Marker, this.TopLevelConfiguration);
}
@ -68,10 +71,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.ByteSpan, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image img = Image.Load(options, this.ByteSpan))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}

15
tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
@ -15,36 +14,36 @@ public partial class ImageTests
private static Span<byte> ByteSpan => new(ByteArray);
private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size());
private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size);
[Fact]
public void Bytes_Specific()
{
using var img = Image.Load<Rgba32>(ByteSpan);
using Image<Rgba32> img = Image.Load<Rgba32>(ByteSpan);
VerifyDecodedImage(img);
}
[Fact]
public void Bytes_Agnostic()
{
using var img = Image.Load(ByteSpan);
using Image img = Image.Load(ByteSpan);
VerifyDecodedImage(img);
}
[Fact]
public void Bytes_OutFormat_Specific()
{
using var img = Image.Load<Rgba32>(ByteSpan, out IImageFormat format);
using Image<Rgba32> img = Image.Load<Rgba32>(ByteSpan);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
[Fact]
public void Bytes_OutFormat_Agnostic()
{
using var img = Image.Load(ByteSpan, out IImageFormat format);
using Image img = Image.Load(ByteSpan);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
}
}

54
tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs

@ -18,10 +18,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Rgb24>(options, this.DataStream);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
using (Image<Rgb24> img = Image.Load<Rgb24>(options, this.DataStream))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
}
this.TestFormat.VerifySpecificDecodeCall<Rgb24>(this.Marker, this.TopLevelConfiguration);
}
@ -34,10 +35,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.DataStream);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
using (Image img = Image.Load(options, this.DataStream))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat.SampleAgnostic(), img);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}
@ -50,10 +52,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var stream = new NonSeekableStream(this.DataStream);
var img = Image.Load<Rgba32>(options, stream);
Assert.NotNull(img);
NonSeekableStream stream = new(this.DataStream);
using (Image<Rgba32> img = Image.Load<Rgba32>(options, stream))
{
Assert.NotNull(img);
}
this.TestFormat.VerifySpecificDecodeCall<Rgba32>(this.Marker, this.TopLevelConfiguration);
}
@ -66,10 +69,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var stream = new NonSeekableStream(this.DataStream);
Image<Rgba32> img = await Image.LoadAsync<Rgba32>(options, stream);
Assert.NotNull(img);
NonSeekableStream stream = new(this.DataStream);
using (Image<Rgba32> img = await Image.LoadAsync<Rgba32>(options, stream))
{
Assert.NotNull(img);
}
this.TestFormat.VerifySpecificDecodeCall<Rgba32>(this.Marker, this.TopLevelConfiguration);
}
@ -82,10 +86,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load<Rgba32>(options, this.DataStream, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image<Rgba32> img = Image.Load<Rgba32>(options, this.DataStream))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifySpecificDecodeCall<Rgba32>(this.Marker, this.TopLevelConfiguration);
}
@ -98,10 +103,11 @@ public partial class ImageTests
Configuration = this.TopLevelConfiguration
};
var img = Image.Load(options, this.DataStream, out IImageFormat format);
Assert.NotNull(img);
Assert.Equal(this.TestFormat, format);
using (Image img = Image.Load(options, this.DataStream))
{
Assert.NotNull(img);
Assert.Equal(this.TestFormat, img.Metadata.DecodedImageFormat);
}
this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration);
}

4
tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs

@ -18,7 +18,7 @@ public partial class ImageTests
public void Image_Load_Throws_UnknownImageFormatException()
=> Assert.Throws<UnknownImageFormatException>(() =>
{
using (Image.Load(DecoderOptions.Default, this.Stream, out IImageFormat format))
using (Image.Load(DecoderOptions.Default, this.Stream))
{
}
});
@ -27,7 +27,7 @@ public partial class ImageTests
public void Image_Load_T_Throws_UnknownImageFormatException()
=> Assert.Throws<UnknownImageFormatException>(() =>
{
using (Image.Load<Rgba32>(DecoderOptions.Default, this.Stream, out IImageFormat format))
using (Image.Load<Rgba32>(DecoderOptions.Default, this.Stream))
{
}
});

31
tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
@ -27,47 +26,47 @@ public partial class ImageTests
}
private static void VerifyDecodedImage(Image img)
=> Assert.Equal(new Size(127, 64), img.Size());
=> Assert.Equal(new Size(127, 64), img.Size);
[Fact]
public void Stream_Specific()
{
using var img = Image.Load<Rgba32>(this.Stream);
using Image<Rgba32> img = Image.Load<Rgba32>(this.Stream);
VerifyDecodedImage(img);
}
[Fact]
public void Stream_Agnostic()
{
using var img = Image.Load(this.Stream);
using Image img = Image.Load(this.Stream);
VerifyDecodedImage(img);
}
[Fact]
public void Stream_OutFormat_Specific()
{
using var img = Image.Load<Rgba32>(this.Stream, out IImageFormat format);
using Image<Rgba32> img = Image.Load<Rgba32>(this.Stream);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
[Fact]
public void Stream_OutFormat_Agnostic()
{
using var img = Image.Load(this.Stream, out IImageFormat format);
using Image img = Image.Load(this.Stream);
VerifyDecodedImage(img);
Assert.IsType<BmpFormat>(format);
Assert.IsType<BmpFormat>(img.Metadata.DecodedImageFormat);
}
[Fact]
public async Task Async_Stream_OutFormat_Agnostic()
{
this.AllowSynchronousIO = false;
(Image Image, IImageFormat Format) formattedImage = await Image.LoadWithFormatAsync(this.Stream);
using (formattedImage.Image)
Image image = await Image.LoadAsync(this.Stream);
using (image)
{
VerifyDecodedImage(formattedImage.Image);
Assert.IsType<BmpFormat>(formattedImage.Format);
VerifyDecodedImage(image);
Assert.IsType<BmpFormat>(image.Metadata.DecodedImageFormat);
}
}
@ -91,11 +90,11 @@ public partial class ImageTests
public async Task Async_Stream_OutFormat_Specific()
{
this.AllowSynchronousIO = false;
(Image<Rgba32> Image, IImageFormat Format) formattedImage = await Image.LoadWithFormatAsync<Rgba32>(this.Stream);
using (formattedImage.Image)
Image<Rgba32> image = await Image.LoadAsync<Rgba32>(this.Stream);
using (image)
{
VerifyDecodedImage(formattedImage.Image);
Assert.IsType<BmpFormat>(formattedImage.Format);
VerifyDecodedImage(image);
Assert.IsType<BmpFormat>(image.Metadata.DecodedImageFormat);
}
}

34
tests/ImageSharp.Tests/Image/ImageTests.Save.cs

@ -19,30 +19,26 @@ public partial class ImageTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "DetectedEncoding.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.Save(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
public void WhenExtensionIsUnknown_Throws()
public void WhenExtensionIsUnknown_Throws_UnknownImageFormatException()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
Assert.Throws<NotSupportedException>(
Assert.Throws<UnknownImageFormatException>(
() =>
{
using (var image = new Image<Rgba32>(10, 10))
{
image.Save(file);
}
using Image<Rgba32> image = new(10, 10);
image.Save(file);
});
}
@ -52,27 +48,23 @@ public partial class ImageTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "SetEncoding.dat");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.Save(file, new PngEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
public void ThrowsWhenDisposed()
{
using var image = new Image<Rgba32>(5, 5);
using Image<Rgba32> image = new(5, 5);
image.Dispose();
IImageEncoder encoder = Mock.Of<IImageEncoder>();
using (var stream = new MemoryStream())
{
Assert.Throws<ObjectDisposedException>(() => image.Save(stream, encoder));
}
using MemoryStream stream = new();
Assert.Throws<ObjectDisposedException>(() => image.Save(stream, encoder));
}
}
}

88
tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs

@ -21,30 +21,26 @@ public partial class ImageTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "DetectedEncodingAsync.png");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Fact]
public async Task WhenExtensionIsUnknown_Throws()
public Task WhenExtensionIsUnknown_Throws_UnknownImageFormatException()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
await Assert.ThrowsAsync<NotSupportedException>(
return Assert.ThrowsAsync<UnknownImageFormatException>(
async () =>
{
using (var image = new Image<Rgba32>(10, 10))
{
await image.SaveAsync(file);
}
using Image<Rgba32> image = new(10, 10);
await image.SaveAsync(file);
});
}
@ -54,15 +50,13 @@ public partial class ImageTests
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = Path.Combine(dir, "SetEncoding.dat");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsync(file, new PngEncoder());
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is PngFormat);
}
[Theory]
@ -74,39 +68,29 @@ public partial class ImageTests
[InlineData("test.gif", "image/gif")]
public async Task SaveStreamWithMime(string filename, string mimeType)
{
using (var image = new Image<Rgba32>(5, 5))
{
string ext = Path.GetExtension(filename);
image.GetConfiguration().ImageFormatsManager.TryFindFormatByFileExtension(ext, out IImageFormat format);
Assert.Equal(mimeType, format!.DefaultMimeType);
using (var stream = new MemoryStream())
{
var asyncStream = new AsyncStreamWrapper(stream, () => false);
await image.SaveAsync(asyncStream, format);
using Image<Rgba32> image = new(5, 5);
string ext = Path.GetExtension(filename);
image.GetConfiguration().ImageFormatsManager.TryFindFormatByFileExtension(ext, out IImageFormat format);
Assert.Equal(mimeType, format!.DefaultMimeType);
stream.Position = 0;
using MemoryStream stream = new();
AsyncStreamWrapper asyncStream = new(stream, () => false);
await image.SaveAsync(asyncStream, format);
(Image Image, IImageFormat Format) imf = await Image.LoadWithFormatAsync(stream);
stream.Position = 0;
Assert.Equal(format, imf.Format);
Assert.Equal(mimeType, imf.Format.DefaultMimeType);
imf.Image.Dispose();
}
}
IImageFormat format2 = Image.DetectFormat(stream);
Assert.Equal(format, format2);
}
[Fact]
public async Task ThrowsWhenDisposed()
{
var image = new Image<Rgba32>(5, 5);
Image<Rgba32> image = new(5, 5);
image.Dispose();
IImageEncoder encoder = Mock.Of<IImageEncoder>();
using (var stream = new MemoryStream())
{
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await image.SaveAsync(stream, encoder));
}
using MemoryStream stream = new();
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await image.SaveAsync(stream, encoder));
}
[Theory]
@ -118,27 +102,23 @@ public partial class ImageTests
[InlineData("test.gif")]
public async Task SaveAsync_NeverCallsSyncMethods(string filename)
{
using (var image = new Image<Rgba32>(5, 5))
{
IImageEncoder encoder = image.DetectEncoder(filename);
using (var stream = new MemoryStream())
{
var asyncStream = new AsyncStreamWrapper(stream, () => false);
await image.SaveAsync(asyncStream, encoder);
}
}
using Image<Rgba32> image = new(5, 5);
IImageEncoder encoder = image.DetectEncoder(filename);
using MemoryStream stream = new();
AsyncStreamWrapper asyncStream = new(stream, () => false);
await image.SaveAsync(asyncStream, encoder);
}
[Fact]
public async Task SaveAsync_WithNonSeekableStream_IsCancellable()
{
using var image = new Image<Rgba32>(4000, 4000);
var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression };
using var stream = new MemoryStream();
var asyncStream = new AsyncStreamWrapper(stream, () => false);
var cts = new CancellationTokenSource();
using Image<Rgba32> image = new(4000, 4000);
PngEncoder encoder = new() { CompressionLevel = PngCompressionLevel.BestCompression };
using MemoryStream stream = new();
AsyncStreamWrapper asyncStream = new(stream, () => false);
CancellationTokenSource cts = new();
var pausedStream = new PausedStream(asyncStream);
PausedStream pausedStream = new(asyncStream);
pausedStream.OnWaiting(s =>
{
cts.Cancel();

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)
{

72
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -23,7 +23,7 @@ public partial class ImageTests
[Fact]
public void Width_Height()
{
using (var image = new Image<Rgba32>(11, 23))
using (Image<Rgba32> image = new(11, 23))
{
Assert.Equal(11, image.Width);
Assert.Equal(23, image.Height);
@ -40,7 +40,7 @@ public partial class ImageTests
{
Configuration configuration = Configuration.Default.Clone();
using (var image = new Image<Rgba32>(configuration, 11, 23))
using (Image<Rgba32> image = new(configuration, 11, 23))
{
Assert.Equal(11, image.Width);
Assert.Equal(23, image.Height);
@ -58,7 +58,7 @@ public partial class ImageTests
Configuration configuration = Configuration.Default.Clone();
Rgba32 color = Color.Aquamarine;
using (var image = new Image<Rgba32>(configuration, 11, 23, color))
using (Image<Rgba32> image = new(configuration, 11, 23, color))
{
Assert.Equal(11, image.Width);
Assert.Equal(23, image.Height);
@ -77,9 +77,9 @@ public partial class ImageTests
byte dirtyValue = 123;
configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue);
var metadata = new ImageMetadata();
ImageMetadata metadata = new();
using (var image = Image.CreateUninitialized<L8>(configuration, 21, 22, metadata))
using (Image<L8> image = Image.CreateUninitialized<L8>(configuration, 21, 22, metadata))
{
Assert.Equal(21, image.Width);
Assert.Equal(22, image.Height);
@ -108,7 +108,7 @@ public partial class ImageTests
this.LimitBufferCapacity(100);
}
using var image = new Image<Rgba32>(this.configuration, 10, 10);
using Image<Rgba32> image = new(this.configuration, 10, 10);
Rgba32 val = image[3, 4];
Assert.Equal(default(Rgba32), val);
image[3, 4] = Color.Red;
@ -116,7 +116,7 @@ public partial class ImageTests
Assert.Equal(Color.Red.ToRgba32(), val);
}
public static TheoryData<bool, int> OutOfRangeData = new TheoryData<bool, int>()
public static TheoryData<bool, int> OutOfRangeData = new()
{
{ false, -1 },
{ false, 10 },
@ -133,7 +133,7 @@ public partial class ImageTests
this.LimitBufferCapacity(100);
}
using var image = new Image<Rgba32>(this.configuration, 10, 10);
using Image<Rgba32> image = new(this.configuration, 10, 10);
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => _ = image[x, 3]);
Assert.Equal("x", ex.ParamName);
}
@ -147,7 +147,7 @@ public partial class ImageTests
this.LimitBufferCapacity(100);
}
using var image = new Image<Rgba32>(this.configuration, 10, 10);
using Image<Rgba32> image = new(this.configuration, 10, 10);
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => image[x, 3] = default);
Assert.Equal("x", ex.ParamName);
}
@ -161,7 +161,7 @@ public partial class ImageTests
this.LimitBufferCapacity(100);
}
using var image = new Image<Rgba32>(this.configuration, 10, 10);
using Image<Rgba32> image = new(this.configuration, 10, 10);
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => image[3, y] = default);
Assert.Equal("y", ex.ParamName);
}
@ -178,7 +178,7 @@ public partial class ImageTests
this.LimitBufferCapacity(20);
}
using var image = new Image<La16>(this.configuration, 10, 10);
using Image<La16> image = new(this.configuration, 10, 10);
if (disco)
{
Assert.True(image.GetPixelMemoryGroup().Count > 1);
@ -204,7 +204,7 @@ public partial class ImageTests
[InlineData(true)]
public void CopyPixelDataTo_DestinationTooShort_Throws(bool byteSpan)
{
using var image = new Image<La16>(this.configuration, 10, 10);
using Image<La16> image = new(this.configuration, 10, 10);
Assert.ThrowsAny<ArgumentOutOfRangeException>(() =>
{
@ -243,7 +243,7 @@ public partial class ImageTests
[Fact]
public void NullReference_Throws()
{
using var img = new Image<Rgb24>(1, 1);
using Image<Rgb24> img = new(1, 1);
Assert.Throws<ArgumentNullException>(() => img.ProcessPixelRows(null));
@ -262,7 +262,7 @@ public partial class ImageTests
public void MultipleDisposeCalls()
{
var image = new Image<Rgba32>(this.configuration, 10, 10);
Image<Rgba32> image = new(this.configuration, 10, 10);
image.Dispose();
image.Dispose();
}
@ -270,24 +270,24 @@ public partial class ImageTests
[Fact]
public void NonPrivateProperties_ObjectDisposedException()
{
var image = new Image<Rgba32>(this.configuration, 10, 10);
var genericImage = (Image)image;
Image<Rgba32> image = new(this.configuration, 10, 10);
Image genericImage = (Image)image;
image.Dispose();
// Image<TPixel>
Assert.Throws<ObjectDisposedException>(() => { var prop = image.Frames; });
Assert.Throws<ObjectDisposedException>(() => { ImageFrameCollection<Rgba32> prop = image.Frames; });
// Image
Assert.Throws<ObjectDisposedException>(() => { var prop = genericImage.Frames; });
Assert.Throws<ObjectDisposedException>(() => { ImageFrameCollection prop = genericImage.Frames; });
}
[Fact]
public void Save_ObjectDisposedException()
{
using var stream = new MemoryStream();
var image = new Image<Rgba32>(this.configuration, 10, 10);
var encoder = new JpegEncoder();
using MemoryStream stream = new();
Image<Rgba32> image = new(this.configuration, 10, 10);
JpegEncoder encoder = new();
image.Dispose();
@ -307,18 +307,18 @@ public partial class ImageTests
[Fact]
public void NonPrivateMethods_ObjectDisposedException()
{
var image = new Image<Rgba32>(this.configuration, 10, 10);
var genericImage = (Image)image;
Image<Rgba32> image = new(this.configuration, 10, 10);
Image genericImage = (Image)image;
image.Dispose();
// Image<TPixel>
Assert.Throws<ObjectDisposedException>(() => { var res = image.Clone(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.CloneAs<Rgba32>(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> _); });
Assert.Throws<ObjectDisposedException>(() => { Image<Rgba32> res = image.Clone(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { Image<Rgba32> res = image.CloneAs<Rgba32>(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { bool res = image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> _); });
// Image
Assert.Throws<ObjectDisposedException>(() => { var res = genericImage.CloneAs<Rgba32>(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { Image<Rgba32> res = genericImage.CloneAs<Rgba32>(this.configuration); });
}
}
@ -327,29 +327,29 @@ public partial class ImageTests
[Fact]
public void KnownExtension_ReturnsEncoder()
{
using var image = new Image<L8>(1, 1);
using Image<L8> image = new(1, 1);
IImageEncoder encoder = image.DetectEncoder("dummy.png");
Assert.NotNull(encoder);
Assert.IsType<PngEncoder>(encoder);
}
[Fact]
public void UnknownExtension_ThrowsNotSupportedException()
public void UnknownExtension_ThrowsUnknownImageFormatException()
{
using var image = new Image<L8>(1, 1);
Assert.Throws<NotSupportedException>(() => image.DetectEncoder("dummy.yolo"));
using Image<L8> image = new(1, 1);
Assert.Throws<UnknownImageFormatException>(() => image.DetectEncoder("dummy.yolo"));
}
[Fact]
public void NoDetectorRegisteredForKnownExtension_ThrowsNotSupportedException()
public void NoDetectorRegisteredForKnownExtension_ThrowsUnknownImageFormatException()
{
var configuration = new Configuration();
var format = new TestFormat();
Configuration configuration = new();
TestFormat format = new();
configuration.ImageFormatsManager.AddImageFormat(format);
configuration.ImageFormatsManager.AddImageFormatDetector(new MockImageFormatDetector(format));
using var image = new Image<L8>(configuration, 1, 1);
Assert.Throws<NotSupportedException>(() => image.DetectEncoder($"dummy.{format.Extension}"));
using Image<L8> image = new(configuration, 1, 1);
Assert.Throws<UnknownImageFormatException>(() => image.DetectEncoder($"dummy.{format.Extension}"));
}
}
}

2
tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs

@ -56,7 +56,7 @@ public class LargeImageIntegrationTests
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
configuration.ImageFormatsManager.TryFindFormatByFileExtension(formatInner, out IImageFormat format);
IImageEncoder encoder = configuration.ImageFormatsManager.FindEncoder(format!);
IImageEncoder encoder = configuration.ImageFormatsManager.GetEncoder(format!);
string dir = TestEnvironment.CreateOutputDirectory(".Temp");
string path = Path.Combine(dir, $"{Guid.NewGuid()}.{formatInner}");
using (Image<Rgba32> temp = new(2048, 2048))

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;

24
tests/ImageSharp.Tests/ImageInfoTests.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
@ -11,20 +11,20 @@ public class ImageInfoTests
[Fact]
public void ImageInfoInitializesCorrectly()
{
const int Width = 50;
const int Height = 60;
var size = new Size(Width, Height);
var rectangle = new Rectangle(0, 0, Width, Height);
var pixelType = new PixelTypeInfo(8);
var meta = new ImageMetadata();
const int width = 50;
const int height = 60;
Size size = new(width, height);
Rectangle rectangle = new(0, 0, width, height);
PixelTypeInfo pixelType = new(8);
ImageMetadata meta = new();
var info = new ImageInfo(pixelType, Width, Height, meta);
ImageInfo info = new(pixelType, width, height, meta);
Assert.Equal(pixelType, info.PixelType);
Assert.Equal(Width, info.Width);
Assert.Equal(Height, info.Height);
Assert.Equal(size, info.Size());
Assert.Equal(rectangle, info.Bounds());
Assert.Equal(width, info.Width);
Assert.Equal(height, info.Height);
Assert.Equal(size, info.Size);
Assert.Equal(rectangle, info.Bounds);
Assert.Equal(meta, info.Metadata);
}
}

2
tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs

@ -15,7 +15,7 @@ public abstract class BaseImageOperationsExtensionTest : IDisposable
protected readonly GraphicsOptions options;
private readonly Image<Rgba32> source;
public Rectangle SourceBounds() => this.source.Bounds();
public Rectangle SourceBounds() => this.source.Bounds;
public BaseImageOperationsExtensionTest()
{

2
tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs

@ -64,7 +64,7 @@ internal class FakeImageOperationsProvider : IImageProcessingContextFactory
public Size GetCurrentSize()
{
return this.Source.Size();
return this.Source.Size;
}
public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save