Browse Source

switch from mimetype strings to IImageFormats

af/merge-core
Scott Williams 9 years ago
parent
commit
bb9280f317
  1. 2
      samples/ChangeDefaultEncoderOptions/Program.cs
  2. 183
      src/ImageSharp/Configuration.cs
  3. 27
      src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
  4. 31
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  5. 8
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  6. 45
      src/ImageSharp/Formats/Bmp/BmpImageFormatProvider.cs
  7. 28
      src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
  8. 31
      src/ImageSharp/Formats/Gif/GifFormat.cs
  9. 8
      src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
  10. 45
      src/ImageSharp/Formats/Gif/GifImageFormatProvider.cs
  11. 39
      src/ImageSharp/Formats/IImageFormat.cs
  12. 6
      src/ImageSharp/Formats/IImageFormatDetector.cs
  13. 56
      src/ImageSharp/Formats/IImageFormatProvider.cs
  14. 28
      src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
  15. 31
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  16. 14
      src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
  17. 45
      src/ImageSharp/Formats/Jpeg/JpegImageFormatProvider.cs
  18. 27
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  19. 31
      src/ImageSharp/Formats/Png/PngFormat.cs
  20. 10
      src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
  21. 45
      src/ImageSharp/Formats/Png/PngImageFormatProvider.cs
  22. 23
      src/ImageSharp/IConfigurationModule.cs
  23. 31
      src/ImageSharp/Image/Image.Decode.cs
  24. 34
      src/ImageSharp/Image/Image.FromBytes.cs
  25. 28
      src/ImageSharp/Image/Image.FromFile.cs
  26. 36
      src/ImageSharp/Image/Image.FromStream.cs
  27. 52
      src/ImageSharp/Image/Image{TPixel}.cs
  28. 39
      src/ImageSharp/ImageFormats.cs
  29. 10
      src/ImageSharp/Memory/Buffer.cs
  30. 76
      tests/ImageSharp.Tests/ConfigurationTests.cs
  31. 6
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  32. 56
      tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs
  33. 14
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  34. 19
      tests/ImageSharp.Tests/Image/ImageSaveTests.cs
  35. 4
      tests/ImageSharp.Tests/Image/ImageTests.cs
  36. 33
      tests/ImageSharp.Tests/TestFormat.cs
  37. 4
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

2
samples/ChangeDefaultEncoderOptions/Program.cs

