Browse Source

Merge pull request #618 from SixLabors/af/memory-bridge2

Add span overloads for Image.Load() and Image.DetectFormat()
pull/625/head
Anton Firsov 8 years ago
committed by GitHub
parent
commit
2e62c60d51
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 167
      src/ImageSharp/Image.FromBytes.cs
  2. 3
      src/ImageSharp/ImageSharp.csproj
  3. 6
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  4. 105
      tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs
  5. 99
      tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs
  6. 92
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
  7. 60
      tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs
  8. 63
      tests/ImageSharp.Tests/Image/ImageTests.Load_BasicCases.cs
  9. 338
      tests/ImageSharp.Tests/Image/ImageTests.Load_ComplexCases.cs
  10. 83
      tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs
  11. 120
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs
  12. 102
      tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs
  13. 13
      tests/ImageSharp.Tests/TestFormat.cs

167
src/ImageSharp/Image.FromBytes.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="data">The byte array containing image data to read the header from.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The format or null if none found.</returns>
public static IImageFormat DetectFormat(byte[] data)
{
@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="data">The byte array containing image data to read the header from.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static IImageFormat DetectFormat(Configuration config, byte[] data)
{
@ -37,30 +38,30 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data) => Load<Rgba32>(Configuration.Default, data);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data, out IImageFormat format) => Load<Rgba32>(Configuration.Default, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, byte[] data) => Load<Rgba32>(config, data);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
@ -69,15 +70,15 @@ namespace SixLabors.ImageSharp
public static Image<Rgba32> Load(Configuration config, byte[] data, out IImageFormat format) => Load<Rgba32>(config, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data, IImageDecoder decoder) => Load<Rgba32>(data, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
@ -86,9 +87,9 @@ namespace SixLabors.ImageSharp
public static Image<Rgba32> Load(Configuration config, byte[] data, IImageDecoder decoder) => Load<Rgba32>(config, data, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(byte[] data)
@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp
=> Load<TPixel>(Configuration.Default, data);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
@ -107,10 +108,10 @@ namespace SixLabors.ImageSharp
=> Load<TPixel>(Configuration.Default, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Configuration config, byte[] data)
@ -123,11 +124,11 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="format">The <see cref="IImageFormat"/> of the decoded image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Configuration config, byte[] data, out IImageFormat format)
@ -140,9 +141,9 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
@ -156,10 +157,10 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The Configuration.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
@ -171,5 +172,125 @@ namespace SixLabors.ImageSharp
return Load<TPixel>(config, memoryStream, decoder);
}
}
#if !NETSTANDARD1_1
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The format or null if none found.</returns>
public static IImageFormat DetectFormat(ReadOnlySpan<byte> data)
{
return DetectFormat(Configuration.Default, data);
}
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static unsafe IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data)
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return DetectFormat(config, stream);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte span.
/// </summary>
/// <param name="data">The byte span containing image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(ReadOnlySpan<byte> data) => Load<Rgba32>(Configuration.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte span containing encoded image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, ReadOnlySpan<byte> data) => Load<Rgba32>(config, data);
/// <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>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(ReadOnlySpan<byte> data)
where TPixel : struct, IPixel<TPixel>
=> Load<TPixel>(Configuration.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte span containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(Configuration config, ReadOnlySpan<byte> data)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The Configuration.</param>
/// <param name="data">The byte span containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(
Configuration config,
ReadOnlySpan<byte> data,
IImageDecoder decoder)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream, decoder);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The configuration 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>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(
Configuration config,
ReadOnlySpan<byte> data,
out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream, out format);
}
}
}
#endif
}
}

3
src/ImageSharp/ImageSharp.csproj

@ -44,6 +44,9 @@
<PackageReference Include="System.Memory" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />

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

