Browse Source

Much better type safety.

pull/693/head
James Jackson-South 8 years ago
parent
commit
25e5d48dca
  1. 18
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  2. 4
      src/ImageSharp/Formats/Bmp/BmpMetaData.cs
  3. 4
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  4. 21
      src/ImageSharp/Formats/Gif/GifFormat.cs
  5. 4
      src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
  6. 4
      src/ImageSharp/Formats/Gif/GifMetaData.cs
  7. 32
      src/ImageSharp/Formats/IImageFormat.cs
  8. 38
      src/ImageSharp/Formats/ImageFormatBase{T}.cs
  9. 18
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  10. 4
      src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
  11. 18
      src/ImageSharp/Formats/Png/PngFormat.cs
  12. 4
      src/ImageSharp/Formats/Png/PngMetaData.cs
  13. 13
      src/ImageSharp/MetaData/IImageFormatFrameMetaData.cs
  14. 13
      src/ImageSharp/MetaData/IImageFormatMetaData.cs
  15. 26
      src/ImageSharp/MetaData/ImageFrameMetaData.cs
  16. 22
      src/ImageSharp/MetaData/ImageMetaData.cs
  17. 8
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  18. 2
      tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs

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

@ -8,22 +8,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
internal sealed class BmpFormat : ImageFormatBase<BmpFormat>
internal sealed class BmpFormat : IImageFormat<BmpMetaData>
{
private BmpFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>
public static BmpFormat Instance { get; } = new BmpFormat();
/// <inheritdoc/>
public string Name => "BMP";
/// <inheritdoc/>
public override string Name => "BMP";
public string DefaultMimeType => "image/bmp";
/// <inheritdoc/>
public override string DefaultMimeType => "image/bmp";
public IEnumerable<string> MimeTypes => BmpConstants.MimeTypes;
/// <inheritdoc/>
public override IEnumerable<string> MimeTypes => BmpConstants.MimeTypes;
public IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
/// <inheritdoc/>
public override IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
public BmpMetaData CreateDefaultFormatMetaData() => new BmpMetaData();
}
}

4
src/ImageSharp/Formats/Bmp/BmpMetaData.cs

@ -1,14 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Provides Bmp specific metadata information for the image.
/// </summary>
public class BmpMetaData : IImageFormatMetaData
public class BmpMetaData
{
// TODO: Analyse what properties we would like to preserve.
}