@ -10,7 +10,7 @@ namespace ChangeDefaultEncoderOptions
{
// lets switch out the default encoder for jpeg to one
// that saves at 90 quality and ignores the matadata
Configuration.Default.SetMimeTypeEncoder("image/jpeg", new ImageSharp.Formats.JpegEncoder()
Configuration.Default.SetEncoder(ImageFormats.Jpeg, new ImageSharp.Formats.JpegEncoder()
{
Quality = 90,
IgnoreMetadata = true

183
src/ImageSharp/Configuration.cs

@ -18,7 +18,7 @@ namespace ImageSharp
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public class Configuration : IImageFormatHost
public class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
@ -33,22 +33,22 @@ namespace ImageSharp
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<string, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<string, IImageEncoder>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported mime types keyed to file extensions.
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<string, string> extensionsMap = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private readonly ConcurrentDictionary<string, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<string, IImageDecoder>(StringComparer.OrdinalIgnoreCase);
private readonly List<IImageFormatDetector> imageFormatDetectors = new List<IImageFormatDetector>();
/// <summary>
/// The list of supported <see cref="IMimeTypeDetector"/>s.
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly List<IMimeTypeDetector> mimeTypeDetectors = new List<IMimeTypeDetector>();
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
@ -61,11 +61,11 @@ namespace ImageSharp
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="providers">A collection of providers to configure</param>
public Configuration(params IImageFormatProvider[] providers)
public Configuration(params IConfigurationModule[] providers)
{
if (providers != null)
{
foreach (IImageFormatProvider p in providers)
foreach (IConfigurationModule p in providers)
{
p.Configure(this);
}
@ -88,24 +88,24 @@ namespace ImageSharp
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IMimeTypeDetector"/>s.
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IMimeTypeDetector> MimeTypeDetectors => this.mimeTypeDetectors;
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<string, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<string, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Gets the currently registered file extensions.
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<string, string>> ImageExtensionToMimeTypeMapping => this.extensionsMap;
internal IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
#if !NETSTANDARD1_1
/// <summary>
@ -117,35 +117,69 @@ namespace ImageSharp
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="formatProvider">The format providers to call configure on.</param>
public void AddImageFormat(IImageFormatProvider formatProvider)
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(formatProvider, nameof(formatProvider));
formatProvider.Configure(this);
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <inheritdoc />
public void SetMimeTypeEncoder(string mimeType, IImageEncoder encoder)
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a well know format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
Guard.NotNull(encoder, nameof(encoder));
this.mimeTypeEncoders.AddOrUpdate(mimeType?.Trim(), encoder, (s, e) => encoder);
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
this.imageFormats.Add(format);
}
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtensions(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <inheritdoc />
public void SetFileExtensionToMimeTypeMapping(string extension, string mimeType)
/// <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)
{
Guard.NotNullOrEmpty(extension, nameof(extension));
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
this.extensionsMap.AddOrUpdate(extension?.Trim(), mimeType, (s, e) => mimeType);
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <inheritdoc />
public void SetMimeTypeDecoder(string mimeType, IImageDecoder decoder)
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.mimeTypeDecoders.AddOrUpdate(mimeType, decoder, (s, e) => decoder);
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
@ -153,43 +187,46 @@ namespace ImageSharp
/// </summary>
public void ClearMimeTypeDetectors()
{
this.mimeTypeDetectors.Clear();
this.imageFormatDetectors.Clear();
}
/// <inheritdoc />
public void AddMimeTypeDetector(IMimeTypeDetector detector)
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.mimeTypeDetectors.Add(detector);
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// Creates the default instance with the following <see cref="IImageFormatProvider"/>s preregistered:
/// <para><see cref="PngImageFormatProvider"/></para>
/// <para><see cref="JpegImageFormatProvider"/></para>
/// <para><see cref="GifImageFormatProvider"/></para>
/// <para><see cref="BmpImageFormatProvider"/></para>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <para><see cref="PngConfigurationModule"/></para>
/// <para><see cref="JpegConfigurationModule"/></para>
/// <para><see cref="GifConfigurationModule"/></para>
/// <para><see cref="BmpConfigurationModule"/></para>
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/></returns>
internal static Configuration CreateDefaultInstance()
{
return new Configuration(
new PngImageFormatProvider(),
new JpegImageFormatProvider(),
new GifImageFormatProvider(),
new BmpImageFormatProvider());
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="mimeType">The mime type to discover</param>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
internal IImageDecoder FindMimeTypeDecoder(string mimeType)
internal IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
if (this.mimeTypeDecoders.TryGetValue(mimeType, out IImageDecoder decoder))
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
@ -200,12 +237,12 @@ namespace ImageSharp
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="mimeType">The mime type to discover</param>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
internal IImageEncoder FindMimeTypeEncoder(string mimeType)
internal IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
if (this.mimeTypeEncoders.TryGetValue(mimeType, out IImageEncoder encoder))
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
@ -213,46 +250,12 @@ namespace ImageSharp
return null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="extensions">The extensions to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
internal IImageEncoder FindFileExtensionsEncoder(string extensions)
{
extensions = extensions?.TrimStart('.');
Guard.NotNullOrEmpty(extensions, nameof(extensions));
if (this.extensionsMap.TryGetValue(extensions, out string mimeType))
{
return this.FindMimeTypeEncoder(mimeType);
}
return null;
}
/// <summary>
/// For the specified extension find the mime type.
/// </summary>
/// <param name="extensions">the extensions to discover</param>
/// <returns>The mime type if found otherwise null</returns>
internal string FindFileExtensionsMimeType(string extensions)
{
extensions = extensions?.TrimStart('.');
Guard.NotNullOrEmpty(extensions, nameof(extensions));
if (this.extensionsMap.TryGetValue(extensions, out string mimeType))
{
return mimeType;
}
return null;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.mimeTypeDetectors.Max(x => x.HeaderSize);
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}

27
src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs

@ -0,0 +1,27 @@
// <copyright file="ConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
public class BmpConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Bitmap, new BmpEncoder());
config.SetDecoder(ImageFormats.Bitmap, new BmpDecoder());
config.AddImageFormatDetector(new BmpImageFormatDetector());
}
}
}

31
src/ImageSharp/Formats/Bmp/BmpFormat.cs

@ -0,0 +1,31 @@
// <copyright file="GifFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class BmpFormat : IImageFormat
{
/// <inheritdoc/>
public string Name => "BMP";
/// <inheritdoc/>
public string DefaultMimeType => "image/bmp";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => BmpConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
}
}

8
src/ImageSharp/Formats/Bmp/BmpMimeTypeDetector.cs → src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs

@ -14,23 +14,23 @@ namespace ImageSharp.Formats
/// <summary>
/// Detects bmp file headers
/// </summary>
internal class BmpMimeTypeDetector : IMimeTypeDetector
internal class BmpImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 2;
/// <inheritdoc/>
public string DetectMimeType(Span<byte> header)
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return "image/bmp";
return ImageFormats.Bitmap;
}
return null;
}
private bool IsSupportedFileFormat(Span<byte> header)
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x42 && // B

45
src/ImageSharp/Formats/Bmp/BmpImageFormatProvider.cs

@ -1,45 +0,0 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
public class BmpImageFormatProvider : IImageFormatProvider
{
/// <inheritdoc/>
public void Configure(IImageFormatHost host)
{
var encoder = new BmpEncoder();
foreach (string mimeType in BmpConstants.MimeTypes)
{
host.SetMimeTypeEncoder(mimeType, encoder);
}
foreach (string ext in BmpConstants.FileExtensions)
{
foreach (string mimeType in BmpConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var decoder = new BmpDecoder();
foreach (string mimeType in BmpConstants.MimeTypes)
{
host.SetMimeTypeDecoder(mimeType, decoder);
}
host.AddMimeTypeDetector(new BmpMimeTypeDetector());
}
}
}

28
src/ImageSharp/Formats/Gif/GifConfigurationModule.cs

@ -0,0 +1,28 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
public class GifConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.AddImageFormatDetector(new GifImageFormatDetector());
}
}
}

31
src/ImageSharp/Formats/Gif/GifFormat.cs

@ -0,0 +1,31 @@
// <copyright file="GifFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class GifFormat : IImageFormat
{
/// <inheritdoc/>
public string Name => "GIF";
/// <inheritdoc/>
public string DefaultMimeType => "image/gif";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => GifConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
}
}

8
src/ImageSharp/Formats/Gif/GifMimeTypeDetector.cs → src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs

@ -14,23 +14,23 @@ namespace ImageSharp.Formats
/// <summary>
/// Detects gif file headers
/// </summary>
public class GifMimeTypeDetector : IMimeTypeDetector
public class GifImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 6;
/// <inheritdoc/>
public string DetectMimeType(Span<byte> header)
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return "image/gif";
return ImageFormats.Gif;
}
return null;
}
private bool IsSupportedFileFormat(Span<byte> header)
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x47 && // G

45
src/ImageSharp/Formats/Gif/GifImageFormatProvider.cs

@ -1,45 +0,0 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
public class GifImageFormatProvider : IImageFormatProvider
{
/// <inheritdoc/>
public void Configure(IImageFormatHost host)
{
var encoder = new GifEncoder();
foreach (string mimeType in GifConstants.MimeTypes)
{
host.SetMimeTypeEncoder(mimeType, encoder);
}
foreach (string ext in GifConstants.FileExtensions)
{
foreach (string mimeType in GifConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var decoder = new GifDecoder();
foreach (string mimeType in GifConstants.MimeTypes)
{
host.SetMimeTypeDecoder(mimeType, decoder);
}
host.AddMimeTypeDetector(new GifMimeTypeDetector());
}
}
}

39
src/ImageSharp/Formats/IImageFormat.cs

@ -0,0 +1,39 @@
// <copyright file="IImageFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary>
/// Describes an image format.
/// </summary>
public interface IImageFormat
{
/// <summary>
/// Gets the name that describes this image format.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the default mimetype that the image foramt uses
/// </summary>
string DefaultMimeType { get; }
/// <summary>
/// Gets all the mimetypes that have been used by this image foramt.
/// </summary>
IEnumerable<string> MimeTypes { get; }
/// <summary>
/// Gets the file extensions this image format commonly uses.
/// </summary>
IEnumerable<string> FileExtensions { get; }
}
}

