Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/ImageSharp/Image.FromBytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,46 @@ public static IImageFormat DetectFormat(Configuration config, byte[] data)
}
}

/// <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>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(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="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(byte[] data, out IImageFormat format) => Identify(Configuration.Default, data, out format);

/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="config">The configuration.</param>
/// <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="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(Configuration config, byte[] data, out IImageFormat format)
{
config ??= Configuration.Default;
using (var stream = new MemoryStream(data))
{
return Identify(config, stream, out format);
}
}

/// <summary>
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
Expand Down
42 changes: 41 additions & 1 deletion src/ImageSharp/Image.FromFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,53 @@ public static IImageFormat DetectFormat(string filePath)
/// <returns>The mime type or null if none found.</returns>
public static IImageFormat DetectFormat(Configuration config, string filePath)
{
config = config ?? Configuration.Default;
config ??= Configuration.Default;
using (Stream file = config.FileSystem.OpenRead(filePath))
{
return DetectFormat(config, file);
}
}

/// <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>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <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>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(string filePath, out IImageFormat format) => Identify(Configuration.Default, filePath, out format);

/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="config">The configuration.</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="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(Configuration config, string filePath, out IImageFormat format)
{
config ??= Configuration.Default;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to guard against null on places like this? A call in a form of Image.Identify(null, "blah", out blah) is not something I would encourage.

Copy link
Member Author

@JimBobSquarePants JimBobSquarePants Mar 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the existing pattern but you're right. I've updated the code to use null checks throughout. Turns out we were passing null in some instances internally!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we consider changing it? Maybe as part of #1110?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above edit.

using (Stream file = config.FileSystem.OpenRead(filePath))
{
return Identify(config, file, out format);
}
}

/// <summary>
/// Create a new instance of the <see cref="Image"/> class from the given file.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp/Image.FromStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream)
=> WithSeekableStream(config, stream, s => InternalDetectFormat(s, config));

/// <summary>
/// By reading the header on the provided stream this reads the raw image information.
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
Expand All @@ -44,7 +44,7 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream)
public static IImageInfo Identify(Stream stream) => Identify(stream, out IImageFormat _);

/// <summary>
/// By reading the header on the provided stream this reads the raw image information.
/// 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>
Expand Down
99 changes: 99 additions & 0 deletions tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System.IO;
using SixLabors.ImageSharp.Formats;

using Xunit;

// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public partial class ImageTests
{
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class Identify : ImageLoadTestBase
{
private static readonly string ActualImagePath = TestFile.GetInputFileFullPath(TestImages.Bmp.F);

private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;

private byte[] ByteArray => this.DataStream.ToArray();

private IImageInfo LocalImageInfo => this.localImageInfoMock.Object;

private IImageFormat LocalImageFormat => this.localImageFormatMock.Object;

private static readonly IImageFormat ExpectedGlobalFormat =
Configuration.Default.ImageFormatsManager.FindFormatByFileExtension("bmp");

[Fact]
public void FromBytes_GlobalConfiguration()
{
IImageInfo info = Image.Identify(this.ActualImageBytes, out IImageFormat type);

Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
}

[Fact]
public void FromBytes_CustomConfiguration()
{
IImageInfo info = Image.Identify(this.LocalConfiguration, this.ByteArray, out IImageFormat type);

Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}

[Fact]
public void FromFileSystemPath_GlobalConfiguration()
{
IImageInfo info = Image.Identify(ActualImagePath, out IImageFormat type);

Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
}

[Fact]
public void FromFileSystemPath_CustomConfiguration()
{
IImageInfo info = Image.Identify(this.LocalConfiguration, this.MockFilePath, out IImageFormat type);

Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}

[Fact]
public void FromStream_GlobalConfiguration()
{
using (var stream = new MemoryStream(this.ActualImageBytes))
{
IImageInfo info = Image.Identify(stream, out IImageFormat type);

Assert.NotNull(info);
Assert.Equal(ExpectedGlobalFormat, type);
}
}

[Fact]
public void FromStream_CustomConfiguration()
{
IImageInfo info = Image.Identify(this.LocalConfiguration, this.DataStream, out IImageFormat type);

Assert.Equal(this.LocalImageInfo, info);
Assert.Equal(this.LocalImageFormat, type);
}

[Fact]
public void WhenNoMatchingFormatFound_ReturnsNull()
{
IImageInfo info = Image.Identify(new Configuration(), this.DataStream, out IImageFormat type);

Assert.Null(info);
Assert.Null(type);
}
}
}
}
11 changes: 7 additions & 4 deletions tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public abstract class ImageLoadTestBase : IDisposable

protected Mock<IImageFormat> localImageFormatMock;

protected Mock<IImageInfo> localImageInfoMock;

protected readonly string MockFilePath = Guid.NewGuid().ToString();

internal readonly Mock<IFileSystem> LocalFileSystemMock = new Mock<IFileSystem>();
Expand Down Expand Up @@ -53,9 +55,12 @@ protected ImageLoadTestBase()
this.localStreamReturnImageRgba32 = new Image<Rgba32>(1, 1);
this.localStreamReturnImageAgnostic = new Image<Bgra4444>(1, 1);

this.localImageInfoMock = new Mock<IImageInfo>();
this.localImageFormatMock = new Mock<IImageFormat>();

this.localDecoder = new Mock<IImageDecoder>();
var detector = new Mock<IImageInfoDetector>();
detector.Setup(x => x.Identify(It.IsAny<Configuration>(), It.IsAny<Stream>())).Returns(this.localImageInfoMock.Object);
this.localDecoder = detector.As<IImageDecoder>();
this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object);

this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>()))
Expand All @@ -80,9 +85,7 @@ protected ImageLoadTestBase()
})
.Returns(this.localStreamReturnImageAgnostic);

this.LocalConfiguration = new Configuration
{
};
this.LocalConfiguration = new Configuration();
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object);

Expand Down