|
|
|
@ -5,6 +5,7 @@ using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.IO; |
|
|
|
using System.Text; |
|
|
|
using System.Threading.Tasks; |
|
|
|
using SixLabors.ImageSharp.Formats; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
@ -99,6 +100,21 @@ namespace SixLabors.ImageSharp |
|
|
|
public static Image Load(Stream stream, out IImageFormat format) |
|
|
|
=> Load(Configuration.Default, stream, out format); |
|
|
|
|
|
|
|
|
|
|
|
/// <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="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="UnknownImageFormatException">Image format not recognised.</exception>
|
|
|
|
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
|
|
|
|
/// <returns>The <see cref="Image"/>.</returns>
|
|
|
|
public static Task<FormattedImage> LoadWithFormatAsync(Stream stream) |
|
|
|
=> LoadWithFormatAsync(Configuration.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.
|
|
|
|
@ -111,6 +127,18 @@ namespace SixLabors.ImageSharp |
|
|
|
/// <returns>The <see cref="Image"/>.</returns>
|
|
|
|
public static Image Load(Stream stream) => Load(Configuration.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>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image> LoadAsync(Stream stream) => LoadAsync(Configuration.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.
|
|
|
|
@ -126,6 +154,21 @@ namespace SixLabors.ImageSharp |
|
|
|
public static Image Load(Stream stream, IImageDecoder decoder) |
|
|
|
=> Load(Configuration.Default, stream, decoder); |
|
|
|
|
|
|
|
/// <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="decoder">The decoder.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The decoder is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image> LoadAsync(Stream stream, IImageDecoder decoder) |
|
|
|
=> LoadAsync(Configuration.Default, stream, decoder); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
|
|
|
|
/// The pixel format is selected by the decoder.
|
|
|
|
@ -146,6 +189,26 @@ namespace SixLabors.ImageSharp |
|
|
|
return WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <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="configuration">The configuration for the decoder.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <param name="decoder">The decoder.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The decoder is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image> LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder) |
|
|
|
{ |
|
|
|
Guard.NotNull(decoder, nameof(decoder)); |
|
|
|
return WithSeekableStreamAsync(configuration, stream, s => decoder.DecodeAsync(configuration, s)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -159,6 +222,23 @@ namespace SixLabors.ImageSharp |
|
|
|
/// <returns>A new <see cref="Image"/>.</returns>>
|
|
|
|
public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decode a new instance of the <see cref="Image"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The configuration for the decoder.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 async Task<Image> LoadAsync(Configuration configuration, Stream stream) |
|
|
|
{ |
|
|
|
var fmt = await LoadWithFormatAsync(configuration, stream); |
|
|
|
return fmt.Image; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -173,6 +253,20 @@ namespace SixLabors.ImageSharp |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> Load<TPixel>(Configuration.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>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image<TPixel>> LoadAsync<TPixel>(Stream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> LoadAsync<TPixel>(Configuration.Default, stream); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -188,6 +282,22 @@ namespace SixLabors.ImageSharp |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> Load<TPixel>(Configuration.Default, stream, out format); |
|
|
|
|
|
|
|
|
|
|
|
/// <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.</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 Task<FormattedImage<TPixel>> LoadWithFormatAsync<TPixel>(Stream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> LoadWithFormatAsync<TPixel>(Configuration.Default, stream); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -203,6 +313,21 @@ namespace SixLabors.ImageSharp |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> WithSeekableStream(Configuration.Default, stream, s => decoder.Decode<TPixel>(Configuration.Default, s)); |
|
|
|
|
|
|
|
/// <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="decoder">The decoder.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image<TPixel>> LoadAsync<TPixel>(Stream stream, IImageDecoder decoder) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> WithSeekableStreamAsync(Configuration.Default, stream, s => decoder.DecodeAsync<TPixel>(Configuration.Default, s)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -220,6 +345,23 @@ namespace SixLabors.ImageSharp |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> WithSeekableStream(configuration, stream, s => decoder.Decode<TPixel>(configuration, s)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The Configuration.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <param name="decoder">The decoder.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 Task<Image<TPixel>> LoadAsync<TPixel>(Configuration configuration, Stream stream, IImageDecoder decoder) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
=> WithSeekableStreamAsync(configuration, stream, s => decoder.DecodeAsync<TPixel>(configuration, s)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
@ -272,6 +414,89 @@ namespace SixLabors.ImageSharp |
|
|
|
throw new UnknownImageFormatException(sb.ToString()); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The configuration options.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 async Task<FormattedImage> LoadWithFormatAsync(Configuration configuration, Stream stream) |
|
|
|
{ |
|
|
|
(Image img, IImageFormat format) data = await WithSeekableStreamAsync(configuration, stream, s => DecodeAsync(s, configuration)); |
|
|
|
|
|
|
|
if (data.img != null) |
|
|
|
{ |
|
|
|
return data; |
|
|
|
} |
|
|
|
|
|
|
|
var sb = new StringBuilder(); |
|
|
|
sb.AppendLine("Image cannot be loaded. Available decoders:"); |
|
|
|
|
|
|
|
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in configuration.ImageFormatsManager.ImageDecoders) |
|
|
|
{ |
|
|
|
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); |
|
|
|
} |
|
|
|
|
|
|
|
throw new UnknownImageFormatException(sb.ToString()); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The configuration options.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 async Task<FormattedImage<TPixel>> LoadWithFormatAsync<TPixel>(Configuration configuration, Stream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
(Image<TPixel> img, IImageFormat format) data = await WithSeekableStreamAsync(configuration, stream, s => DecodeAsync<TPixel>(s, configuration)); |
|
|
|
|
|
|
|
if (data.img != null) |
|
|
|
{ |
|
|
|
return data; |
|
|
|
} |
|
|
|
|
|
|
|
var sb = new StringBuilder(); |
|
|
|
sb.AppendLine("Image cannot be loaded. Available decoders:"); |
|
|
|
|
|
|
|
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in configuration.ImageFormatsManager.ImageDecoders) |
|
|
|
{ |
|
|
|
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); |
|
|
|
} |
|
|
|
|
|
|
|
throw new UnknownImageFormatException(sb.ToString()); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The configuration options.</param>
|
|
|
|
/// <param name="stream">The stream containing image information.</param>
|
|
|
|
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
|
|
|
|
/// <exception cref="ArgumentNullException">The stream is null.</exception>
|
|
|
|
/// <exception cref="NotSupportedException">The stream is not readable.</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 async Task<Image<TPixel>> LoadAsync<TPixel>(Configuration configuration, Stream stream) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
(Image<TPixel> img, _) = await LoadWithFormatAsync<TPixel>(configuration, stream); |
|
|
|
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.
|
|
|
|
@ -336,5 +561,121 @@ namespace SixLabors.ImageSharp |
|
|
|
return action(memoryStream); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static async Task<T> WithSeekableStreamAsync<T>(Configuration configuration, Stream stream, Func<Stream, Task<T>> action) |
|
|
|
{ |
|
|
|
Guard.NotNull(configuration, nameof(configuration)); |
|
|
|
Guard.NotNull(stream, nameof(stream)); |
|
|
|
|
|
|
|
if (!stream.CanRead) |
|
|
|
{ |
|
|
|
throw new NotSupportedException("Cannot read from the stream."); |
|
|
|
} |
|
|
|
|
|
|
|
// to make sure we don't trigger anything with aspnetcore then we just need to make sure we are seekable and we make the copy using CopyToAsync
|
|
|
|
// if the stream is seekable then we arn't using one of the aspnetcore wrapped streams that error on sync api calls and we can use it with out
|
|
|
|
// having to further wrap
|
|
|
|
if (stream.CanSeek) |
|
|
|
{ |
|
|
|
if (configuration.ReadOrigin == ReadOrigin.Begin) |
|
|
|
{ |
|
|
|
stream.Position = 0; |
|
|
|
} |
|
|
|
|
|
|
|
return await action(stream); |
|
|
|
} |
|
|
|
|
|
|
|
using (var memoryStream = new MemoryStream()) // should really find a nice way to use a pool for these!!
|
|
|
|
{ |
|
|
|
await stream.CopyToAsync(memoryStream); |
|
|
|
memoryStream.Position = 0; |
|
|
|
|
|
|
|
return await action(memoryStream); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public readonly struct FormattedImage<TPixel> where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
public FormattedImage(Image<TPixel> image, IImageFormat format) |
|
|
|
{ |
|
|
|
this.Image = image; |
|
|
|
this.Format = format; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Image<TPixel> Image { get; } |
|
|
|
|
|
|
|
public readonly IImageFormat Format { get; } |
|
|
|
|
|
|
|
|
|
|
|
public static implicit operator (Image<TPixel> image, IImageFormat format)(FormattedImage<TPixel> value) |
|
|
|
{ |
|
|
|
return (value.Image, value.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public static implicit operator FormattedImage<TPixel>((Image<TPixel> image, IImageFormat format) value) |
|
|
|
{ |
|
|
|
return new FormattedImage<TPixel>(value.image, value.format); |
|
|
|
} |
|
|
|
|
|
|
|
public override bool Equals(object obj) |
|
|
|
{ |
|
|
|
return obj is FormattedImage<TPixel> other && |
|
|
|
EqualityComparer<Image<TPixel>>.Default.Equals(this.Image, other.Image) && |
|
|
|
EqualityComparer<IImageFormat>.Default.Equals(this.Format, other.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public override int GetHashCode() |
|
|
|
{ |
|
|
|
return HashCode.Combine(this.Image, this.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public void Deconstruct(out Image<TPixel> image, out IImageFormat format) |
|
|
|
{ |
|
|
|
image = this.Image; |
|
|
|
format = this.Format; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public readonly struct FormattedImage |
|
|
|
{ |
|
|
|
public FormattedImage(Image image, IImageFormat format) |
|
|
|
{ |
|
|
|
this.Image = image; |
|
|
|
this.Format = format; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Image Image { get; } |
|
|
|
|
|
|
|
public readonly IImageFormat Format { get; } |
|
|
|
|
|
|
|
|
|
|
|
public static implicit operator (Image image, IImageFormat format)(FormattedImage value) |
|
|
|
{ |
|
|
|
return (value.Image, value.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public static implicit operator FormattedImage((Image image, IImageFormat format) value) |
|
|
|
{ |
|
|
|
return new FormattedImage(value.image, value.format); |
|
|
|
} |
|
|
|
|
|
|
|
public override bool Equals(object obj) |
|
|
|
{ |
|
|
|
return obj is FormattedImage other && |
|
|
|
EqualityComparer<Image>.Default.Equals(this.Image, other.Image) && |
|
|
|
EqualityComparer<IImageFormat>.Default.Equals(this.Format, other.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public override int GetHashCode() |
|
|
|
{ |
|
|
|
return HashCode.Combine(this.Image, this.Format); |
|
|
|
} |
|
|
|
|
|
|
|
public void Deconstruct(out Image image, out IImageFormat format) |
|
|
|
{ |
|
|
|
image = this.Image; |
|
|
|
format = this.Format; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|