6
src/ImageSharp/Formats/IMimeTypeDetector.cs → src/ImageSharp/Formats/IImageFormatDetector.cs

@ -1,4 +1,4 @@
// <copyright file="IMimeTypeDetector.cs" company="James Jackson-South">
// <copyright file="IImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -12,7 +12,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Used for detecting mime types from a file header
/// </summary>
public interface IMimeTypeDetector
public interface IImageFormatDetector
{
/// <summary>
/// Gets the size of the header for this image type.
@ -25,6 +25,6 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>returns the mime type of detected othersie returns null</returns>
string DetectMimeType(Span<byte> header);
IImageFormat DetectFormat(ReadOnlySpan<byte> header);
}
}

56
src/ImageSharp/Formats/IImageFormatProvider.cs

@ -1,56 +0,0 @@
// <copyright file="IImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Represents an interface that can register image encoders, decoders and mime type detectors.
/// </summary>
public interface IImageFormatProvider
{
/// <summary>
/// Called when loaded so the provider and register its encoders, decoders and mime type detectors into an IImageFormatHost.
/// </summary>
/// <param name="host">The host that will retain the encoders, decodes and mime type detectors.</param>
void Configure(IImageFormatHost host);
}
/// <summary>
/// Represents an interface that can have encoders, decoders and mime type detectors loaded into.
/// </summary>
public interface IImageFormatHost
{
/// <summary>
/// Sets a specific image encoder as the encoder for a specific mime type.
/// </summary>
/// <param name="mimeType">the target mimetype</param>
/// <param name="encoder">the encoder to use</param>
void SetMimeTypeEncoder(string mimeType, IImageEncoder encoder); // could/should this be an Action<IImageEncoder>???
/// <summary>
/// Sets a mapping value between a file extension and a mime type.
/// </summary>
/// <param name="extension">The target mime type</param>
/// <param name="mimetype">The mime type this extension equates to</param>
void SetFileExtensionToMimeTypeMapping(string extension, string mimetype);
/// <summary>
/// Sets a specific image decoder as the decoder for a specific mime type.
/// </summary>
/// <param name="mimeType">The target mime type</param>
/// <param name="decoder">The decoder to use</param>
void SetMimeTypeDecoder(string mimeType, IImageDecoder decoder);
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
void AddMimeTypeDetector(IMimeTypeDetector detector);
}
}

28
src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs

@ -0,0 +1,28 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class JpegConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Jpeg, new JpegEncoder());
config.SetDecoder(ImageFormats.Jpeg, new JpegDecoder());
config.AddImageFormatDetector(new JpegImageFormatDetector());
}
}
}

31
src/ImageSharp/Formats/Jpeg/JpegFormat.cs

@ -0,0 +1,31 @@
// <copyright file="JpegFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class JpegFormat : IImageFormat
{
/// <inheritdoc/>
public string Name => "JPEG";
/// <inheritdoc/>
public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
}
}

14
src/ImageSharp/Formats/Jpeg/JpegMimeTypeDetector.cs → src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs

@ -14,23 +14,23 @@ namespace ImageSharp.Formats
/// <summary>
/// Detects Jpeg file headers
/// </summary>
public class JpegMimeTypeDetector : IMimeTypeDetector
public class JpegImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 11;
/// <inheritdoc/>
public string DetectMimeType(Span<byte> header)
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return "image/jpeg";
return ImageFormats.Jpeg;
}
return null;
}
private bool IsSupportedFileFormat(Span<byte> header)
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
(this.IsJfif(header) || this.IsExif(header) || this.IsJpeg(header));
@ -41,7 +41,7 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJfif(Span<byte> header)
private bool IsJfif(ReadOnlySpan<byte> header)
{
bool isJfif =
header[6] == 0x4A && // J
@ -58,7 +58,7 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsExif(Span<byte> header)
private bool IsExif(ReadOnlySpan<byte> header)
{
bool isExif =
header[6] == 0x45 && // E
@ -76,7 +76,7 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJpeg(Span<byte> header)
private bool IsJpeg(ReadOnlySpan<byte> header)
{
bool isJpg =
header[0] == 0xFF && // 255

45
src/ImageSharp/Formats/Jpeg/JpegImageFormatProvider.cs

@ -1,45 +0,0 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class JpegImageFormatProvider : IImageFormatProvider
{
/// <inheritdoc/>
public void Configure(IImageFormatHost host)
{
var pngEncoder = new JpegEncoder();
foreach (string mimeType in JpegConstants.MimeTypes)
{
host.SetMimeTypeEncoder(mimeType, pngEncoder);
}
foreach (string ext in JpegConstants.FileExtensions)
{
foreach (string mimeType in JpegConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var pngDecoder = new JpegDecoder();
foreach (string mimeType in JpegConstants.MimeTypes)
{
host.SetMimeTypeDecoder(mimeType, pngDecoder);
}
host.AddMimeTypeDetector(new JpegMimeTypeDetector());
}
}
}

27
src/ImageSharp/Formats/Png/PngConfigurationModule.cs

@ -0,0 +1,27 @@
// <copyright file="PngConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
public class PngConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration host)
{
host.SetEncoder(ImageFormats.Png, new PngEncoder());
host.SetDecoder(ImageFormats.Png, new PngDecoder());
host.AddImageFormatDetector(new PngImageFormatDetector());
}
}
}

31
src/ImageSharp/Formats/Png/PngFormat.cs

@ -0,0 +1,31 @@
// <copyright file="PngFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class PngFormat : IImageFormat
{
/// <inheritdoc/>
public string Name => "PNG";
/// <inheritdoc/>
public string DefaultMimeType => "image/png";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => PngConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
}
}

10
src/ImageSharp/Formats/Png/PngMimeTypeDetector.cs → src/ImageSharp/Formats/Png/PngImageFormatDetector.cs