@ -20,12 +20,12 @@ namespace SixLabors.ImageSharp.Tests
{
public class ImageFormatManagerTests
{
public ImageFormatManager FormatsManagerEmpty { get; private set; }
public ImageFormatManager DefaultFormatsManager { get; private set; }
public ImageFormatManager FormatsManagerEmpty { get; }
public ImageFormatManager DefaultFormatsManager { get; }
public ImageFormatManagerTests()
{
this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager;
this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager;
this.FormatsManagerEmpty = new ImageFormatManager();
}

105
tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs

@ -1,105 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using Moq;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class DiscoverImageFormatTests
{
private readonly Mock<IFileSystem> fileSystem;
private readonly string FilePath;
private readonly IImageFormatDetector localMimeTypeDetector;
private readonly Mock<IImageFormat> localImageFormatMock;
public IImageFormat localImageFormat => this.localImageFormatMock.Object;
public Configuration LocalConfiguration { get; private set; }
public byte[] Marker { get; private set; }
public MemoryStream DataStream { get; private set; }
public byte[] DecodedData { get; private set; }
private const string localMimeType = "image/local";
public DiscoverImageFormatTests()
{
this.localImageFormatMock = new Mock<IImageFormat>();
this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object);
this.fileSystem = new Mock<IFileSystem>();
this.LocalConfiguration = new Configuration
{
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
TestFormat.RegisterGlobalTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();
this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker);
this.FilePath = Guid.NewGuid().ToString();
this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream);
TestFileSystem.RegisterGlobalTestFormat();
TestFileSystem.Global.AddFile(this.FilePath, this.DataStream);
}
[Fact]
public void DiscoverImageFormatByteArray()
{
IImageFormat type = Image.DetectFormat(this.DataStream.ToArray());
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverImageFormatByteArray_WithConfig()
{
IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream.ToArray());
Assert.Equal(this.localImageFormat, type);
}
[Fact]
public void DiscoverImageFormatFile()
{
IImageFormat type = Image.DetectFormat(this.FilePath);
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverImageFormatFilePath_WithConfig()
{
IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.FilePath);
Assert.Equal(this.localImageFormat, type);
}
[Fact]
public void DiscoverImageFormatStream()
{
IImageFormat type = Image.DetectFormat(this.DataStream);
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverImageFormatFileStream_WithConfig()
{
IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream);
Assert.Equal(this.localImageFormat, type);
}
[Fact]
public void DiscoverImageFormatNoDetectorsRegisterdShouldReturnNull()
{
IImageFormat type = Image.DetectFormat(new Configuration(), this.DataStream);
Assert.Null(type);
}
}
}

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

@ -0,0 +1,99 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using Moq;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class DetectFormat : ImageLoadTestBase
{
private static readonly string ActualImagePath = TestFile.GetInputFileFullPath(TestImages.Bmp.F);
private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private ReadOnlySpan<byte> ActualImageSpan => this.ActualImageBytes.AsSpan();
private byte[] ByteArray => this.DataStream.ToArray();
private ReadOnlySpan<byte> ByteSpan => this.ByteArray.AsSpan();
private IImageFormat LocalImageFormat => this.localImageFormatMock.Object;
private static readonly IImageFormat ExpectedGlobalFormat =
Configuration.Default.ImageFormatsManager.FindFormatByFileExtension("bmp");
[Theory]
[InlineData(false)]
[InlineData(true)]
public void FromBytes_GlobalConfiguration(bool useSpan)
{
IImageFormat type = useSpan
? Image.DetectFormat(this.ActualImageSpan)
: Image.DetectFormat(this.ActualImageBytes);
Assert.Equal(ExpectedGlobalFormat, type);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void FromBytes_CustomConfiguration(bool useSpan)
{
IImageFormat type = useSpan
? Image.DetectFormat(this.LocalConfiguration, this.ByteArray.AsSpan())
: Image.DetectFormat(this.LocalConfiguration, this.ByteArray);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromFileSystemPath_GlobalConfiguration()
{
IImageFormat type = Image.DetectFormat(ActualImagePath);
Assert.Equal(ExpectedGlobalFormat, type);
}
[Fact]
public void FromFileSystemPath_CustomConfiguration()
{
IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.MockFilePath);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void FromStream_GlobalConfiguration()
{
using (var stream = new MemoryStream(this.ActualImageBytes))
{
IImageFormat type = Image.DetectFormat(stream);
Assert.Equal(ExpectedGlobalFormat, type);
}
}
[Fact]
public void FromStream_CustomConfiguration()
{
IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream);
Assert.Equal(this.LocalImageFormat, type);
}
[Fact]
public void WhenNoMatchingFormatFound_ReturnsNull()
{
IImageFormat type = Image.DetectFormat(new Configuration(), this.DataStream);
Assert.Null(type);
}
}
}
}

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

