Browse Source

All metadata now performs a deep clone

af/merge-core
James Jackson-South 8 years ago
parent
commit
eb9e051da0
  1. 20
      src/ImageSharp/Formats/Bmp/BmpMetaData.cs
  2. 2
      src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
  3. 23
      src/ImageSharp/Formats/Gif/GifMetaData.cs
  4. 18
      src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
  5. 23
      src/ImageSharp/Formats/Png/PngMetaData.cs
  6. 4
      src/ImageSharp/ImageFrameCollection.cs
  7. 4
      src/ImageSharp/Image{TPixel}.cs
  8. 38
      src/ImageSharp/MetaData/ImageMetaData.cs
  9. 27
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  10. 69
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  11. 36
      src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
  12. 2
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  13. 2
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
  14. 2
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
  15. 2
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  16. 2
      tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs
  17. 28
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

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

@ -6,13 +6,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Provides Bmp specific metadata information for the image. /// Provides Bmp specific metadata information for the image.
/// </summary> /// </summary>
public class BmpMetaData public class BmpMetaData : IDeepCloneable
{ {
/// <summary>
/// Initializes a new instance of the <see cref="BmpMetaData"/> class.
/// </summary>
public BmpMetaData()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BmpMetaData"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private BmpMetaData(BmpMetaData other) => this.BitsPerPixel = other.BitsPerPixel;
/// <summary> /// <summary>
/// Gets or sets the number of bits per pixel. /// Gets or sets the number of bits per pixel.
/// </summary> /// </summary>
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new BmpMetaData(this);
// TODO: Colors used once we support encoding palette bmps. // TODO: Colors used once we support encoding palette bmps.
} }
} }

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the <see cref="GifFrameMetaData"/> class. /// Initializes a new instance of the <see cref="GifFrameMetaData"/> class.
/// </summary> /// </summary>
/// <param name="other">The metadata to create an instance from.</param> /// <param name="other">The metadata to create an instance from.</param>
internal GifFrameMetaData(GifFrameMetaData other) private GifFrameMetaData(GifFrameMetaData other)
{ {
this.ColorTableLength = other.ColorTableLength; this.ColorTableLength = other.ColorTableLength;
this.FrameDelay = other.FrameDelay; this.FrameDelay = other.FrameDelay;

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

@ -6,8 +6,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Provides Gif specific metadata information for the image. /// Provides Gif specific metadata information for the image.
/// </summary> /// </summary>
public class GifMetaData public class GifMetaData : IDeepCloneable
{ {
/// <summary>
/// Initializes a new instance of the <see cref="GifMetaData"/> class.
/// </summary>
public GifMetaData()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GifMetaData"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private GifMetaData(GifMetaData other)
{
this.RepeatCount = other.RepeatCount;
this.ColorTableMode = other.ColorTableMode;
this.GlobalColorTableLength = other.GlobalColorTableLength;
}
/// <summary> /// <summary>
/// Gets or sets the number of times any animation is repeated. /// Gets or sets the number of times any animation is repeated.
/// <remarks> /// <remarks>
@ -25,5 +43,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Gets or sets the length of the global color table if present. /// Gets or sets the length of the global color table if present.
/// </summary> /// </summary>
public int GlobalColorTableLength { get; set; } public int GlobalColorTableLength { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new GifMetaData(this);
} }
} }

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

@ -6,11 +6,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Provides Jpeg specific metadata information for the image. /// Provides Jpeg specific metadata information for the image.
/// </summary> /// </summary>
public class JpegMetaData public class JpegMetaData : IDeepCloneable
{ {
/// <summary>
/// Initializes a new instance of the <see cref="JpegMetaData"/> class.
/// </summary>
public JpegMetaData()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JpegMetaData"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private JpegMetaData(JpegMetaData other) => this.Quality = other.Quality;
/// <summary> /// <summary>
/// Gets or sets the encoded quality. /// Gets or sets the encoded quality.
/// </summary> /// </summary>
public int Quality { get; set; } = 75; public int Quality { get; set; } = 75;
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new JpegMetaData(this);
} }
} }

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