@ -1,4 +1,4 @@
// <copyright file="PngMimeTypeDetector.cs" company="James Jackson-South">
// <copyright file="PngImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -14,23 +14,23 @@ namespace ImageSharp.Formats
/// <summary>
/// Detects png file headers
/// </summary>
public class PngMimeTypeDetector : IMimeTypeDetector
public class PngImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 8;
/// <inheritdoc/>
public string DetectMimeType(Span<byte> header)
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return "image/png";
return ImageFormats.Png;
}
return null;
}
private bool IsSupportedFileFormat(Span<byte> header)
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x89 &&

45
src/ImageSharp/Formats/Png/PngImageFormatProvider.cs

@ -1,45 +0,0 @@
// <copyright file="PngImageFormatProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
public class PngImageFormatProvider : IImageFormatProvider
{
/// <inheritdoc/>
public void Configure(IImageFormatHost host)
{
var pngEncoder = new PngEncoder();
foreach (string mimeType in PngConstants.MimeTypes)
{
host.SetMimeTypeEncoder(mimeType, pngEncoder);
}
foreach (string ext in PngConstants.FileExtensions)
{
foreach (string mimeType in PngConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var pngDecoder = new PngDecoder();
foreach (string mimeType in PngConstants.MimeTypes)
{
host.SetMimeTypeDecoder(mimeType, pngDecoder);
}
host.AddMimeTypeDetector(new PngMimeTypeDetector());
}
}
}

23
src/ImageSharp/IConfigurationModule.cs

@ -0,0 +1,23 @@
// <copyright file="IConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Represents an interface that can register image encoders, decoders and image format detectors.
/// </summary>
public interface IConfigurationModule
{
/// <summary>
/// Called when loaded into a configuration object so the module can register items into the configuration.
/// </summary>
/// <param name="configuration">The configuration that will retain the encoders, decodes and mime type detectors.</param>
void Configure(Configuration configuration);
}
}

31
src/ImageSharp/Image/Image.Decode.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using System.IO;
using System.Linq;
using Formats;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
/// <content>
@ -23,7 +23,7 @@ namespace ImageSharp
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="config">The configuration.</param>
/// <returns>The mime type or null if none found.</returns>
private static string InternalDiscoverMimeType(Stream stream, Configuration config)
private static IImageFormat InternalDetectFormat(Stream stream, Configuration config)
{
// This is probably a candidate for making into a public API in the future!
int maxHeaderSize = config.MaxHeaderSize;
@ -32,17 +32,12 @@ namespace ImageSharp
return null;
}
byte[] header = ArrayPool<byte>.Shared.Rent(maxHeaderSize);
try
using (var buffer = new Buffer<byte>(maxHeaderSize))
{
long startPosition = stream.Position;
stream.Read(header, 0, maxHeaderSize);
stream.Read(buffer.Array, 0, maxHeaderSize);
stream.Position = startPosition;
return config.MimeTypeDetectors.Select(x => x.DetectMimeType(header)).LastOrDefault(x => x != null);
}
finally
{
ArrayPool<byte>.Shared.Return(header);
return config.FormatDetectors.Select(x => x.DetectFormat(buffer)).LastOrDefault(x => x != null);
}
}
@ -51,14 +46,14 @@ namespace ImageSharp
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="config">The configuration.</param>
/// <param name="mimeType">The mimeType.</param>
/// <param name="format">The IImageFormat.</param>
/// <returns>The image format or null if none found.</returns>
private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out string mimeType)
private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format)
{
mimeType = InternalDiscoverMimeType(stream, config);
if (mimeType != null)
format = InternalDetectFormat(stream, config);
if (format != null)
{
return config.FindMimeTypeDecoder(mimeType);
return config.FindDecoder(format);
}
return null;
@ -74,18 +69,18 @@ namespace ImageSharp
/// <returns>
/// A new <see cref="Image{TPixel}"/>.
/// </returns>
private static (Image<TPixel> img, string mimeType) Decode<TPixel>(Stream stream, Configuration config)
private static (Image<TPixel> img, IImageFormat format) Decode<TPixel>(Stream stream, Configuration config)
#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly
where TPixel : struct, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(stream, config, out string mimeType);
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
if (decoder == null)
{
return (null, null);
}
Image<TPixel> img = decoder.Decode<TPixel>(config, stream);
return (img, mimeType);
return (img, format);
}
}
}

34
src/ImageSharp/Image/Image.FromBytes.cs