@ -0,0 +1,92 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using Moq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
public abstract class ImageLoadTestBase : IDisposable
{
protected Image<Rgba32> returnImage;
protected Mock<IImageDecoder> localDecoder;
protected IImageFormatDetector localMimeTypeDetector;
protected Mock<IImageFormat> localImageFormatMock;
protected readonly string MockFilePath = Guid.NewGuid().ToString();
internal readonly Mock<IFileSystem> localFileSystemMock = new Mock<IFileSystem>();
protected readonly TestFileSystem topLevelFileSystem = new TestFileSystem();
public Configuration LocalConfiguration { get; }
public TestFormat TestFormat { get; } = new TestFormat();
/// <summary>
/// Gets the top-level configuration in the context of this test case.
/// It has <see cref="TestFormat"/> registered.
/// </summary>
public Configuration TopLevelConfiguration { get; }
public byte[] Marker { get; }
public MemoryStream DataStream { get; }
public byte[] DecodedData { get; private set; }
protected ImageLoadTestBase()
{
this.returnImage = new Image<Rgba32>(1, 1);
this.localImageFormatMock = new Mock<IImageFormat>();
this.localDecoder = new Mock<IImageDecoder>();
this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object);
this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>()))
.Callback<Configuration, Stream>((c, s) =>
{
using (var ms = new MemoryStream())
{
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
}
})
.Returns(this.returnImage);
this.LocalConfiguration = new Configuration
{
};
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object);
this.TopLevelConfiguration = new Configuration(this.TestFormat);
this.Marker = Guid.NewGuid().ToByteArray();
this.DataStream = this.TestFormat.CreateStream(this.Marker);
this.localFileSystemMock.Setup(x => x.OpenRead(this.MockFilePath)).Returns(this.DataStream);
this.topLevelFileSystem.AddFile(this.MockFilePath, this.DataStream);
this.LocalConfiguration.FileSystem = this.localFileSystemMock.Object;
this.TopLevelConfiguration.FileSystem = this.topLevelFileSystem;
}
public void Dispose()
{
// clean up the global object;
this.returnImage?.Dispose();
}
}
}
}

60
tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs

@ -0,0 +1,60 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
public class LoadPixelData
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void FromPixels(bool useSpan)
{
Rgba32[] data = { Rgba32.Black, Rgba32.White, Rgba32.White, Rgba32.Black, };
using (Image<Rgba32> img = useSpan
? Image.LoadPixelData<Rgba32>(data.AsSpan(), 2, 2)
: Image.LoadPixelData<Rgba32>(data, 2, 2))
{
Assert.NotNull(img);
Assert.Equal(Rgba32.Black, img[0, 0]);
Assert.Equal(Rgba32.White, img[0, 1]);
Assert.Equal(Rgba32.White, img[1, 0]);
Assert.Equal(Rgba32.Black, img[1, 1]);
}
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void FromBytes(bool useSpan)
{
byte[] data =
{
0, 0, 0, 255, // 0,0
255, 255, 255, 255, // 0,1
255, 255, 255, 255, // 1,0
0, 0, 0, 255, // 1,1
};
using (Image<Rgba32> img = useSpan
? Image.LoadPixelData<Rgba32>(data.AsSpan(), 2, 2)
: Image.LoadPixelData<Rgba32>(data, 2, 2))
{
Assert.NotNull(img);
Assert.Equal(Rgba32.Black, img[0, 0]);
Assert.Equal(Rgba32.White, img[0, 1]);
Assert.Equal(Rgba32.White, img[1, 0]);
Assert.Equal(Rgba32.Black, img[1, 1]);
}
}
}
}
}