@ -6,8 +6,26 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Provides Png specific metadata information for the image. /// Provides Png specific metadata information for the image.
/// </summary> /// </summary>
public class PngMetaData public class PngMetaData : IDeepCloneable
{ {
/// <summary>
/// Initializes a new instance of the <see cref="PngMetaData"/> class.
/// </summary>
public PngMetaData()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PngMetaData"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private PngMetaData(PngMetaData other)
{
this.BitDepth = other.BitDepth;
this.ColorType = other.ColorType;
this.Gamma = other.Gamma;
}
/// <summary> /// <summary>
/// Gets or sets the number of bits per sample or per palette index (not per pixel). /// Gets or sets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType"/> values. /// Not all values are allowed for all <see cref="ColorType"/> values.
@ -23,5 +41,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Gets or sets the gamma value for the image. /// Gets or sets the gamma value for the image.
/// </summary> /// </summary>
public float Gamma { get; set; } public float Gamma { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new PngMetaData(this);
} }
} }

4
src/ImageSharp/ImageFrameCollection.cs

@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp
this.frames.Remove(frame); this.frames.Remove(frame);
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame }); return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { frame });
} }
/// <summary> /// <summary>
@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp
{ {
ImageFrame<TPixel> frame = this[index]; ImageFrame<TPixel> frame = this[index];
ImageFrame<TPixel> clonedFrame = frame.Clone(); ImageFrame<TPixel> clonedFrame = frame.Clone();
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame }); return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { clonedFrame });
} }
/// <summary> /// <summary>

4
src/ImageSharp/Image{TPixel}.cs

@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp
public Image<TPixel> Clone() public Image<TPixel> Clone()
{ {
IEnumerable<ImageFrame<TPixel>> clonedFrames = this.frames.Select(x => x.Clone()); IEnumerable<ImageFrame<TPixel>> clonedFrames = this.frames.Select(x => x.Clone());
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), clonedFrames); return new Image<TPixel>(this.configuration, this.MetaData.DeepClone(), clonedFrames);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp
where TPixel2 : struct, IPixel<TPixel2> where TPixel2 : struct, IPixel<TPixel2>
{ {
IEnumerable<ImageFrame<TPixel2>> clonedFrames = this.frames.Select(x => x.CloneAs<TPixel2>()); IEnumerable<ImageFrame<TPixel2>> clonedFrames = this.frames.Select(x => x.CloneAs<TPixel2>());
var target = new Image<TPixel2>(this.configuration, this.MetaData.Clone(), clonedFrames); var target = new Image<TPixel2>(this.configuration, this.MetaData.DeepClone(), clonedFrames);
return target; return target;
} }