@ -16,26 +16,26 @@ namespace ImageSharp
public static partial class Image
{
/// <summary>
/// By reading the header on the provided byte array this calculates the images mime type.
/// 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>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(byte[] data)
/// <returns>The format or null if none found.</returns>
public static IImageFormat DetectFormat(byte[] data)
{
return DiscoverMimeType(null, data);
return DetectFormat(null, data);
}
/// <summary>
/// By reading the header on the provided byte array this calculates the images mime type.
/// 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>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(Configuration config, byte[] data)
public static IImageFormat DetectFormat(Configuration config, byte[] data)
{
using (Stream stream = new MemoryStream(data))
{
return DiscoverMimeType(config, stream);
return DetectFormat(config, stream);
}
}
@ -50,9 +50,9 @@ namespace ImageSharp
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="mimeType">The mime type of the decoded image.</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 string mimeType) => Load<Rgba32>(null, data, out mimeType);
public static Image<Rgba32> Load(byte[] data, out IImageFormat format) => Load<Rgba32>(null, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
@ -67,9 +67,9 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="mimeType">The mime type of the decoded image.</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(Configuration config, byte[] data, out string mimeType) => Load<Rgba32>(config, data, out mimeType);
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.
@ -104,13 +104,13 @@ namespace ImageSharp
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="mimeType">The mime type of the decoded image.</param>
/// <param name="format">The mime type 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>(byte[] data, out string mimeType)
public static Image<TPixel> Load<TPixel>(byte[] data, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
return Load<TPixel>(null, data, out mimeType);
return Load<TPixel>(null, data, out format);
}
/// <summary>
@ -134,15 +134,15 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="mimeType">The mime type of the decoded image.</param>
/// <param name="format">The mime type 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 string mimeType)
public static Image<TPixel> Load<TPixel>(Configuration config, byte[] data, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
using (var memoryStream = new MemoryStream(data))
{
return Load<TPixel>(config, memoryStream, out mimeType);
return Load<TPixel>(config, memoryStream, out format);
}
}

28
src/ImageSharp/Image/Image.FromFile.cs

@ -21,9 +21,9 @@ namespace ImageSharp
/// </summary>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(string filePath)
public static IImageFormat DetectFormat(string filePath)
{
return DiscoverMimeType(null, filePath);
return DetectFormat(null, filePath);
}
/// <summary>
@ -32,12 +32,12 @@ namespace ImageSharp
/// <param name="config">The configuration.</param>
/// <param name="filePath">The image file to open and to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(Configuration config, string filePath)
public static IImageFormat DetectFormat(Configuration config, string filePath)
{
config = config ?? Configuration.Default;
using (Stream file = config.FileSystem.OpenRead(filePath))
{
return DiscoverMimeType(config, file);
return DetectFormat(config, file);
}
}
@ -55,12 +55,12 @@ namespace ImageSharp
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="mimeType">The mime type of the decoded 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<Rgba32> Load(string path, out string mimeType) => Load<Rgba32>(path, out mimeType);
public static Image<Rgba32> Load(string path, out IImageFormat format) => Load<Rgba32>(path, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -78,12 +78,12 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="mimeType">The mime type of the decoded 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<Rgba32> Load(Configuration config, string path, out string mimeType) => Load<Rgba32>(config, path, out mimeType);
public static Image<Rgba32> Load(Configuration config, string path, out IImageFormat format) => Load<Rgba32>(config, path, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -127,16 +127,16 @@ namespace ImageSharp
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="mimeType">The mime type of the decoded 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(string path, out string mimeType)
public static Image<TPixel> Load<TPixel>(string path, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
return Load<TPixel>(null, path, out mimeType);
return Load<TPixel>(null, path, out format);
}
/// <summary>
@ -164,19 +164,19 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="mimeType">The mime type of the decoded 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Configuration config, string path, out string mimeType)
public static Image<TPixel> Load<TPixel>(Configuration config, string path, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
config = config ?? Configuration.Default;
using (Stream stream = config.FileSystem.OpenRead(path))
{
return Load<TPixel>(config, stream, out mimeType);
return Load<TPixel>(config, stream, out format);
}
}

36
src/ImageSharp/Image/Image.FromStream.cs

@ -24,9 +24,9 @@ namespace ImageSharp
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(Stream stream)
public static IImageFormat DetectFormat(Stream stream)
{
return DiscoverMimeType(null, stream);
return DetectFormat(null, stream);
}
/// <summary>
@ -35,21 +35,21 @@ namespace ImageSharp
/// <param name="config">The configuration.</param>
/// <param name="stream">The image stream to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static string DiscoverMimeType(Configuration config, Stream stream)
public static IImageFormat DetectFormat(Configuration config, Stream stream)
{
return WithSeekableStream(stream, s => InternalDiscoverMimeType(s, config ?? Configuration.Default));
return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default));
}
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="mimeType">the mime type of the decoded 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<Rgba32> Load(Stream stream, out string mimeType) => Load<Rgba32>(stream, out mimeType);
public static Image<Rgba32> Load(Stream stream, out IImageFormat format) => Load<Rgba32>(stream, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
@ -88,12 +88,12 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="mimeType">the mime type of the decoded 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<Rgba32> Load(Configuration config, Stream stream, out string mimeType) => Load<Rgba32>(config, stream, out mimeType);
public static Image<Rgba32> Load(Configuration config, Stream stream, out IImageFormat format) => Load<Rgba32>(config, stream, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
@ -114,16 +114,16 @@ namespace ImageSharp
/// 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="mimeType">the mime type of the decoded 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>>
public static Image<TPixel> Load<TPixel>(Stream stream, out string mimeType)
public static Image<TPixel> Load<TPixel>(Stream stream, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
return Load<TPixel>(null, stream, out mimeType);
return Load<TPixel>(null, stream, out format);
}
/// <summary>
@ -180,20 +180,20 @@ namespace ImageSharp
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="mimeType">the mime type of the decoded 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>>
public static Image<TPixel> Load<TPixel>(Configuration config, Stream stream, out string mimeType)
public static Image<TPixel> Load<TPixel>(Configuration config, Stream stream, out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
config = config ?? Configuration.Default;
mimeType = null;
(Image<TPixel> img, string mimeType) data = WithSeekableStream(stream, s => Decode<TPixel>(s, config));
format = null;
(Image<TPixel> img, IImageFormat format) data = WithSeekableStream(stream, s => Decode<TPixel>(s, config));
mimeType = data.mimeType;
format = data.format;
if (data.img != null)
{
@ -203,9 +203,9 @@ namespace ImageSharp
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<string, IImageDecoder> val in config.ImageDecoders)
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageDecoders)
{
stringBuilder.AppendLine($" - {val.Key} : {val.Value.GetType().Name}");
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());

52
src/ImageSharp/Image/Image{TPixel}.cs

@ -152,22 +152,22 @@ namespace ImageSharp
/// Saves the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="mimeType">The mime type to save the image to.</param>
/// <param name="format">The format to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public Image<TPixel> Save(Stream stream, string mimeType)
public Image<TPixel> Save(Stream stream, IImageFormat format)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
IImageEncoder encoder = this.Configuration.FindMimeTypeEncoder(mimeType);
Guard.NotNull(format, nameof(format));
IImageEncoder encoder = this.Configuration.FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<string, IImageEncoder> val in this.Configuration.ImageEncoders)
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in this.Configuration.ImageEncoders)
{
stringBuilder.AppendLine($" - {val.Key} : {val.Value.GetType().Name}");
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
@ -207,26 +207,28 @@ namespace ImageSharp
Guard.NotNullOrEmpty(filePath, nameof(filePath));
string ext = Path.GetExtension(filePath).Trim('.');
IImageEncoder encoder = this.Configuration.FindFileExtensionsEncoder(ext);
if (encoder == null)
var format = this.Configuration.FindFormatByFileExtensions(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
string mime = this.Configuration.FindFileExtensionsMimeType(ext);
if (mime == null)
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in this.Configuration.ImageFormats)
{
stringBuilder.AppendLine($"Can't find a mime type for the file extention '{ext}'. Registered file extension maps include:");
foreach (KeyValuePair<string, string> map in this.Configuration.ImageExtensionToMimeTypeMapping)
{
stringBuilder.AppendLine($" - {map.Key} : {map.Value}");
}
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
}
else
throw new NotSupportedException(stringBuilder.ToString());
}
IImageEncoder encoder = this.Configuration.FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in this.Configuration.ImageEncoders)
{
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using mime type '{mime}'. Registered encoders include:");
foreach (KeyValuePair<string, IImageEncoder> enc in this.Configuration.ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
@ -262,15 +264,15 @@ namespace ImageSharp
/// Returns a Base64 encoded string from the given image.
/// </summary>
/// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example>
/// <param name="mimeType">The mimeType.</param>
/// <param name="format">The format.</param>
/// <returns>The <see cref="string"/></returns>
public string ToBase64String(string mimeType)
public string ToBase64String(IImageFormat format)
{
using (var stream = new MemoryStream())
{
this.Save(stream, mimeType);
this.Save(stream, format);
stream.Flush();
return $"data:{mimeType};base64,{Convert.ToBase64String(stream.ToArray())}";
return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}";
}
}

39
src/ImageSharp/ImageFormats.cs

@ -0,0 +1,39 @@
// <copyright file="IImageFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
/// <summary>
/// The static collection of all the default image formats
/// </summary>
public static class ImageFormats
{
/// <summary>
/// The format details for the jpegs.
/// </summary>
public static readonly IImageFormat Jpeg = new JpegFormat();
/// <summary>
/// The format details for the pngs.
/// </summary>
public static readonly IImageFormat Png = new PngFormat();
/// <summary>
/// The format details for the gifs.
/// </summary>
public static readonly IImageFormat Gif = new GifFormat();
/// <summary>
/// The format details for the bitmaps.
/// </summary>
public static readonly IImageFormat Bitmap = new BmpFormat();
}
}

