Browse Source

Merge pull request #478 from vpenades/master

Refactored Image Formats management into its own class
af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
caa816052d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 417
      src/ImageSharp/Configuration.cs
  2. 6
      src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
  3. 2
      src/ImageSharp/Formats/Bmp/ImageExtensions.cs
  4. 6
      src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
  5. 2
      src/ImageSharp/Formats/Gif/ImageExtensions.cs
  6. 186
      src/ImageSharp/Formats/ImageFormatManager.cs
  7. 2
      src/ImageSharp/Formats/Jpeg/ImageExtensions.cs
  8. 6
      src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
  9. 2
      src/ImageSharp/Formats/Png/ImageExtensions.cs
  10. 6
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  11. 4
      src/ImageSharp/Image/Image.Decode.cs
  12. 2
      src/ImageSharp/Image/Image.FromStream.cs
  13. 10
      src/ImageSharp/Image/ImageExtensions.cs
  14. 89
      tests/ImageSharp.Tests/ConfigurationTests.cs
  15. 129
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  16. 2
      tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs
  17. 4
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  18. 4
      tests/ImageSharp.Tests/Image/ImageSaveTests.cs
  19. 6
      tests/ImageSharp.Tests/TestFormat.cs
  20. 12
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  21. 2
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

417
src/ImageSharp/Configuration.cs

@ -1,272 +1,145 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public sealed class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules)
{
if (configurationModules != null)
{
foreach (IConfigurationModule p in configurationModules)
{
p.Configure(this);
}
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
public static Configuration Default { get; } = Lazy.Value;
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
/// </summary>
public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current;
/// <summary>
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
/// </summary>
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault();
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the filesystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
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 FindFormatByFileExtension(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <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)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <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.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.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <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.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
return null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return null;
}
/// <summary>
/// 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 PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public sealed class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules)
{
if (configurationModules != null)
{
foreach (IConfigurationModule p in configurationModules)
{
p.Configure(this);
}
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
public static Configuration Default { get; } = Lazy.Value;
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats;
/// <summary>
/// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
/// </summary>
public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current;
/// <summary>
/// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use.
/// </summary>
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
/// <summary>
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
/// </summary>
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault();
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize;
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the filesystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <summary>
/// Creates a shallow copy of the <see cref="Configuration"/>
/// </summary>
/// <returns>A new configuration instance</returns>
public Configuration ShallowCopy()
{
return new Configuration
{
ParallelOptions = this.ParallelOptions,
ImageFormatsManager = this.ImageFormatsManager,
MemoryManager = this.MemoryManager,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,
#if !NETSTANDARD1_1
FileSystem = this.FileSystem
#endif
};
}
/// <summary>
/// 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 PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
}
}

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

@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
config.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
config.AddImageFormatDetector(new BmpImageFormatDetector());
config.ImageFormatsManager.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
config.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector());
}
}
}

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

@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream, BmpEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Bmp));
}
}

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

@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.ImageFormatsManager.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.AddImageFormatDetector(new GifImageFormatDetector());
config.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector());
}
}
}

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

@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Gif));
}
}

186
src/ImageSharp/Formats/ImageFormatManager.cs

@ -0,0 +1,186 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Collection of Image Formats to be used in <see cref="Configuration" /> class.
/// </summary>
public class ImageFormatManager
{
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
/// </summary>
public ImageFormatManager()
{
}
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
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 FindFormatByFileExtension(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <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)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <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.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.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <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.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
return null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return null;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}

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

@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, JpegEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Jpeg));
}
}

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

@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Jpeg, new JpegEncoder());
config.SetDecoder(ImageFormats.Jpeg, new JpegDecoder());
config.ImageFormatsManager.SetEncoder(ImageFormats.Jpeg, new JpegEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Jpeg, new JpegDecoder());
config.AddImageFormatDetector(new JpegImageFormatDetector());
config.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector());
}
}
}

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

@ -35,6 +35,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, PngEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Png));
}
}

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

@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Png, new PngEncoder());
config.SetDecoder(ImageFormats.Png, new PngDecoder());
config.AddImageFormatDetector(new PngImageFormatDetector());
config.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder());
config.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
}
}
}