38
src/ImageSharp/MetaData/ImageMetaData.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary> /// <summary>
/// Encapsulates the metadata of an image. /// Encapsulates the metadata of an image.
/// </summary> /// </summary>
public sealed class ImageMetaData public sealed class ImageMetaData : IDeepCloneable<ImageMetaData>
{ {
/// <summary> /// <summary>
/// The default horizontal resolution value (dots per inch) in x direction. /// The default horizontal resolution value (dots per inch) in x direction.
@ -26,7 +25,13 @@ namespace SixLabors.ImageSharp.MetaData
/// </summary> /// </summary>
public const double DefaultVerticalResolution = 96; public const double DefaultVerticalResolution = 96;
private readonly Dictionary<IImageFormat, object> formatMetaData = new Dictionary<IImageFormat, object>(); /// <summary>
/// The default pixel resolution units.
/// <remarks>The default value is <see cref="PixelResolutionUnit.PixelsPerInch"/>.</remarks>
/// </summary>
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch;
private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetaData = new Dictionary<IImageFormat, IDeepCloneable>();
private double horizontalResolution; private double horizontalResolution;
private double verticalResolution; private double verticalResolution;
@ -37,6 +42,7 @@ namespace SixLabors.ImageSharp.MetaData
{ {
this.horizontalResolution = DefaultHorizontalResolution; this.horizontalResolution = DefaultHorizontalResolution;
this.verticalResolution = DefaultVerticalResolution; this.verticalResolution = DefaultVerticalResolution;
this.ResolutionUnits = DefaultPixelResolutionUnits;
} }
/// <summary> /// <summary>
@ -52,9 +58,9 @@ namespace SixLabors.ImageSharp.MetaData
this.VerticalResolution = other.VerticalResolution; this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits; this.ResolutionUnits = other.ResolutionUnits;
foreach (KeyValuePair<IImageFormat, object> meta in other.formatMetaData) foreach (KeyValuePair<IImageFormat, IDeepCloneable> meta in other.formatMetaData)
{ {
this.formatMetaData.Add(meta.Key, meta.Value); this.formatMetaData.Add(meta.Key, meta.Value.DeepClone());
} }
foreach (ImageProperty property in other.Properties) foreach (ImageProperty property in other.Properties)
@ -62,13 +68,8 @@ namespace SixLabors.ImageSharp.MetaData
this.Properties.Add(property); this.Properties.Add(property);
} }
this.ExifProfile = other.ExifProfile != null this.ExifProfile = other.ExifProfile?.DeepClone();
? new ExifProfile(other.ExifProfile) this.IccProfile = other.IccProfile?.DeepClone();
: null;
this.IccProfile = other.IccProfile != null
? new IccProfile(other.IccProfile)
: null;
} }
/// <summary> /// <summary>
@ -114,7 +115,7 @@ namespace SixLabors.ImageSharp.MetaData
/// 02 : Pixels per centimeter /// 02 : Pixels per centimeter
/// 03 : Pixels per meter /// 03 : Pixels per meter
/// </summary> /// </summary>
public PixelResolutionUnit ResolutionUnits { get; set; } = PixelResolutionUnit.PixelsPerInch; public PixelResolutionUnit ResolutionUnits { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Exif profile. /// Gets or sets the Exif profile.
@ -140,9 +141,9 @@ namespace SixLabors.ImageSharp.MetaData
/// The <typeparamref name="TFormatMetaData"/>. /// The <typeparamref name="TFormatMetaData"/>.
/// </returns> /// </returns>
public TFormatMetaData GetFormatMetaData<TFormatMetaData>(IImageFormat<TFormatMetaData> key) public TFormatMetaData GetFormatMetaData<TFormatMetaData>(IImageFormat<TFormatMetaData> key)
where TFormatMetaData : class where TFormatMetaData : class, IDeepCloneable
{ {
if (this.formatMetaData.TryGetValue(key, out object meta)) if (this.formatMetaData.TryGetValue(key, out IDeepCloneable meta))
{ {
return (TFormatMetaData)meta; return (TFormatMetaData)meta;
} }
@ -152,11 +153,8 @@ namespace SixLabors.ImageSharp.MetaData
return newMeta; return newMeta;
} }
/// <summary> /// <inheritdoc/>
/// Clones this into a new instance public ImageMetaData DeepClone() => new ImageMetaData(this);
/// </summary>
/// <returns>The cloned metadata instance</returns>
public ImageMetaData Clone() => new ImageMetaData(this);
/// <summary> /// <summary>
/// Looks up a property with the provided name. /// Looks up a property with the provided name.

27
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -12,23 +12,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Represents an EXIF profile providing access to the collection of values. /// Represents an EXIF profile providing access to the collection of values.
/// </summary> /// </summary>
public sealed class ExifProfile public sealed class ExifProfile : IDeepCloneable<ExifProfile>
{ {
/// <summary> /// <summary>
/// The byte array to read the EXIF profile from. /// The byte array to read the EXIF profile from.
/// </summary> /// </summary>
private byte[] data; private readonly byte[] data;
/// <summary> /// <summary>
/// The collection of EXIF values /// The collection of EXIF values
/// </summary> /// </summary>
private List<ExifValue> values; private List<ExifValue> values;
/// <summary>
/// The list of invalid EXIF tags
/// </summary>
private IReadOnlyList<ExifTag> invalidTags;
/// <summary> /// <summary>
/// The thumbnail offset position in the byte stream /// The thumbnail offset position in the byte stream
/// </summary> /// </summary>
@ -55,7 +50,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
this.Parts = ExifParts.All; this.Parts = ExifParts.All;
this.data = data; this.data = data;
this.invalidTags = new List<ExifTag>(); this.InvalidTags = new List<ExifTag>();
} }
/// <summary> /// <summary>
@ -63,22 +58,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// by making a copy from another EXIF profile. /// by making a copy from another EXIF profile.
/// </summary> /// </summary>
/// <param name="other">The other EXIF profile, where the clone should be made from.</param> /// <param name="other">The other EXIF profile, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception> private ExifProfile(ExifProfile other)
public ExifProfile(ExifProfile other)
{ {
Guard.NotNull(other, nameof(other));
this.Parts = other.Parts; this.Parts = other.Parts;
this.thumbnailLength = other.thumbnailLength; this.thumbnailLength = other.thumbnailLength;
this.thumbnailOffset = other.thumbnailOffset; this.thumbnailOffset = other.thumbnailOffset;
this.invalidTags = new List<ExifTag>(other.invalidTags); this.InvalidTags = new List<ExifTag>(other.InvalidTags);
if (other.values != null) if (other.values != null)
{ {
this.values = new List<ExifValue>(other.Values.Count); this.values = new List<ExifValue>(other.Values.Count);
foreach (ExifValue value in other.Values) foreach (ExifValue value in other.Values)
{ {
this.values.Add(new ExifValue(value)); this.values.Add(value.DeepClone());
} }
} }
@ -97,7 +89,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Gets the tags that where found but contained an invalid value. /// Gets the tags that where found but contained an invalid value.
/// </summary> /// </summary>
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags; public IReadOnlyList<ExifTag> InvalidTags { get; private set; }
/// <summary> /// <summary>
/// Gets the values of this EXIF profile. /// Gets the values of this EXIF profile.
@ -249,6 +241,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return writer.GetData(); return writer.GetData();
} }
/// <inheritdoc/>
public ExifProfile DeepClone() => new ExifProfile(this);
/// <summary> /// <summary>
/// Synchronizes the profiles with the specified meta data. /// Synchronizes the profiles with the specified meta data.
/// </summary> /// </summary>
@ -294,7 +289,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.values = reader.ReadValues(); this.values = reader.ReadValues();
this.invalidTags = new List<ExifTag>(reader.InvalidTags); this.InvalidTags = new List<ExifTag>(reader.InvalidTags);
this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength; this.thumbnailLength = (int)reader.ThumbnailLength;
} }