10
src/ImageSharp/Memory/Buffer.cs

@ -115,6 +115,16 @@ namespace ImageSharp.Memory
}
}
/// <summary>
/// Converts <see cref="Buffer{T}"/> to an <see cref="ReadOnlySpan{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(Buffer<T> buffer)
{
return new ReadOnlySpan<T>(buffer.Array, 0, buffer.Length);
}
/// <summary>
/// Converts <see cref="Buffer{T}"/> to an <see cref="Span{T}"/>.
/// </summary>

76
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -40,8 +40,8 @@ namespace ImageSharp.Tests
[Fact]
public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded()
{
Assert.Equal(6, DefaultConfiguration.ImageEncoders.Count());
Assert.Equal(6, DefaultConfiguration.ImageDecoders.Count());
Assert.Equal(4, DefaultConfiguration.ImageEncoders.Count());
Assert.Equal(4, DefaultConfiguration.ImageDecoders.Count());
}
/// <summary>
@ -73,11 +73,11 @@ namespace ImageSharp.Tests
}
[Fact]
public void AddMimeTypeDetectorNullthrows()
public void AddImageFormatDetectorNullthrows()
{
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.AddMimeTypeDetector(null);
DefaultConfiguration.AddImageFormatDetector(null);
});
}
@ -86,49 +86,32 @@ namespace ImageSharp.Tests
{
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeEncoder(null, new Mock<IImageEncoder>().Object);
DefaultConfiguration.SetEncoder(null, new Mock<IImageEncoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeEncoder("sdsdsd", null);
DefaultConfiguration.SetEncoder(ImageFormats.Bitmap, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeEncoder(null, null);
DefaultConfiguration.SetEncoder(null, null);
});
}
[Fact]
public void RegisterNullFileExtEncoder()
public void RegisterNullSetDecoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionToMimeTypeMapping(null, "str");
DefaultConfiguration.SetDecoder(null, new Mock<IImageDecoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionToMimeTypeMapping("sdsdsd", null);
DefaultConfiguration.SetDecoder(ImageFormats.Bitmap, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionToMimeTypeMapping(null, null);
});
}
[Fact]
public void RegisterNullMimeTypeDecoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeDecoder(null, new Mock<IImageDecoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeDecoder("sdsdsd", null);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetMimeTypeDecoder(null, null);
DefaultConfiguration.SetDecoder(null, null);
});
}
@ -136,28 +119,13 @@ namespace ImageSharp.Tests
public void RegisterMimeTypeEncoderReplacesLast()
{
var encoder1 = new Mock<IImageEncoder>().Object;
ConfigurationEmpty.SetMimeTypeEncoder("test", encoder1);
var found = ConfigurationEmpty.FindMimeTypeEncoder("TEST");
ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1);
var found = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder1, found);
var encoder2 = new Mock<IImageEncoder>().Object;
ConfigurationEmpty.SetMimeTypeEncoder("TEST", encoder2);
var found2 = ConfigurationEmpty.FindMimeTypeEncoder("test");
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}
[Fact]
public void RegisterFileExtEnecoderReplacesLast()
{
var encoder1 = "mime1";
ConfigurationEmpty.SetFileExtensionToMimeTypeMapping("TEST", encoder1);
var found = ConfigurationEmpty.FindFileExtensionsMimeType("test");
Assert.Equal(encoder1, found);
var encoder2 = "mime2";
ConfigurationEmpty.SetFileExtensionToMimeTypeMapping("test", encoder2);
var found2 = ConfigurationEmpty.FindFileExtensionsMimeType("TEST");
ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2);
var found2 = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}
@ -166,13 +134,13 @@ namespace ImageSharp.Tests
public void RegisterMimeTypeDecoderReplacesLast()
{
var decoder1 = new Mock<IImageDecoder>().Object;
ConfigurationEmpty.SetMimeTypeDecoder("test", decoder1);
var found = ConfigurationEmpty.FindMimeTypeDecoder("TEST");
ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
var found = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
var decoder2 = new Mock<IImageDecoder>().Object;
ConfigurationEmpty.SetMimeTypeDecoder("TEST", decoder2);
var found2 = ConfigurationEmpty.FindMimeTypeDecoder("test");
ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
var found2 = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2);
}
@ -181,7 +149,7 @@ namespace ImageSharp.Tests
[Fact]
public void ConstructorCallConfigureOnFormatProvider()
{
var provider = new Mock<IImageFormatProvider>();
var provider = new Mock<IConfigurationModule>();
var config = new Configuration(provider.Object);
provider.Verify(x => x.Configure(config));
@ -190,9 +158,9 @@ namespace ImageSharp.Tests
[Fact]
public void AddFormatCallsConfig()
{
var provider = new Mock<IImageFormatProvider>();
var provider = new Mock<IConfigurationModule>();
var config = new Configuration();
config.AddImageFormat(provider.Object);
config.Configure(provider.Object);
provider.Verify(x => x.Configure(config));
}

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

@ -6,7 +6,7 @@
namespace ImageSharp.Tests
{
using System.IO;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
using Xunit;
@ -36,7 +36,7 @@ namespace ImageSharp.Tests
using (Image<Rgba32> image = file.CreateImage())
{
string filename = path + "/" + file.FileNameWithoutExtension + ".txt";
File.WriteAllText(filename, image.ToBase64String("image/png"));
File.WriteAllText(filename, image.ToBase64String(ImageFormats.Png));
}
}
}
@ -135,7 +135,7 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files)
{
byte[] serialized;
using (Image<Rgba32> image = Image.Load(file.Bytes, out string mimeType))
using (Image<Rgba32> image = Image.Load(file.Bytes, out IImageFormat mimeType))
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream, mimeType);

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