63
tests/ImageSharp.Tests/Image/ImageTests.Load_BasicCases.cs

@ -1,63 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
public class Load_BasicCases
{
[Fact]
public void ByteArray()
{
Assert.Throws<ArgumentNullException>(() =>
{
Image.Load<Rgba32>((byte[])null);
});
var file = TestFile.Create(TestImages.Bmp.Car);
using (var image = Image.Load<Rgba32>(file.Bytes))
{
Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height);
}
}
[Fact]
public void FileSystemPath()
{
var file = TestFile.Create(TestImages.Bmp.Car);
using (var image = Image.Load<Rgba32>(file.FullPath))
{
Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height);
}
}
[Fact]
public void FileSystemPath_FileNotFound()
{
System.IO.FileNotFoundException ex = Assert.Throws<System.IO.FileNotFoundException>(
() =>
{
Image.Load<Rgba32>(Guid.NewGuid().ToString());
});
}
[Fact]
public void FileSystemPath_NullPath()
{
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(
() =>
{
Image.Load<Rgba32>((string)null);
});
}
}
}
}

338
tests/ImageSharp.Tests/Image/ImageTests.Load_ComplexCases.cs

@ -1,338 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using Moq;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class Load_ComplexCases : IDisposable
{
private readonly Mock<IFileSystem> fileSystem;
private readonly Image<Rgba32> returnImage;
private readonly Mock<IImageDecoder> localDecoder;
private readonly string FilePath;
private readonly IImageFormatDetector localMimeTypeDetector;
private readonly Mock<IImageFormat> localImageFormatMock;
public Configuration LocalConfiguration { get; private set; }
public byte[] Marker { get; private set; }
public MemoryStream DataStream { get; private set; }
public byte[] DecodedData { get; private set; }
public Load_ComplexCases()
{
this.returnImage = new Image<Rgba32>(1, 1);
this.localImageFormatMock = new Mock<IImageFormat>();
this.localDecoder = new Mock<IImageDecoder>();
this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object);
this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>()))
.Callback<Configuration, Stream>((c, s) =>
{
using (var ms = new MemoryStream())
{
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
}
})
.Returns(this.returnImage);
this.fileSystem = new Mock<IFileSystem>();
this.LocalConfiguration = new Configuration
{
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object);
TestFormat.RegisterGlobalTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();
this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker);
this.FilePath = Guid.NewGuid().ToString();
this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream);
TestFileSystem.RegisterGlobalTestFormat();
TestFileSystem.Global.AddFile(this.FilePath, this.DataStream);
}
[Fact]
public void LoadFromStream()
{
var img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromNoneSeekableStream()
{
var stream = new NoneSeekableStream(this.DataStream);
var img = Image.Load<Rgba32>(stream);
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithType()
{
var img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithConfig()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(this.LocalConfiguration, stream);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
}
[Fact]
public void LoadFromStreamWithTypeAndConfig()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(this.LocalConfiguration, stream);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
}
[Fact]
public void LoadFromStreamWithDecoder()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(stream, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, stream));
}
[Fact]
public void LoadFromStreamWithTypeAndDecoder()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(stream, this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, stream));
}
[Fact]
public void LoadFromBytes()
{
var img = Image.Load<Rgba32>(this.DataStream.ToArray());
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithType()
{
var img = Image.Load<Rgba32>(this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithConfig()
{
var img = Image.Load<Rgba32>(this.LocalConfiguration, this.DataStream.ToArray());
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndConfig()
{
var img = Image.Load<Rgba32>(this.LocalConfiguration, this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithDecoder()
{
var img = Image.Load<Rgba32>(this.DataStream.ToArray(), this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndDecoder()
{
var img = Image.Load<Rgba32>(this.DataStream.ToArray(), this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromFile()
{
var img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromFileWithType()
{
var img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromFileWithConfig()
{
var img = Image.Load<Rgba32>(this.LocalConfiguration, this.FilePath);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream));
}
[Fact]
public void LoadFromFileWithTypeAndConfig()
{
var img = Image.Load<Rgba32>(this.LocalConfiguration, this.FilePath);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream));
}
[Fact]
public void LoadFromFileWithDecoder()
{
var img = Image.Load<Rgba32>(this.FilePath, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, this.DataStream));
}
[Fact]
public void LoadFromFileWithTypeAndDecoder()
{
var img = Image.Load<Rgba32>(this.FilePath, this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, this.DataStream));
}
[Fact]
public void LoadFromPixelData_Pixels()
{
var img = Image.LoadPixelData<Rgba32>(new Rgba32[] {
Rgba32.Black, Rgba32.White,
Rgba32.White, Rgba32.Black,
}, 2, 2);
Assert.NotNull(img);
Assert.Equal(Rgba32.Black, img[0, 0]);
Assert.Equal(Rgba32.White, img[0, 1]);
Assert.Equal(Rgba32.White, img[1, 0]);
Assert.Equal(Rgba32.Black, img[1, 1]);
}
[Fact]
public void LoadFromPixelData_Bytes()
{
var img = Image.LoadPixelData<Rgba32>(new byte[] {
0,0,0,255, // 0,0
255,255,255,255, // 0,1
255,255,255,255, // 1,0
0,0,0,255, // 1,1
}, 2, 2);
Assert.NotNull(img);
Assert.Equal(Rgba32.Black, img[0, 0]);
Assert.Equal(Rgba32.White, img[0, 1]);
Assert.Equal(Rgba32.White, img[1, 0]);
Assert.Equal(Rgba32.Black, img[1, 1]);
}
[Fact]
public void LoadsImageWithoutThrowingCrcException()
{
var image1Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VersioningImage1);
using (Image<Rgba32> img = image1Provider.GetImage())
{
Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length);
}
}
public void Dispose()
{
// clean up the global object;
this.returnImage?.Dispose();
}
}
}
}

83
tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
using Moq;
using SixLabors.ImageSharp.IO;
public partial class ImageTests
{
public class Load_FileSystemPath : ImageLoadTestBase
{
[Fact]
public void BasicCase()
{
var img = Image.Load<Rgba32>(this.TopLevelConfiguration, this.MockFilePath);
Assert.NotNull(img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Fact]
public void UseLocalConfiguration()
{
var img = Image.Load<Rgba32>(this.LocalConfiguration, this.MockFilePath);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream));
}
[Fact]
public void UseCustomDecoder()
{
var img = Image.Load<Rgba32>(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.TopLevelConfiguration, this.DataStream));
}
[Fact]
public void UseGlobalConfigration()
{
var file = TestFile.Create(TestImages.Bmp.Car);
using (var image = Image.Load<Rgba32>(file.FullPath))
{
Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height);
}
}
[Fact]
public void WhenFileNotFound_Throws()
{
System.IO.FileNotFoundException ex = Assert.Throws<System.IO.FileNotFoundException>(
() =>
{
Image.Load<Rgba32>(Guid.NewGuid().ToString());
});
}
[Fact]
public void WhenPathIsNull_Throws()
{
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(
() =>
{
Image.Load<Rgba32>((string)null);
});
}
}
}
}