69
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -11,15 +11,30 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Represent the value of the EXIF profile. /// Represent the value of the EXIF profile.
/// </summary> /// </summary>
public sealed class ExifValue : IEquatable<ExifValue> public sealed class ExifValue : IEquatable<ExifValue>, IDeepCloneable<ExifValue>
{ {
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray && dataType != ExifDataType.Ascii;
this.Value = value;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class /// Initializes a new instance of the <see cref="ExifValue"/> class
/// by making a copy from another exif value. /// by making a copy from another exif value.
/// </summary> /// </summary>
/// <param name="other">The other exif value, where the clone should be made from.</param> /// <param name="other">The other exif value, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>
public ExifValue(ExifValue other) private ExifValue(ExifValue other)
{ {
Guard.NotNull(other, nameof(other)); Guard.NotNull(other, nameof(other));
@ -29,30 +44,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (!other.IsArray) if (!other.IsArray)
{ {
// All types are value types except for string which is immutable so safe to simply assign.
this.Value = other.Value; this.Value = other.Value;
} }
else else
{ {
// All array types are value types so Clone() is sufficient here.
var array = (Array)other.Value; var array = (Array)other.Value;
this.Value = array.Clone(); this.Value = array.Clone();
} }
} }
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray && dataType != ExifDataType.Ascii;
this.Value = value;
}
/// <summary> /// <summary>
/// Gets the data type of the exif value. /// Gets the data type of the exif value.
/// </summary> /// </summary>
@ -145,10 +147,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns> /// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator ==(ExifValue left, ExifValue right) public static bool operator ==(ExifValue left, ExifValue right) => ReferenceEquals(left, right) || left.Equals(right);
{
return ReferenceEquals(left, right) || left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="ExifValue"/> objects for equality. /// Compares two <see cref="ExifValue"/> objects for equality.
@ -162,16 +161,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns> /// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator !=(ExifValue left, ExifValue right) public static bool operator !=(ExifValue left, ExifValue right) => !(left == right);
{
return !(left == right);
}
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj) => obj is ExifValue other && this.Equals(other);
{
return obj is ExifValue other && this.Equals(other);
}
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(ExifValue other) public bool Equals(ExifValue other)
@ -187,9 +180,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
return return
this.Tag == other.Tag && this.Tag == other.Tag
this.DataType == other.DataType && && this.DataType == other.DataType
object.Equals(this.Value, other.Value); && object.Equals(this.Value, other.Value);
} }
/// <summary> /// <summary>
@ -205,10 +198,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode() => this.GetHashCode(this);
{
return this.GetHashCode(this);
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
@ -238,6 +228,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return sb.ToString(); return sb.ToString();
} }
/// <inheritdoc/>
public ExifValue DeepClone() => new ExifValue(this);
/// <summary> /// <summary>
/// Creates a new <see cref="ExifValue"/> /// Creates a new <see cref="ExifValue"/>
/// </summary> /// </summary>
@ -584,7 +577,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray) private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
{ {
Type type = value?.GetType(); Type type = value?.GetType();
if (type != null && type.IsArray) if (type?.IsArray == true)
{ {
type = type.GetElementType(); type = type.GetElementType();
} }

36
src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary> /// <summary>
/// Represents an ICC profile /// Represents an ICC profile
/// </summary> /// </summary>
public sealed class IccProfile public sealed class IccProfile : IDeepCloneable<IccProfile>
{ {
/// <summary> /// <summary>
/// The byte array to read the ICC profile from /// The byte array to read the ICC profile from
@ -42,23 +42,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// Initializes a new instance of the <see cref="IccProfile"/> class. /// Initializes a new instance of the <see cref="IccProfile"/> class.
/// </summary> /// </summary>
/// <param name="data">The raw ICC profile data</param> /// <param name="data">The raw ICC profile data</param>
public IccProfile(byte[] data) public IccProfile(byte[] data) => this.data = data;
{
this.data = data;
}
/// <summary>
/// Initializes a new instance of the <see cref="IccProfile"/> class
/// by making a copy from another ICC profile.
/// </summary>
/// <param name="other">The other ICC profile, where the clone should be made from.</param>
/// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>>
public IccProfile(IccProfile other)
{
Guard.NotNull(other, nameof(other));
this.data = other.ToByteArray();
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="IccProfile"/> class. /// Initializes a new instance of the <see cref="IccProfile"/> class.
@ -74,6 +58,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
this.entries = new List<IccTagDataEntry>(entries); this.entries = new List<IccTagDataEntry>(entries);
} }
/// <summary>
/// Initializes a new instance of the <see cref="IccProfile"/> class
/// by making a copy from another ICC profile.
/// </summary>
/// <param name="other">The other ICC profile, where the clone should be made from.</param>
/// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>>
private IccProfile(IccProfile other)
{
Guard.NotNull(other, nameof(other));
this.data = other.ToByteArray();
}
/// <summary> /// <summary>
/// Gets or sets the profile header /// Gets or sets the profile header
/// </summary> /// </summary>
@ -100,6 +97,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
} }
} }
/// <inheritdoc/>
public IccProfile DeepClone() => new IccProfile(this);
#if !NETSTANDARD1_1 #if !NETSTANDARD1_1
/// <summary> /// <summary>