@ -17,23 +17,27 @@ namespace ImageSharp.Tests
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class DiscoverMimeTypeTests
public class DiscoverImageFormatTests
{
private readonly Mock<IFileSystem> fileSystem;
private readonly string FilePath;
private readonly Mock<IMimeTypeDetector> localMimeTypeDetector;
private readonly Mock<IImageFormatDetector> localMimeTypeDetector;
private readonly Mock<IImageFormat> localImageFormatMock;
public IImageFormat localImageFormat => 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 DiscoverMimeTypeTests()
public DiscoverImageFormatTests()
{
this.localMimeTypeDetector = new Mock<IMimeTypeDetector>();
this.localImageFormatMock = new Mock<IImageFormat>();
this.localMimeTypeDetector = new Mock<IImageFormatDetector>();
this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1);
this.localMimeTypeDetector.Setup(x => x.DetectMimeType(It.IsAny<Span<byte>>())).Returns(localMimeType);
this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny<ReadOnlySpan<byte>>())).Returns(localImageFormatMock.Object);
this.fileSystem = new Mock<IFileSystem>();
@ -41,7 +45,7 @@ namespace ImageSharp.Tests
{
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.AddMimeTypeDetector(this.localMimeTypeDetector.Object);
this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object);
TestFormat.RegisterGloablTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();
@ -55,52 +59,52 @@ namespace ImageSharp.Tests
}
[Fact]
public void DiscoverMimeTypeByteArray()
public void DiscoverImageFormatByteArray()
{
var type = Image.DiscoverMimeType(DataStream.ToArray());
Assert.Equal(TestFormat.GlobalTestFormat.MimeType, type);
var type = Image.DetectFormat(DataStream.ToArray());
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverMimeTypeByteArray_WithConfig()
public void DiscoverImageFormatByteArray_WithConfig()
{
var type = Image.DiscoverMimeType(this.LocalConfiguration, DataStream.ToArray());
Assert.Equal(localMimeType, type);
var type = Image.DetectFormat(this.LocalConfiguration, DataStream.ToArray());
Assert.Equal(localImageFormat, type);
}
[Fact]
public void DiscoverMimeTypeFile()
public void DiscoverImageFormatFile()
{
var type = Image.DiscoverMimeType(this.FilePath);
Assert.Equal(TestFormat.GlobalTestFormat.MimeType, type);
var type = Image.DetectFormat(this.FilePath);
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverMimeTypeFilePath_WithConfig()
public void DiscoverImageFormatFilePath_WithConfig()
{
var type = Image.DiscoverMimeType(this.LocalConfiguration, FilePath);
Assert.Equal(localMimeType, type);
var type = Image.DetectFormat(this.LocalConfiguration, FilePath);
Assert.Equal(localImageFormat, type);
}
[Fact]
public void DiscoverMimeTypeStream()
public void DiscoverImageFormatStream()
{
var type = Image.DiscoverMimeType(this.DataStream);
Assert.Equal(TestFormat.GlobalTestFormat.MimeType, type);
var type = Image.DetectFormat(this.DataStream);
Assert.Equal(TestFormat.GlobalTestFormat, type);
}
[Fact]
public void DiscoverMimeTypeFileStream_WithConfig()
public void DiscoverImageFormatFileStream_WithConfig()
{
var type = Image.DiscoverMimeType(this.LocalConfiguration, DataStream);
Assert.Equal(localMimeType, type);
var type = Image.DetectFormat(this.LocalConfiguration, DataStream);
Assert.Equal(localImageFormat, type);
}
[Fact]
public void DiscoverMimeTypeNoDetectorsRegisterdShouldReturnNull()
public void DiscoverImageFormatNoDetectorsRegisterdShouldReturnNull()
{
var type = Image.DiscoverMimeType(new Configuration(), DataStream);
var type = Image.DetectFormat(new Configuration(), DataStream);
Assert.Null(type);
}
}

14
tests/ImageSharp.Tests/Image/ImageLoadTests.cs

@ -23,7 +23,8 @@ namespace ImageSharp.Tests
private Image<Rgba32> returnImage;
private Mock<IImageDecoder> localDecoder;
private readonly string FilePath;
private readonly Mock<IMimeTypeDetector> localMimeTypeDetector;
private readonly Mock<IImageFormatDetector> localMimeTypeDetector;
private readonly Mock<IImageFormat> localImageFormatMock;
public Configuration LocalConfiguration { get; private set; }
public byte[] Marker { get; private set; }
@ -34,10 +35,12 @@ namespace ImageSharp.Tests
{
this.returnImage = new Image<Rgba32>(1, 1);
this.localImageFormatMock = new Mock<IImageFormat>();
this.localDecoder = new Mock<IImageDecoder>();
this.localMimeTypeDetector = new Mock<IMimeTypeDetector>();
this.localMimeTypeDetector = new Mock<IImageFormatDetector>();
this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1);
this.localMimeTypeDetector.Setup(x => x.DetectMimeType(It.IsAny<Span<byte>>())).Returns("test");
this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny<ReadOnlySpan<byte>>())).Returns(localImageFormatMock.Object);
this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>()))
@ -56,8 +59,8 @@ namespace ImageSharp.Tests
{
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.AddMimeTypeDetector(this.localMimeTypeDetector.Object);
this.LocalConfiguration.SetMimeTypeDecoder("test", this.localDecoder.Object);
this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object);
this.LocalConfiguration.SetDecoder(localImageFormatMock.Object, this.localDecoder.Object);
TestFormat.RegisterGloablTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();
@ -78,7 +81,6 @@ namespace ImageSharp.Tests
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]