4
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
GifFrameMetaData frameMetaData = frame.MetaData.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifFrameMetaData frameMetaData = frame.MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
this.WriteGraphicalControlExtension(frameMetaData, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
GifFrameMetaData previousMeta = null;
foreach (ImageFrame<TPixel> frame in image.Frames)
{
GifFrameMetaData meta = frame.MetaData.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifFrameMetaData meta = frame.MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.

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

@ -8,22 +8,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
internal sealed class GifFormat : ImageFormatBase<GifFormat>
internal sealed class GifFormat : IImageFormat<GifMetaData, GifFrameMetaData>
{
private GifFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>
public static GifFormat Instance { get; } = new GifFormat();
/// <inheritdoc/>
public string Name => "GIF";
/// <inheritdoc/>
public string DefaultMimeType => "image/gif";
/// <inheritdoc/>
public override string Name => "GIF";
public IEnumerable<string> MimeTypes => GifConstants.MimeTypes;
/// <inheritdoc/>
public override string DefaultMimeType => "image/gif";
public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
/// <inheritdoc/>
public override IEnumerable<string> MimeTypes => GifConstants.MimeTypes;
public GifMetaData CreateDefaultFormatMetaData() => new GifMetaData();
/// <inheritdoc/>
public override IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
public GifFrameMetaData CreateDefaultFormatFrameMetaData() => new GifFrameMetaData();
}
}

4
src/ImageSharp/Formats/Gif/GifFrameMetaData.cs

@ -1,14 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides Gif specific metadata information for the image frame.
/// </summary>
public class GifFrameMetaData : IImageFormatFrameMetaData
public class GifFrameMetaData
{
/// <summary>
/// Gets or sets the length of the color table for paletted images.

4
src/ImageSharp/Formats/Gif/GifMetaData.cs

@ -1,14 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides Gif specific metadata information for the image.
/// </summary>
public class GifMetaData : IImageFormatMetaData
public class GifMetaData
{
/// <summary>
/// Gets or sets the number of times any animation is repeated.

32
src/ImageSharp/Formats/IImageFormat.cs

@ -6,7 +6,37 @@ using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Describes an image format.
/// Defines the contract for an image format containing metadata with multiple frames.
/// </summary>
/// <typeparam name="TFormatMetaData">The type of format metadata.</typeparam>
/// <typeparam name="TFormatFrameMetaData">The type of format frame metadata.</typeparam>
public interface IImageFormat<TFormatMetaData, TFormatFrameMetaData> : IImageFormat<TFormatMetaData>
where TFormatMetaData : class
where TFormatFrameMetaData : class
{
/// <summary>
/// Creates a default instance of the format frame metadata.
/// </summary>
/// <returns>The <typeparamref name="TFormatFrameMetaData"/>.</returns>
TFormatFrameMetaData CreateDefaultFormatFrameMetaData();
}
/// <summary>
/// Defines the contract for an image format containing metadata.
/// </summary>
/// <typeparam name="TFormatMetaData">The type of format metadata.</typeparam>
public interface IImageFormat<TFormatMetaData> : IImageFormat
where TFormatMetaData : class
{
/// <summary>
/// Creates a default instance of the format metadata.
/// </summary>
/// <returns>The <typeparamref name="TFormatMetaData"/>.</returns>
TFormatMetaData CreateDefaultFormatMetaData();
}
/// <summary>
/// Defines the contract for an image format.
/// </summary>
public interface IImageFormat
{

38
src/ImageSharp/Formats/ImageFormatBase{T}.cs

@ -1,38 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// The base class for all image formats.
/// Inheriting classes should implement the singleton pattern by creating a private constructor.
/// </summary>
/// <typeparam name="T">The type of image format.</typeparam>
public abstract class ImageFormatBase<T> : IImageFormat
where T : class, IImageFormat
{
private static readonly Lazy<T> Lazy = new Lazy<T>(CreateInstance);
/// <summary>
/// Gets the current instance.
/// </summary>
public static T Instance => Lazy.Value;
/// <inheritdoc/>
public abstract string Name { get; }
/// <inheritdoc/>
public abstract string DefaultMimeType { get; }
/// <inheritdoc/>
public abstract IEnumerable<string> MimeTypes { get; }
/// <inheritdoc/>
public abstract IEnumerable<string> FileExtensions { get; }
private static T CreateInstance() => (T)Activator.CreateInstance(typeof(T), true);
}
}

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

@ -8,22 +8,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class JpegFormat : ImageFormatBase<JpegFormat>
internal sealed class JpegFormat : IImageFormat<JpegMetaData>
{
private JpegFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>
public static JpegFormat Instance { get; } = new JpegFormat();
/// <inheritdoc/>
public string Name => "JPEG";
/// <inheritdoc/>
public override string Name => "JPEG";
public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/>
public override string DefaultMimeType => "image/jpeg";
public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <inheritdoc/>
public override IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
/// <inheritdoc/>
public override IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
public JpegMetaData CreateDefaultFormatMetaData() => new JpegMetaData();
}
}

4
src/ImageSharp/Formats/Jpeg/JpegMetaData.cs

@ -1,14 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Provides Jpeg specific metadata information for the image.
/// </summary>
public class JpegMetaData : IImageFormatMetaData
public class JpegMetaData
{
// TODO: Analyse what properties we would like to preserve.
}

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

@ -8,22 +8,30 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
internal sealed class PngFormat : ImageFormatBase<PngFormat>
internal sealed class PngFormat : IImageFormat<PngMetaData>
{
private PngFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>
public static PngFormat Instance { get; } = new PngFormat();
/// <inheritdoc/>
public string Name => "PNG";
/// <inheritdoc/>
public override string Name => "PNG";
public string DefaultMimeType => "image/png";
/// <inheritdoc/>
public override string DefaultMimeType => "image/png";
public IEnumerable<string> MimeTypes => PngConstants.MimeTypes;
/// <inheritdoc/>
public override IEnumerable<string> MimeTypes => PngConstants.MimeTypes;
public IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
/// <inheritdoc/>
public override IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
public PngMetaData CreateDefaultFormatMetaData() => new PngMetaData();
}
}

4
src/ImageSharp/Formats/Png/PngMetaData.cs

@ -1,14 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Provides Png specific metadata information for the image.
/// </summary>
public class PngMetaData : IImageFormatMetaData
public class PngMetaData
{
// TODO: Analyse what properties we would like to preserve.
}

13
src/ImageSharp/MetaData/IImageFormatFrameMetaData.cs

@ -1,13 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.MetaData
{
/// <summary>
/// Encapsulates the format specific metadata of an image frame.
/// This interface exists to allow type saftey and avoid the performance overhead of parsing attributes.
/// </summary>
public interface IImageFormatFrameMetaData
{
}
}

13
src/ImageSharp/MetaData/IImageFormatMetaData.cs

@ -1,13 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.MetaData
{
/// <summary>
/// Encapsulates the format specific metadata of an image.
/// This interface exists to allow type saftey and avoid the performance overhead of parsing attributes.
/// </summary>
public interface IImageFormatMetaData
{
}
}

26
src/ImageSharp/MetaData/ImageFrameMetaData.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </summary>
public sealed class ImageFrameMetaData
{
private readonly Dictionary<IImageFormat, IImageFormatFrameMetaData> metaData = new Dictionary<IImageFormat, IImageFormatFrameMetaData>();
private readonly Dictionary<IImageFormat, object> metaData = new Dictionary<IImageFormat, object>();
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrameMetaData"/> class.
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.MetaData
{
DebugGuard.NotNull(other, nameof(other));
foreach (KeyValuePair<IImageFormat, IImageFormatFrameMetaData> meta in other.metaData)
foreach (KeyValuePair<IImageFormat, object> meta in other.metaData)
{
this.metaData.Add(meta.Key, meta.Value);
}
@ -47,12 +47,16 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary>
/// Adds or updates the specified key and value to the <see cref="ImageMetaData"/>.
/// </summary>
/// <typeparam name="TFormatMetaData">The type of format metadata.</typeparam>
/// <typeparam name="TFormatFrameMetaData">The type of format frame metadata.</typeparam>
/// <param name="key">The key of the metadata to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="ArgumentNullException">key is null.</exception>
/// <exception cref="ArgumentNullException">value is null.</exception>
/// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="ImageMetaData"/>.</exception>
public void AddOrUpdateFormatMetaData(IImageFormat key, IImageFormatFrameMetaData value)
public void AddOrUpdateFormatMetaData<TFormatMetaData, TFormatFrameMetaData>(IImageFormat<TFormatMetaData, TFormatFrameMetaData> key, TFormatFrameMetaData value)
where TFormatMetaData : class
where TFormatFrameMetaData : class
{
// Don't think this needs to be threadsafe.
Guard.NotNull(value, nameof(value));
@ -62,20 +66,22 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary>
/// Gets the metadata value associated with the specified key.
/// </summary>
/// <typeparam name="T">The type of metadata.</typeparam>
/// <typeparam name="TFormatMetaData">The type of format metadata.</typeparam>
/// <typeparam name="TFormatFrameMetaData">The type of format frame metadata.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <returns>
/// The <typeparamref name="T"/>.
/// The <typeparamref name="TFormatFrameMetaData"/>.
/// </returns>
public T GetOrAddFormatMetaData<T>(IImageFormat key)
where T : IImageFormatFrameMetaData, new()
public TFormatFrameMetaData GetOrAddFormatMetaData<TFormatMetaData, TFormatFrameMetaData>(IImageFormat<TFormatMetaData, TFormatFrameMetaData> key)
where TFormatMetaData : class
where TFormatFrameMetaData : class
{
if (this.metaData.TryGetValue(key, out IImageFormatFrameMetaData meta))
if (this.metaData.TryGetValue(key, out object meta))
{
return (T)meta;
return (TFormatFrameMetaData)meta;
}
var newMeta = new T();
TFormatFrameMetaData newMeta = key.CreateDefaultFormatFrameMetaData();
this.AddOrUpdateFormatMetaData(key, newMeta);
return newMeta;
}

22
src/ImageSharp/MetaData/ImageMetaData.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </summary>
public const double DefaultVerticalResolution = 96;
private readonly Dictionary<IImageFormat, IImageFormatMetaData> metaData = new Dictionary<IImageFormat, IImageFormatMetaData>();
private readonly Dictionary<IImageFormat, object> metaData = new Dictionary<IImageFormat, object>();
private double horizontalResolution;
private double verticalResolution;
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.MetaData
this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits;
foreach (KeyValuePair<IImageFormat, IImageFormatMetaData> meta in other.metaData)
foreach (KeyValuePair<IImageFormat, object> meta in other.metaData)
{
this.metaData.Add(meta.Key, meta.Value);
}
@ -134,12 +134,14 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary>
/// Adds or updates the specified key and value to the <see cref="ImageMetaData"/>.
/// </summary>
/// <typeparam name="TFormatMetaData">The type of format metadata.</typeparam>
/// <param name="key">The key of the metadata to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="ArgumentNullException">key is null.</exception>
/// <exception cref="ArgumentNullException">value is null.</exception>
/// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="ImageMetaData"/>.</exception>
public void AddOrUpdateFormatMetaData(IImageFormat key, IImageFormatMetaData value)
public void AddOrUpdateFormatMetaData<TFormatMetaData>(IImageFormat<TFormatMetaData> key, TFormatMetaData value)
where TFormatMetaData : class
{
// Don't think this needs to be threadsafe.
Guard.NotNull(value, nameof(value));
@ -149,20 +151,20 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary>
/// Gets the metadata value associated with the specified key.
/// </summary>
/// <typeparam name="T">The type of metadata.</typeparam>
/// <typeparam name="TFormatMetaData">The type of metadata.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <returns>
/// The <typeparamref name="T"/>.
/// The <typeparamref name="TFormatMetaData"/>.
/// </returns>
public T GetOrAddFormatMetaData<T>(IImageFormat key)
where T : IImageFormatMetaData, new()
public TFormatMetaData GetOrAddFormatMetaData<TFormatMetaData>(IImageFormat<TFormatMetaData> key)
where TFormatMetaData : class
{
if (this.metaData.TryGetValue(key, out IImageFormatMetaData meta))
if (this.metaData.TryGetValue(key, out object meta))
{
return (T)meta;
return (TFormatMetaData)meta;
}
var newMeta = new T();
TFormatMetaData newMeta = key.CreateDefaultFormatMetaData();
this.AddOrUpdateFormatMetaData(key, newMeta);
return newMeta;
}

8
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -189,8 +189,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
inStream.Position = 0;
var image = Image.Load(inStream);
GifMetaData metaData = image.MetaData.GetOrAddFormatMetaData<GifMetaData>(GifFormat.Instance);
GifFrameMetaData frameMetaData = image.Frames.RootFrame.MetaData.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifMetaData metaData = image.MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
GifFrameMetaData frameMetaData = image.Frames.RootFrame.MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
GifColorTableMode colorMode = metaData.ColorTableMode;
var encoder = new GifEncoder()
{
@ -212,8 +212,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
for (int i = 0; i < image.Frames.Count; i++)
{
GifFrameMetaData ifm = image.Frames[i].MetaData.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifFrameMetaData cifm = clone.Frames[i].MetaData.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifFrameMetaData ifm = image.Frames[i].MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
GifFrameMetaData cifm = clone.Frames[i].MetaData.GetOrAddFormatMetaData(GifFormat.Instance);
Assert.Equal(ifm.ColorTableLength, cifm.ColorTableLength);
Assert.Equal(ifm.FrameDelay, cifm.FrameDelay);

2
tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests
metaData.AddOrUpdateFormatMetaData(GifFormat.Instance, gifFrameMetaData);
var clone = new ImageFrameMetaData(metaData);
GifFrameMetaData cloneGifFrameMetaData = clone.GetOrAddFormatMetaData<GifFrameMetaData>(GifFormat.Instance);
GifFrameMetaData cloneGifFrameMetaData = clone.GetOrAddFormatMetaData(GifFormat.Instance);
Assert.Equal(frameDelay, cloneGifFrameMetaData.FrameDelay);
Assert.Equal(colorTableLength, cloneGifFrameMetaData.ColorTableLength);

Loading…
Cancel
Save