4
src/ImageSharp/Image/Image.Decode.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp
long startPosition = stream.Position;
stream.Read(buffer.Array, 0, maxHeaderSize);
stream.Position = startPosition;
return config.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null);
return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null);
}
}
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp
format = InternalDetectFormat(stream, config);
if (format != null)
{
return config.FindDecoder(format);
return config.ImageFormatsManager.FindDecoder(format);
}
return null;

2
src/ImageSharp/Image/Image.FromStream.cs

@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageDecoders)
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}

10
src/ImageSharp/Image/ImageExtensions.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp
Guard.NotNullOrEmpty(filePath, nameof(filePath));
string ext = Path.GetExtension(filePath).Trim('.');
IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext);
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
@ -45,13 +45,13 @@ namespace SixLabors.ImageSharp
throw new NotSupportedException(stringBuilder.ToString());
}
IImageEncoder encoder = source.GetConfiguration().FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.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 source.GetConfiguration().ImageEncoders)
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
@ -93,14 +93,14 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(format, nameof(format));
IImageEncoder encoder = source.GetConfiguration().FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageEncoders)
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}

89
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -22,8 +22,10 @@ namespace SixLabors.ImageSharp.Tests
public Configuration DefaultConfiguration { get; private set; }
public ConfigurationTests()
{
this.DefaultConfiguration = Configuration.CreateDefaultInstance();
{
// the shallow copy of configuration should behave exactly like the default configuration,
// so by using the copy, we test both the default and the copy.
this.DefaultConfiguration = Configuration.CreateDefaultInstance().ShallowCopy();
this.ConfigurationEmpty = new Configuration();
}
@ -32,14 +34,7 @@ namespace SixLabors.ImageSharp.Tests
{
Assert.IsType<LocalFileSystem>(this.DefaultConfiguration.FileSystem);
Assert.IsType<LocalFileSystem>(this.ConfigurationEmpty.FileSystem);
}
[Fact]
public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded()
{
Assert.Equal(4, this.DefaultConfiguration.ImageEncoders.Count());
Assert.Equal(4, this.DefaultConfiguration.ImageDecoders.Count());
}
}
/// <summary>
/// Test that the default configuration is not null.
@ -78,80 +73,6 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(Configuration.Default.ParallelOptions.MaxDegreeOfParallelism == Environment.ProcessorCount);
}
[Fact]
public void AddImageFormatDetectorNullthrows()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.AddImageFormatDetector(null);
});
}
[Fact]
public void RegisterNullMimeTypeEncoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetEncoder(null, new Mock<IImageEncoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetEncoder(ImageFormats.Bmp, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetEncoder(null, null);
});
}
[Fact]
public void RegisterNullSetDecoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetDecoder(null, new Mock<IImageDecoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetDecoder(ImageFormats.Bmp, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultConfiguration.SetDecoder(null, null);
});
}
[Fact]
public void RegisterMimeTypeEncoderReplacesLast()
{
IImageEncoder encoder1 = new Mock<IImageEncoder>().Object;
this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1);
IImageEncoder found = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder1, found);
IImageEncoder encoder2 = new Mock<IImageEncoder>().Object;
this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2);
IImageEncoder found2 = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}
[Fact]
public void RegisterMimeTypeDecoderReplacesLast()
{
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object;
this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object;
this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
IImageDecoder found2 = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2);
}
[Fact]
public void ConstructorCallConfigureOnFormatProvider()
{

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

@ -0,0 +1,129 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Gif;
using Moq;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public class ImageFormatManagerTests
{
public ImageFormatManager FormatsManagerEmpty { get; private set; }
public ImageFormatManager DefaultFormatsManager { get; private set; }
public ImageFormatManagerTests()
{
this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager;
this.FormatsManagerEmpty = new ImageFormatManager();
}
[Fact]
public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded()
{
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<PngEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<BmpEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<JpegEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<GifEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<PngDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<JpegDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
}
[Fact]
public void AddImageFormatDetectorNullthrows()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.AddImageFormatDetector(null);
});
}
[Fact]
public void RegisterNullMimeTypeEncoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetEncoder(null, new Mock<IImageEncoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetEncoder(null, null);
});
}
[Fact]
public void RegisterNullSetDecoder()
{
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetDecoder(null, new Mock<IImageDecoder>().Object);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
this.DefaultFormatsManager.SetDecoder(null, null);
});
}
[Fact]
public void RegisterMimeTypeEncoderReplacesLast()
{
IImageEncoder encoder1 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1);
IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder1, found);
IImageEncoder encoder2 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2);
IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}
[Fact]
public void RegisterMimeTypeDecoderReplacesLast()
{
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2);
}
[Fact]
public void AddFormatCallsConfig()
{
var provider = new Mock<IConfigurationModule>();
var config = new Configuration();
config.Configure(provider.Object);
provider.Verify(x => x.Configure(config));
}
}
}

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

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
TestFormat.RegisterGlobalTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();

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