19
tests/ImageSharp.Tests/Image/ImageSaveTests.cs

@ -24,13 +24,17 @@ namespace ImageSharp.Tests
private readonly Mock<IFileSystem> fileSystem;
private readonly Mock<IImageEncoder> encoder;
private readonly Mock<IImageEncoder> encoderNotInFormat;
private Mock<IMimeTypeDetector> localMimeTypeDetector;
private Mock<IImageFormatDetector> localMimeTypeDetector;
private Mock<IImageFormat> localImageFormat;
public ImageSaveTests()
{
this.localMimeTypeDetector = new Mock<IMimeTypeDetector>();
this.localImageFormat = new Mock<IImageFormat>();
this.localImageFormat.Setup(x => x.FileExtensions).Returns(new[] { "png" });
this.localMimeTypeDetector = new Mock<IImageFormatDetector>();
this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1);
this.localMimeTypeDetector.Setup(x => x.DetectMimeType(It.IsAny<Span<byte>>())).Returns("img/test");
this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny<Span<byte>>())).Returns(localImageFormat.Object);
this.encoder = new Mock<IImageEncoder>();
@ -41,9 +45,8 @@ namespace ImageSharp.Tests
{
FileSystem = this.fileSystem.Object
};
config.AddMimeTypeDetector(this.localMimeTypeDetector.Object);
config.SetMimeTypeEncoder("img/test", this.encoder.Object);
config.SetFileExtensionToMimeTypeMapping("png", "img/test");
config.AddImageFormatDetector(this.localMimeTypeDetector.Object);
config.SetEncoder(localImageFormat.Object, this.encoder.Object);
this.Image = new Image<Rgba32>(config, 1, 1);
}
@ -72,7 +75,7 @@ namespace ImageSharp.Tests
[Fact]
public void ToBase64String()
{
var str = this.Image.ToBase64String("img/test");
var str = this.Image.ToBase64String(localImageFormat.Object);
this.encoder.Verify(x => x.Encode<Rgba32>(this.Image, It.IsAny<Stream>()));
}
@ -81,7 +84,7 @@ namespace ImageSharp.Tests
public void SaveStreamWithMime()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, "img/test");
this.Image.Save(stream, localImageFormat.Object);
this.encoder.Verify(x => x.Encode<Rgba32>(this.Image, stream));
}

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

@ -76,7 +76,7 @@ namespace ImageSharp.Tests
using (Image<Rgba32> img = Image.Load(file, out var mime))
{
Assert.Equal("image/png", mime);
Assert.Equal("image/png", mime.DefaultMimeType);
}
}
@ -105,7 +105,7 @@ namespace ImageSharp.Tests
}
using (Image<Rgba32> img = Image.Load(file, out var mime))
{
Assert.Equal("image/png", mime);
Assert.Equal("image/png", mime.DefaultMimeType);
}
}
}

33
tests/ImageSharp.Tests/TestFormat.cs

@ -19,13 +19,13 @@ namespace ImageSharp.Tests
/// <summary>
/// A test image file.
/// </summary>
public class TestFormat : IImageFormatProvider
public class TestFormat : IConfigurationModule, IImageFormat
{
public static TestFormat GlobalTestFormat { get; } = new TestFormat();
public static void RegisterGloablTestFormat()
{
Configuration.Default.AddImageFormat(GlobalTestFormat);
Configuration.Default.Configure(GlobalTestFormat);
}
public TestFormat()
@ -93,7 +93,15 @@ namespace ImageSharp.Tests
public int HeaderSize => this.header.Length;
public bool IsSupportedFileFormat(Span<byte> header)
public string Name => this.Extension;
public string DefaultMimeType => this.MimeType;
public IEnumerable<string> MimeTypes => new[] { this.MimeType };
public IEnumerable<string> FileExtensions => this.SupportedExtensions;
public bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
if (header.Length < this.header.Length)
{
@ -109,16 +117,11 @@ namespace ImageSharp.Tests
return true;
}
public void Configure(IImageFormatHost host)
public void Configure(Configuration host)
{
host.AddMimeTypeDetector(new TestHeader(this));
foreach (var ext in this.SupportedExtensions)
{
host.SetFileExtensionToMimeTypeMapping(ext, this.MimeType);
}
host.SetMimeTypeEncoder(this.MimeType, new TestEncoder(this));
host.SetMimeTypeDecoder(this.MimeType, new TestDecoder(this));
host.AddImageFormatDetector(new TestHeader(this));
host.SetEncoder(this, new TestEncoder(this));
host.SetDecoder(this, new TestDecoder(this));
}
public struct DecodeOperation
@ -150,17 +153,17 @@ namespace ImageSharp.Tests
}
}
public class TestHeader : IMimeTypeDetector
public class TestHeader : IImageFormatDetector
{
private TestFormat testFormat;
public int HeaderSize => testFormat.HeaderSize;
public string DetectMimeType(Span<byte> header)
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (testFormat.IsSupportedFileFormat(header))
return testFormat.MimeType;
return testFormat;
return null;
}

4
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -124,13 +124,13 @@ namespace ImageSharp.Tests
private static IImageEncoder GetImageFormatByExtension(string extension)
{
extension = extension?.TrimStart('.');
return Configuration.Default.FindFileExtensionsEncoder(extension);
var format = Configuration.Default.FindFormatByFileExtensions(extension);
return Configuration.Default.FindEncoder(format);
}
private string GetTestOutputDir()
{
string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName);
return CreateOutputDirectory(testGroupName);
}
}

Loading…
Cancel
Save