2
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs

@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.MetaData.DeepClone())); source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.MetaData.DeepClone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.DeepClone(), frames);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs

@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.MetaData.DeepClone())); IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.MetaData.DeepClone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.DeepClone(), frames);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs

@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.DeepClone())); source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.DeepClone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.DeepClone(), frames);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -218,7 +218,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.Width, this.Height, x.MetaData.DeepClone())); IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.Width, this.Height, x.MetaData.DeepClone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.DeepClone(), frames);
} }
/// <inheritdoc/> /// <inheritdoc/>

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

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests
metaData.VerticalResolution = 2; metaData.VerticalResolution = 2;
metaData.Properties.Add(imageProperty); metaData.Properties.Add(imageProperty);
ImageMetaData clone = metaData.Clone(); ImageMetaData clone = metaData.DeepClone();
Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray());
Assert.Equal(4, clone.HorizontalResolution); Assert.Equal(4, clone.HorizontalResolution);

28
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

@ -67,16 +67,16 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void ConstructorCopy() public void ConstructorCopy()
{ {
Assert.Throws<ArgumentNullException>(() => { new ExifProfile((ExifProfile)null); }); Assert.Throws<NullReferenceException>(() => ((ExifProfile)null).DeepClone());
ExifProfile profile = GetExifProfile(); ExifProfile profile = GetExifProfile();
var clone = new ExifProfile(profile); ExifProfile clone = profile.DeepClone();
TestProfile(clone); TestProfile(clone);
profile.SetValue(ExifTag.ColorSpace, (ushort)2); profile.SetValue(ExifTag.ColorSpace, (ushort)2);
clone = new ExifProfile(profile); clone = profile.DeepClone();
TestProfile(clone); TestProfile(clone);
} }
@ -234,10 +234,12 @@ namespace SixLabors.ImageSharp.Tests
exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.XResolution, new Rational(200));
exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300));
var metaData = new ImageMetaData(); var metaData = new ImageMetaData
metaData.ExifProfile = exifProfile; {
metaData.HorizontalResolution = 200; ExifProfile = exifProfile,
metaData.VerticalResolution = 300; HorizontalResolution = 200,
VerticalResolution = 300
};
metaData.HorizontalResolution = 100; metaData.HorizontalResolution = 100;
@ -355,11 +357,11 @@ namespace SixLabors.ImageSharp.Tests
// act // act
Image<Rgba32> reloadedImage = WriteAndRead(image, imageFormat); Image<Rgba32> reloadedImage = WriteAndRead(image, imageFormat);
// assert // assert
ExifProfile actual = reloadedImage.MetaData.ExifProfile; ExifProfile actual = reloadedImage.MetaData.ExifProfile;
Assert.NotNull(actual); Assert.NotNull(actual);
foreach(KeyValuePair<ExifTag, object> expectedProfileValue in TestProfileValues) foreach (KeyValuePair<ExifTag, object> expectedProfileValue in TestProfileValues)
{ {
ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key); ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key);
Assert.NotNull(actualProfileValue); Assert.NotNull(actualProfileValue);
@ -371,7 +373,7 @@ namespace SixLabors.ImageSharp.Tests
public void ProfileToByteArray() public void ProfileToByteArray()
{ {
// arrange // arrange
byte[] exifBytesWithExifCode = ProfileResolver.ExifMarker.Concat(ExifConstants.LittleEndianByteOrderMarker).ToArray(); byte[] exifBytesWithExifCode = ProfileResolver.ExifMarker.Concat(ExifConstants.LittleEndianByteOrderMarker).ToArray();
byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker; byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
ExifProfile expectedProfile = CreateExifProfile(); ExifProfile expectedProfile = CreateExifProfile();
var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
@ -384,7 +386,7 @@ namespace SixLabors.ImageSharp.Tests
Assert.NotNull(actualBytes); Assert.NotNull(actualBytes);
Assert.NotEmpty(actualBytes); Assert.NotEmpty(actualBytes);
Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray()); Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
foreach(ExifTag expectedProfileTag in expectedProfileTags) foreach (ExifTag expectedProfileTag in expectedProfileTags)
{ {
ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag); ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag);
ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag); ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag);
@ -396,7 +398,7 @@ namespace SixLabors.ImageSharp.Tests
{ {
var profile = new ExifProfile(); var profile = new ExifProfile();
foreach(KeyValuePair<ExifTag, object> exifProfileValue in TestProfileValues) foreach (KeyValuePair<ExifTag, object> exifProfileValue in TestProfileValues)
{ {
profile.SetValue(exifProfileValue.Key, exifProfileValue.Value); profile.SetValue(exifProfileValue.Key, exifProfileValue.Value);
} }
@ -416,7 +418,7 @@ namespace SixLabors.ImageSharp.Tests
private static Image<Rgba32> WriteAndRead(Image<Rgba32> image, TestImageWriteFormat imageFormat) private static Image<Rgba32> WriteAndRead(Image<Rgba32> image, TestImageWriteFormat imageFormat)
{ {
switch(imageFormat) switch (imageFormat)
{ {
case TestImageWriteFormat.Jpeg: case TestImageWriteFormat.Jpeg:
return WriteAndReadJpeg(image); return WriteAndReadJpeg(image);

Loading…
Cancel
Save