@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp.Tests
{
FileSystem = this.fileSystem.Object
};
this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object);
this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object);
TestFormat.RegisterGlobalTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();

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

@ -42,8 +42,8 @@ namespace SixLabors.ImageSharp.Tests
{
FileSystem = this.fileSystem.Object
};
config.AddImageFormatDetector(this.localMimeTypeDetector);
config.SetEncoder(this.localImageFormat.Object, this.encoder.Object);
config.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector);
config.ImageFormatsManager.SetEncoder(this.localImageFormat.Object, this.encoder.Object);
this.Image = new Image<Rgba32>(config, 1, 1);
}

6
tests/ImageSharp.Tests/TestFormat.cs

@ -116,9 +116,9 @@ namespace SixLabors.ImageSharp.Tests
public void Configure(Configuration host)
{
host.AddImageFormatDetector(new TestHeader(this));
host.SetEncoder(this, new TestEncoder(this));
host.SetDecoder(this, new TestDecoder(this));
host.ImageFormatsManager.AddImageFormatDetector(new TestHeader(this));
host.ImageFormatsManager.SetEncoder(this, new TestEncoder(this));
host.ImageFormatsManager.SetDecoder(this, new TestDecoder(this));
}
public struct DecodeOperation

12
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -21,20 +21,20 @@ namespace SixLabors.ImageSharp.Tests
internal static IImageDecoder GetReferenceDecoder(string filePath)
{
IImageFormat format = GetImageFormat(filePath);
return Configuration.FindDecoder(format);
return Configuration.ImageFormatsManager.FindDecoder(format);
}
internal static IImageEncoder GetReferenceEncoder(string filePath)
{
IImageFormat format = GetImageFormat(filePath);
return Configuration.FindEncoder(format);
return Configuration.ImageFormatsManager.FindEncoder(format);
}
internal static IImageFormat GetImageFormat(string filePath)
{
string extension = Path.GetExtension(filePath).ToLower();
if (extension[0] == '.') extension = extension.Substring(1);
IImageFormat format = Configuration.FindFormatByFileExtension(extension);
IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension);
return format;
}
@ -45,9 +45,9 @@ namespace SixLabors.ImageSharp.Tests
IImageEncoder encoder,
IImageFormatDetector detector)
{
cfg.SetDecoder(imageFormat, decoder);
cfg.SetEncoder(imageFormat, encoder);
cfg.AddImageFormatDetector(detector);
cfg.ImageFormatsManager.SetDecoder(imageFormat, decoder);
cfg.ImageFormatsManager.SetEncoder(imageFormat, encoder);
cfg.ImageFormatsManager.AddImageFormatDetector(detector);
}
private static Configuration CreateDefaultConfiguration()

2
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Tests
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path);
IImageFormat format = TestEnvironment.GetImageFormat(path);
IImageDecoder defaultDecoder = Configuration.Default.FindDecoder(format);
IImageDecoder defaultDecoder = Configuration.Default.ImageFormatsManager.FindDecoder(format);
//if (referenceDecoder.GetType() == defaultDecoder.GetType())
//{

Loading…
Cancel
Save