120
tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs

@ -0,0 +1,120 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using Moq;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
public class Load_FromBytes : ImageLoadTestBase
{
private byte[] ByteArray => this.DataStream.ToArray();
private ReadOnlySpan<byte> ByteSpan => this.ByteArray.AsSpan();
private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private ReadOnlySpan<byte> ActualImageSpan => this.ActualImageBytes.AsSpan();
[Theory]
[InlineData(false)]
[InlineData(true)]
public void BasicCase(bool useSpan)
{
Image<Rgba32> img = useSpan
? Image.Load(this.TopLevelConfiguration, this.ByteSpan)
: Image.Load(this.TopLevelConfiguration, this.ByteArray);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgba32>(), img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void NonDefaultPixelType(bool useSpan)
{
Image<Rgb24> img = useSpan
? Image.Load<Rgb24>(this.TopLevelConfiguration, this.ByteSpan)
: Image.Load<Rgb24>(this.TopLevelConfiguration, this.ByteArray);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void UseLocalConfiguration(bool useSpan)
{
Image<Rgba32> img = useSpan
? Image.Load<Rgba32>(this.LocalConfiguration, this.ByteSpan)
: Image.Load<Rgba32>(this.LocalConfiguration, this.ByteArray);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void UseCustomDecoder(bool useSpan)
{
Image<Rgba32> img = useSpan
? Image.Load<Rgba32>(
this.TopLevelConfiguration,
this.ByteSpan,
this.localDecoder.Object)
: Image.Load<Rgba32>(
this.TopLevelConfiguration,
this.ByteArray,
this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.TopLevelConfiguration, It.IsAny<Stream>()));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void UseGlobalConfiguration(bool useSpan)
{
using (Image<Rgba32> img =
useSpan ? Image.Load(this.ActualImageSpan) : Image.Load(this.ActualImageBytes))
{
Assert.Equal(new Size(108, 202), img.Size());
}
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void UseGlobalConfiguration_NonDefaultPixelType(bool useSpan)
{
using (Image<Rgb24> img = useSpan
? Image.Load<Rgb24>(this.ActualImageSpan)
: Image.Load<Rgb24>(this.ActualImageBytes))
{
Assert.Equal(new Size(108, 202), img.Size());
}
}
}
}
}

102
tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs

@ -0,0 +1,102 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.Primitives;
public partial class ImageTests
{
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class Load_FromStream : ImageLoadTestBase
{
[Fact]
public void BasicCase()
{
var img = Image.Load(this.TopLevelConfiguration, this.DataStream);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgba32>(), img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Fact]
public void UseGlobalConfiguration()
{
byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes;
using (var stream = new MemoryStream(data))
using (var img = Image.Load(stream))
{
Assert.Equal(new Size(108, 202), img.Size());
}
}
[Fact]
public void NonDefaultPixelTypeImage()
{
var img = Image.Load<Rgb24>(this.TopLevelConfiguration, this.DataStream);
Assert.NotNull(img);
Assert.Equal(this.TestFormat.Sample<Rgb24>(), img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Fact]
public void NonSeekableStream()
{
var stream = new NoneSeekableStream(this.DataStream);
var img = Image.Load<Rgba32>(this.TopLevelConfiguration, stream);
Assert.NotNull(img);
this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration);
}
[Fact]
public void UseLocalConfiguration()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(this.LocalConfiguration, stream);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
}
[Fact]
public void UseCustomDecoder()
{
Stream stream = new MemoryStream();
var img = Image.Load<Rgba32>(this.TopLevelConfiguration, stream, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.TopLevelConfiguration, stream));
}
// TODO: This should be a png decoder test!
[Fact]
public void LoadsImageWithoutThrowingCrcException()
{
var image1Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VersioningImage1);
using (Image<Rgba32> img = image1Provider.GetImage())
{
Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length);
}
}
}
}
}

13
tests/ImageSharp.Tests/TestFormat.cs

@ -18,13 +18,10 @@ namespace SixLabors.ImageSharp.Tests
/// </summary>
public class TestFormat : IConfigurationModule, IImageFormat
{
// We should not change Configuration.Default in individual tests!
// Create new configuration instances with new Configuration(TestFormat.GlobalTestFormat) instead!
public static TestFormat GlobalTestFormat { get; } = new TestFormat();
public static void RegisterGlobalTestFormat()
{
Configuration.Default.Configure(GlobalTestFormat);
}
public TestFormat()
{
this.Encoder = new TestEncoder(this);
@ -155,12 +152,12 @@ namespace SixLabors.ImageSharp.Tests
private TestFormat testFormat;
public int HeaderSize => testFormat.HeaderSize;
public int HeaderSize => this.testFormat.HeaderSize;
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (testFormat.IsSupportedFileFormat(header))
return testFormat;
if (this.testFormat.IsSupportedFileFormat(header))
return this.testFormat;
return null;
}

Loading…
Cancel
Save