diff --git a/src/ImageProcessorCore/Numerics/Rational.cs b/src/ImageProcessorCore/Numerics/Rational.cs index fd578dc7c..0b66d8367 100644 --- a/src/ImageProcessorCore/Numerics/Rational.cs +++ b/src/ImageProcessorCore/Numerics/Rational.cs @@ -8,27 +8,25 @@ namespace ImageProcessorCore using System; using System.Globalization; using System.Numerics; - using System.Runtime.InteropServices; using System.Text; /// /// Represents a number that can be expressed as a fraction /// /// - /// This is a very simplified implimentation of a rational number designed for use with - /// metadata only. + /// This is a very simplified implimentation of a rational number designed for use with metadata only. /// public struct Rational : IEquatable { /// - /// Represents a rational object that is not a number. + /// Represents a rational object that is not a number. NaN /// - public static Rational Indeterminate = new Rational(0, 0); + public static Rational Indeterminate = new Rational(3, 0); /// /// Represents a rational object that is equal to 0. /// - public static Rational Zero = new Rational(0, 1); + public static Rational Zero = new Rational(0, 0); /// /// Represents a rational object that is equal to 1. @@ -127,17 +125,20 @@ namespace ImageProcessorCore this = Indeterminate; return; } - else if (double.IsPositiveInfinity(value)) + + if (double.IsPositiveInfinity(value)) { this = PositiveInfinity; return; } - else if (double.IsNegativeInfinity(value)) + + if (double.IsNegativeInfinity(value)) { this = NegativeInfinity; return; } + // TODO: Not happy with parsing a string like this. I should be able to use maths but maths is HARD! this = Parse(value.ToString("R", CultureInfo.InvariantCulture)); } @@ -154,32 +155,87 @@ namespace ImageProcessorCore /// /// Gets a value indicating whether this instance is indeterminate. /// - public bool IsIndeterminate => (this.Equals(Indeterminate)); + public bool IsIndeterminate + { + get + { + if (this.Denominator != 0) + { + return false; + } + + return this.Numerator == 3; + } + } /// /// Gets a value indicating whether this instance is an integer. /// - public bool IsInteger => (this.Denominator == 1); + public bool IsInteger => this.Denominator == 1; /// /// Gets a value indicating whether this instance is equal to 0 /// - public bool IsZero => (this.Equals(Zero)); + public bool IsZero + { + get + { + if (this.Denominator != 0) + { + return false; + } + + return this.Numerator == 0; + } + } /// /// Gets a value indicating whether this instance is equal to 1. /// - public bool IsOne => (this.Equals(One)); + public bool IsOne + { + get + { + if (this.Denominator != 1) + { + return false; + } + + return this.Numerator == 1; + } + } /// /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0). /// - public bool IsNegativeInfinity => (this.Equals(NegativeInfinity)); + public bool IsNegativeInfinity + { + get + { + if (this.Denominator != 0) + { + return false; + } + + return this.Numerator == -1; + } + } /// - /// Gets a value indicating whether this instance is equal to positive infinity (1, 0). + /// Gets a value indicating whether this instance is equal to positive infinity (+1, 0). /// - public bool IsPositiveInfinity => (this.Equals(PositiveInfinity)); + public bool IsPositiveInfinity + { + get + { + if (this.Denominator != 0) + { + return false; + } + + return this.Numerator == 1; + } + } /// /// Converts a rational number to the nearest double. @@ -226,31 +282,31 @@ namespace ImageProcessorCore /// public bool Equals(Rational other) { + // Standard: a/b = c/d if (this.Denominator == other.Denominator) { return this.Numerator == other.Numerator; } - else if (this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero) - { - return other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero; - } - else if (other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero) + + // Indeterminate + if (this.Numerator == 3 && this.Denominator == 0) { - return this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero; + return other.Numerator == 3 && other.Denominator == 0; } - else + + if (other.Numerator == 3 && other.Denominator == 0) { - return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator); + return this.Numerator == 3 && this.Denominator == 0; } + + // ad = bc + return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator); } /// public override int GetHashCode() { - unchecked - { - return ((int)this.Numerator * 397) ^ (int)this.Denominator; - } + return this.GetHashCode(this); } /// @@ -268,7 +324,6 @@ namespace ImageProcessorCore /// /// public string ToString(IFormatProvider provider) - { if (this.IsIndeterminate) { @@ -285,6 +340,11 @@ namespace ImageProcessorCore return "[ NegativeInfinity ]"; } + if (this.IsZero) + { + return "[ Zero ]"; + } + if (this.IsInteger) { return this.Numerator.ToString(provider); @@ -322,16 +382,16 @@ namespace ImageProcessorCore return; } - if (this.Numerator == BigInteger.Zero) + if (this.Numerator == 0) { - Denominator = BigInteger.One; + this.Denominator = 1; return; } if (this.Numerator == this.Denominator) { - this.Numerator = BigInteger.One; - this.Denominator = BigInteger.One; + this.Numerator = 1; + this.Denominator = 1; return; } @@ -350,36 +410,36 @@ namespace ImageProcessorCore /// The internal static Rational Parse(string value) { - int periodIndex = value.IndexOf("."); - int eIndeix = value.IndexOf("E"); - int slashIndex = value.IndexOf("/"); + int periodIndex = value.IndexOf(".", StringComparison.Ordinal); + int eIndex = value.IndexOf("E", StringComparison.Ordinal); + int slashIndex = value.IndexOf("/", StringComparison.Ordinal); // An integer such as 7 - if (periodIndex == -1 && eIndeix == -1 && slashIndex == -1) + if (periodIndex == -1 && eIndex == -1 && slashIndex == -1) { return new Rational(BigInteger.Parse(value)); } // A fraction such as 3/7 - if (periodIndex == -1 && eIndeix == -1 && slashIndex != -1) + if (periodIndex == -1 && eIndex == -1 && slashIndex != -1) { return new Rational(BigInteger.Parse(value.Substring(0, slashIndex)), BigInteger.Parse(value.Substring(slashIndex + 1))); } // No scientific Notation such as 5.997 - if (eIndeix == -1) + if (eIndex == -1) { - BigInteger n = BigInteger.Parse(value.Replace(".", "")); + BigInteger n = BigInteger.Parse(value.Replace(".", string.Empty)); BigInteger d = (BigInteger)Math.Pow(10, value.Length - periodIndex - 1); return new Rational(n, d); } // Scientific notation such as 2.4556E-2 - int characteristic = int.Parse(value.Substring(eIndeix + 1)); + int characteristic = int.Parse(value.Substring(eIndex + 1)); BigInteger ten = 10; - BigInteger numerator = BigInteger.Parse(value.Substring(0, eIndeix).Replace(".", "")); - BigInteger denominator = new BigInteger(Math.Pow(10, eIndeix - periodIndex - 1)); + BigInteger numerator = BigInteger.Parse(value.Substring(0, eIndex).Replace(".", string.Empty)); + BigInteger denominator = new BigInteger(Math.Pow(10, eIndex - periodIndex - 1)); BigInteger charPower = BigInteger.Pow(ten, Math.Abs(characteristic)); if (characteristic > 0) @@ -393,5 +453,19 @@ namespace ImageProcessorCore return new Rational(numerator, denominator); } + + /// + /// Returns the hash code for this instance. + /// + /// + /// The instance of to return the hash code for. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + private int GetHashCode(Rational rational) + { + return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode(); + } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifDataType.cs b/src/ImageProcessorCore/Profiles/Exif/ExifDataType.cs index 517b76011..15a66bebe 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifDataType.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifDataType.cs @@ -5,9 +5,9 @@ namespace ImageProcessorCore { - /// + /// /// Specifies exif data types. - /// + /// public enum ExifDataType { /// diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifParts.cs b/src/ImageProcessorCore/Profiles/Exif/ExifParts.cs index 3ad53430b..0684904f0 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifParts.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifParts.cs @@ -7,13 +7,12 @@ namespace ImageProcessorCore { using System; - /// + /// /// Specifies which parts will be written when the profile is added to an image. - /// + /// [Flags] public enum ExifParts { - /// /// None /// diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs index 5db095f09..86b90d76f 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs @@ -2,9 +2,9 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageProcessorCore { + using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; @@ -54,7 +54,6 @@ namespace ImageProcessorCore public ExifProfile(byte[] data) { this.Parts = ExifParts.All; - this.BestPrecision = false; this.data = data; this.invalidTags = new List(); } @@ -70,8 +69,6 @@ namespace ImageProcessorCore Guard.NotNull(other, nameof(other)); this.Parts = other.Parts; - this.BestPrecision = other.BestPrecision; - this.thumbnailLength = other.thumbnailLength; this.thumbnailOffset = other.thumbnailOffset; this.invalidTags = new List(other.invalidTags); @@ -89,17 +86,6 @@ namespace ImageProcessorCore } } - /// - /// Gets or sets a value indicating whether rational numbers should be stored with the best - /// precision possible. This is disabled by default, setting this to true will have an - /// impact on the performance. - /// - public bool BestPrecision - { - get; - set; - } - /// /// Gets or sets which parts will be written when the profile is added to an image. /// @@ -121,7 +107,7 @@ namespace ImageProcessorCore { get { - InitializeValues(); + this.InitializeValues(); return this.values; } } @@ -135,7 +121,7 @@ namespace ImageProcessorCore where T : IPackedVector where TP : struct { - InitializeValues(); + this.InitializeValues(); if (this.thumbnailOffset == 0 || this.thumbnailLength == 0) { @@ -159,7 +145,7 @@ namespace ImageProcessorCore /// The tag of the EXIF value. public ExifValue GetValue(ExifTag tag) { - foreach (ExifValue exifValue in Values) + foreach (ExifValue exifValue in this.Values) { if (exifValue.Tag == tag) return exifValue; @@ -174,7 +160,7 @@ namespace ImageProcessorCore /// The tag of the EXIF value. public bool RemoveValue(ExifTag tag) { - InitializeValues(); + this.InitializeValues(); for (int i = 0; i < this.values.Count; i++) { @@ -224,7 +210,7 @@ namespace ImageProcessorCore return null; } - ExifWriter writer = new ExifWriter(this.values, this.Parts, this.BestPrecision); + ExifWriter writer = new ExifWriter(this.values, this.Parts); return writer.GetData(); } diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs index 1ebb9c9e9..333a00d0a 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs @@ -2,7 +2,6 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageProcessorCore { using System; @@ -118,11 +117,11 @@ namespace ImageProcessorCore private void AddValues(Collection values, uint index) { this.currentIndex = this.startIndex + index; - ushort count = GetShort(); + ushort count = this.GetShort(); for (ushort i = 0; i < count; i++) { - ExifValue value = CreateValue(); + ExifValue value = this.CreateValue(); if (value == null) { continue; @@ -197,7 +196,7 @@ namespace ImageProcessorCore return this.ToLong(data); } - return ToArray(dataType, data, ToLong); + return ToArray(dataType, data, this.ToLong); case ExifDataType.Rational: if (numberOfComponents == 1) { @@ -243,10 +242,10 @@ namespace ImageProcessorCore case ExifDataType.SingleFloat: if (numberOfComponents == 1) { - return ToSingle(data); + return this.ToSingle(data); } - return ToArray(dataType, data, ToSingle); + return ToArray(dataType, data, this.ToSingle); case ExifDataType.Undefined: if (numberOfComponents == 1) { @@ -261,13 +260,13 @@ namespace ImageProcessorCore private ExifValue CreateValue() { - if (RemainingLength < 12) + if (this.RemainingLength < 12) { return null; } - ExifTag tag = ToEnum(this.GetShort(), ExifTag.Unknown); - ExifDataType dataType = ToEnum(this.GetShort(), ExifDataType.Unknown); + ExifTag tag = this.ToEnum(this.GetShort(), ExifTag.Unknown); + ExifDataType dataType = this.ToEnum(this.GetShort(), ExifDataType.Unknown); object value; if (dataType == ExifDataType.Unknown) @@ -278,25 +277,25 @@ namespace ImageProcessorCore uint numberOfComponents = this.GetLong(); uint size = numberOfComponents * ExifValue.GetSize(dataType); - byte[] data = GetBytes(4); + byte[] data = this.GetBytes(4); if (size > 4) { uint oldIndex = this.currentIndex; - this.currentIndex = ToLong(data) + this.startIndex; - if (RemainingLength < size) + this.currentIndex = this.ToLong(data) + this.startIndex; + if (this.RemainingLength < size) { this.invalidTags.Add(tag); this.currentIndex = oldIndex; return null; } - value = ConvertValue(dataType, this.GetBytes(size), numberOfComponents); + value = this.ConvertValue(dataType, this.GetBytes(size), numberOfComponents); this.currentIndex = oldIndex; } else { - value = ConvertValue(dataType, data, numberOfComponents); + value = this.ConvertValue(dataType, data, numberOfComponents); } bool isArray = value != null && numberOfComponents > 1; @@ -331,38 +330,38 @@ namespace ImageProcessorCore private uint GetLong() { - return ToLong(GetBytes(4)); + return this.ToLong(this.GetBytes(4)); } private ushort GetShort() { - return ToShort(GetBytes(2)); + return this.ToShort(this.GetBytes(2)); } private string GetString(uint length) { - return ToString(GetBytes(length)); + return ToString(this.GetBytes(length)); } private void GetThumbnail(uint offset) { Collection values = new Collection(); - AddValues(values, offset); + this.AddValues(values, offset); foreach (ExifValue value in values) { if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long)) { - ThumbnailOffset = (uint)value.Value + this.startIndex; + this.ThumbnailOffset = (uint)value.Value + this.startIndex; } else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long) { - ThumbnailLength = (uint)value.Value; + this.ThumbnailLength = (uint)value.Value; } } } - private static TDataType[] ToArray(ExifDataType dataType, Byte[] data, + private static TDataType[] ToArray(ExifDataType dataType, byte[] data, ConverterMethod converter) { int dataTypeSize = (int)ExifValue.GetSize(dataType); @@ -388,7 +387,7 @@ namespace ImageProcessorCore private double ToDouble(byte[] data) { - if (!ValidateArray(data, 8)) + if (!this.ValidateArray(data, 8)) { return default(double); } @@ -398,7 +397,7 @@ namespace ImageProcessorCore private uint ToLong(byte[] data) { - if (!ValidateArray(data, 4)) + if (!this.ValidateArray(data, 4)) { return default(uint); } @@ -409,7 +408,7 @@ namespace ImageProcessorCore private ushort ToShort(byte[] data) { - if (!ValidateArray(data, 2)) + if (!this.ValidateArray(data, 2)) { return default(ushort); } @@ -419,7 +418,7 @@ namespace ImageProcessorCore private float ToSingle(byte[] data) { - if (!ValidateArray(data, 4)) + if (!this.ValidateArray(data, 4)) { return default(float); } @@ -441,7 +440,7 @@ namespace ImageProcessorCore private Rational ToRational(byte[] data) { - if (!ValidateArray(data, 8, 4)) + if (!this.ValidateArray(data, 8, 4)) { return Rational.Zero; } @@ -449,24 +448,9 @@ namespace ImageProcessorCore uint numerator = BitConverter.ToUInt32(data, 0); uint denominator = BitConverter.ToUInt32(data, 4); - // TODO: investigate the possibility of a Rational struct return new Rational(numerator, denominator); } - //private double ToRational(byte[] data) - //{ - // if (!ValidateArray(data, 8, 4)) - // { - // return default(double); - // } - - // uint numerator = BitConverter.ToUInt32(data, 0); - // uint denominator = BitConverter.ToUInt32(data, 4); - - // // TODO: investigate the possibility of a Rational struct - // return numerator / (double)denominator; - //} - private sbyte ToSignedByte(byte[] data) { return unchecked((sbyte)data[0]); @@ -474,7 +458,7 @@ namespace ImageProcessorCore private int ToSignedLong(byte[] data) { - if (!ValidateArray(data, 4)) + if (!this.ValidateArray(data, 4)) { return default(int); } @@ -484,7 +468,7 @@ namespace ImageProcessorCore private Rational ToSignedRational(byte[] data) { - if (!ValidateArray(data, 8, 4)) + if (!this.ValidateArray(data, 8, 4)) { return Rational.Zero; } @@ -495,22 +479,9 @@ namespace ImageProcessorCore return new Rational(numerator, denominator); } - //private double ToSignedRational(byte[] data) - //{ - // if (!ValidateArray(data, 8, 4)) - // { - // return default(double); - // } - - // int numerator = BitConverter.ToInt32(data, 0); - // int denominator = BitConverter.ToInt32(data, 4); - - // return numerator / (double)denominator; - //} - private short ToSignedShort(byte[] data) { - if (!ValidateArray(data, 2)) + if (!this.ValidateArray(data, 2)) { return default(short); } @@ -520,7 +491,7 @@ namespace ImageProcessorCore private bool ValidateArray(byte[] data, int size) { - return ValidateArray(data, size, size); + return this.ValidateArray(data, size, size); } private bool ValidateArray(byte[] data, int size, int stepSize) diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs b/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs index 9609ff366..23441cf0e 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs @@ -14,7 +14,7 @@ namespace ImageProcessorCore /// public sealed class ExifValue : IEquatable { - private object value; + private object exifValue; /// /// Initializes a new instance of the class @@ -26,18 +26,18 @@ namespace ImageProcessorCore { Guard.NotNull(other, nameof(other)); - DataType = other.DataType; - IsArray = other.IsArray; - Tag = other.Tag; + this.DataType = other.DataType; + this.IsArray = other.IsArray; + this.Tag = other.Tag; if (!other.IsArray) { - value = other.value; + this.exifValue = other.exifValue; } else { - Array array = (Array)other.value; - value = array.Clone(); + Array array = (Array)other.exifValue; + this.exifValue = array.Clone(); } } @@ -47,7 +47,6 @@ namespace ImageProcessorCore public ExifDataType DataType { get; - private set; } /// @@ -56,7 +55,6 @@ namespace ImageProcessorCore public bool IsArray { get; - private set; } /// @@ -65,7 +63,6 @@ namespace ImageProcessorCore public ExifTag Tag { get; - private set; } /// @@ -75,12 +72,12 @@ namespace ImageProcessorCore { get { - return this.value; + return this.exifValue; } set { - CheckValue(value); - this.value = value; + this.CheckValue(value); + this.exifValue = value; } } @@ -113,9 +110,11 @@ namespace ImageProcessorCore public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) + { return true; + } - return Equals(obj as ExifValue); + return this.Equals(obj as ExifValue); } /// @@ -125,45 +124,50 @@ namespace ImageProcessorCore public bool Equals(ExifValue other) { if (ReferenceEquals(other, null)) + { return false; + } if (ReferenceEquals(this, other)) + { return true; + } return - Tag == other.Tag && - DataType == other.DataType && - object.Equals(this.value, other.value); + this.Tag == other.Tag && + this.DataType == other.DataType && + Equals(this.exifValue, other.exifValue); } - /// - /// Serves as a hash of this type. - /// + /// public override int GetHashCode() { - int hashCode = Tag.GetHashCode() ^ DataType.GetHashCode(); - return this.value != null ? hashCode ^ this.value.GetHashCode() : hashCode; + return this.GetHashCode(this); } - /// - /// Returns a string that represents the current value. - /// + /// public override string ToString() { - if (this.value == null) + if (this.exifValue == null) + { return null; + } - if (DataType == ExifDataType.Ascii) - return (string)this.value; + if (this.DataType == ExifDataType.Ascii) + { + return (string)this.exifValue; + } - if (!IsArray) - return ToString(this.value); + if (!this.IsArray) + { + return this.ToString(this.exifValue); + } StringBuilder sb = new StringBuilder(); - foreach (object value in (Array)this.value) + foreach (object value in (Array)this.exifValue) { - sb.Append(ToString(value)); + sb.Append(this.ToString(value)); sb.Append(" "); } @@ -174,11 +178,15 @@ namespace ImageProcessorCore { get { - if (this.value == null) + if (this.exifValue == null) + { return false; + } - if (DataType == ExifDataType.Ascii) - return ((string)this.value).Length > 0; + if (this.DataType == ExifDataType.Ascii) + { + return ((string)this.exifValue).Length > 0; + } return true; } @@ -188,10 +196,12 @@ namespace ImageProcessorCore { get { - if (this.value == null) + if (this.exifValue == null) + { return 4; + } - int size = (int)(GetSize(DataType) * NumberOfComponents); + int size = (int)(GetSize(this.DataType) * this.NumberOfComponents); return size < 4 ? 4 : size; } @@ -201,11 +211,15 @@ namespace ImageProcessorCore { get { - if (DataType == ExifDataType.Ascii) - return Encoding.UTF8.GetBytes((string)this.value).Length; + if (this.DataType == ExifDataType.Ascii) + { + return Encoding.UTF8.GetBytes((string)this.exifValue).Length; + } - if (IsArray) - return ((Array)this.value).Length; + if (this.IsArray) + { + return ((Array)this.exifValue).Length; + } return 1; } @@ -213,28 +227,32 @@ namespace ImageProcessorCore internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray) { - Tag = tag; - DataType = dataType; - IsArray = isArray; + this.Tag = tag; + this.DataType = dataType; + this.IsArray = isArray; if (dataType == ExifDataType.Ascii) - IsArray = false; + { + this.IsArray = false; + } } internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray) : this(tag, dataType, isArray) { - this.value = value; + this.exifValue = value; } internal static ExifValue Create(ExifTag tag, object value) { Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag"); - ExifValue exifValue = null; - Type type = value != null ? value.GetType() : null; + ExifValue exifValue; + Type type = value?.GetType(); if (type != null && type.IsArray) + { type = type.GetElementType(); + } switch (tag) { @@ -456,7 +474,7 @@ namespace ImageProcessorCore break; default: - throw new NotImplementedException(); + throw new NotSupportedException(); } exifValue.Value = value; @@ -484,18 +502,20 @@ namespace ImageProcessorCore case ExifDataType.SignedRational: return 8; default: - throw new NotImplementedException(dataType.ToString()); + throw new NotSupportedException(dataType.ToString()); } } private void CheckValue(object value) { if (value == null) + { return; + } Type type = value.GetType(); - if (DataType == ExifDataType.Ascii) + if (this.DataType == ExifDataType.Ascii) { Guard.IsTrue(type == typeof(string), nameof(value), "Value should be a string."); return; @@ -503,45 +523,43 @@ namespace ImageProcessorCore if (type.IsArray) { - Guard.IsTrue(IsArray, nameof(value), "Value should not be an array."); + Guard.IsTrue(this.IsArray, nameof(value), "Value should not be an array."); type = type.GetElementType(); } else { - Guard.IsFalse(IsArray, nameof(value), "Value should not be an array."); + Guard.IsFalse(this.IsArray, nameof(value), "Value should not be an array."); } - switch (DataType) + switch (this.DataType) { case ExifDataType.Byte: - Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.DoubleFloat: - //case ExifDataType.Rational: - //case ExifDataType.SignedRational: - Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.Rational: case ExifDataType.SignedRational: - Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.Long: - Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.Short: - Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.SignedByte: - Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.SignedLong: - Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.SignedShort: - Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.SingleFloat: - Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(IsArray ? " array." : ".")}"); + Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(this.IsArray ? " array." : ".")}"); break; case ExifDataType.Undefined: Guard.IsTrue(type == typeof(byte), nameof(value), "Value should be a byte array."); @@ -554,22 +572,32 @@ namespace ImageProcessorCore private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray) { if (type == null || type == typeof(ushort)) + { return new ExifValue(tag, ExifDataType.Short, isArray); - else if (type == typeof(short)) + } + + if (type == typeof(short)) + { return new ExifValue(tag, ExifDataType.SignedShort, isArray); - else if (type == typeof(uint)) + } + + if (type == typeof(uint)) + { return new ExifValue(tag, ExifDataType.Long, isArray); - else - return new ExifValue(tag, ExifDataType.SignedLong, isArray); + } + + return new ExifValue(tag, ExifDataType.SignedLong, isArray); } private string ToString(object value) { - string description = ExifTagDescriptionAttribute.GetDescription(Tag, value); + string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value); if (description != null) - return description; + { + return description; + } - switch (DataType) + switch (this.DataType) { case ExifDataType.Ascii: return (string)value; @@ -580,7 +608,6 @@ namespace ImageProcessorCore case ExifDataType.Long: return ((uint)value).ToString(CultureInfo.InvariantCulture); case ExifDataType.Rational: - //return ((double)value).ToString(CultureInfo.InvariantCulture); return ((Rational)value).ToString(CultureInfo.InvariantCulture); case ExifDataType.Short: return ((ushort)value).ToString(CultureInfo.InvariantCulture); @@ -589,7 +616,6 @@ namespace ImageProcessorCore case ExifDataType.SignedLong: return ((int)value).ToString(CultureInfo.InvariantCulture); case ExifDataType.SignedRational: - //return ((double)value).ToString(CultureInfo.InvariantCulture); return ((Rational)value).ToString(CultureInfo.InvariantCulture); case ExifDataType.SignedShort: return ((short)value).ToString(CultureInfo.InvariantCulture); @@ -598,8 +624,23 @@ namespace ImageProcessorCore case ExifDataType.Undefined: return ((byte)value).ToString("X2", CultureInfo.InvariantCulture); default: - throw new NotImplementedException(); + throw new NotSupportedException(); } } + + /// + /// Returns the hash code for this instance. + /// + /// + /// The instance of to return the hash code for. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + private int GetHashCode(ExifValue exif) + { + int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode(); + return hashCode ^ exif.exifValue?.GetHashCode() ?? hashCode; + } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs b/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs index f41668a58..5fc9721f9 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs @@ -82,22 +82,19 @@ namespace ImageProcessorCore private const int StartIndex = 6; private ExifParts allowedParts; - private bool bestPrecision; private Collection values; private Collection dataOffsets; private Collection ifdIndexes; private Collection exifIndexes; private Collection gpsIndexes; - public ExifWriter(Collection values, ExifParts allowedParts, bool bestPrecision) + public ExifWriter(Collection values, ExifParts allowedParts) { this.values = values; this.allowedParts = allowedParts; - this.bestPrecision = bestPrecision; - - this.ifdIndexes = GetIndexes(ExifParts.IfdTags, IfdTags); - this.exifIndexes = GetIndexes(ExifParts.ExifTags, ExifTags); - this.gpsIndexes = GetIndexes(ExifParts.GPSTags, GPSTags); + this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, IfdTags); + this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags); + this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags); } public byte[] GetData() @@ -107,25 +104,35 @@ namespace ImageProcessorCore int gpsIndex = -1; if (this.exifIndexes.Count > 0) - exifIndex = (int)GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset); + { + exifIndex = (int)this.GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset); + } if (this.gpsIndexes.Count > 0) - gpsIndex = (int)GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset); + { + gpsIndex = (int)this.GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset); + } - uint ifdLength = 2 + GetLength(this.ifdIndexes) + 4; - uint exifLength = GetLength(this.exifIndexes); - uint gpsLength = GetLength(this.gpsIndexes); + uint ifdLength = 2 + this.GetLength(this.ifdIndexes) + 4; + uint exifLength = this.GetLength(this.exifIndexes); + uint gpsLength = this.GetLength(this.gpsIndexes); if (exifLength > 0) + { exifLength += 2; + } if (gpsLength > 0) + { gpsLength += 2; + } length = ifdLength + exifLength + gpsLength; if (length == 6) + { return null; + } length += 10 + 4 + 2; @@ -146,26 +153,30 @@ namespace ImageProcessorCore uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength; if (exifLength > 0) - this.values[exifIndex].Value = (ifdOffset + ifdLength); + { + this.values[exifIndex].Value = ifdOffset + ifdLength; + } if (gpsLength > 0) - this.values[gpsIndex].Value = (ifdOffset + ifdLength + exifLength); + { + this.values[gpsIndex].Value = ifdOffset + ifdLength + exifLength; + } i = Write(BitConverter.GetBytes(ifdOffset), result, i); - i = WriteHeaders(this.ifdIndexes, result, i); + i = this.WriteHeaders(this.ifdIndexes, result, i); i = Write(BitConverter.GetBytes(thumbnailOffset), result, i); - i = WriteData(this.ifdIndexes, result, i); + i = this.WriteData(this.ifdIndexes, result, i); if (exifLength > 0) { - i = WriteHeaders(this.exifIndexes, result, i); - i = WriteData(this.exifIndexes, result, i); + i = this.WriteHeaders(this.exifIndexes, result, i); + i = this.WriteData(this.exifIndexes, result, i); } if (gpsLength > 0) { - i = WriteHeaders(this.gpsIndexes, result, i); - i = WriteData(this.gpsIndexes, result, i); + i = this.WriteHeaders(this.gpsIndexes, result, i); + i = this.WriteData(this.gpsIndexes, result, i); } Write(BitConverter.GetBytes((ushort)0), result, i); @@ -178,7 +189,9 @@ namespace ImageProcessorCore foreach (int index in indexes) { if (this.values[index].Tag == tag) + { return index; + } } int newIndex = this.values.Count; @@ -190,7 +203,9 @@ namespace ImageProcessorCore private Collection GetIndexes(ExifParts part, ExifTag[] tags) { if (((int)this.allowedParts & (int)part) == 0) + { return new Collection(); + } Collection result = new Collection(); for (int i = 0; i < this.values.Count; i++) @@ -198,11 +213,15 @@ namespace ImageProcessorCore ExifValue value = this.values[i]; if (!value.HasValue) + { continue; + } int index = Array.IndexOf(tags, value.Tag); if (index > -1) + { result.Add(i); + } } return result; @@ -217,9 +236,13 @@ namespace ImageProcessorCore uint valueLength = (uint)this.values[index].Length; if (valueLength > 4) + { length += 12 + valueLength; + } else + { length += 12; + } } return length; @@ -235,11 +258,15 @@ namespace ImageProcessorCore private int WriteArray(ExifValue value, byte[] destination, int offset) { if (value.DataType == ExifDataType.Ascii) - return WriteValue(ExifDataType.Ascii, value.Value, destination, offset); + { + return this.WriteValue(ExifDataType.Ascii, value.Value, destination, offset); + } int newOffset = offset; foreach (object obj in (Array)value.Value) - newOffset = WriteValue(value.DataType, obj, destination, newOffset); + { + newOffset = this.WriteValue(value.DataType, obj, destination, newOffset); + } return newOffset; } @@ -247,7 +274,9 @@ namespace ImageProcessorCore private int WriteData(Collection indexes, byte[] destination, int offset) { if (this.dataOffsets.Count == 0) + { return offset; + } int newOffset = offset; @@ -258,7 +287,7 @@ namespace ImageProcessorCore if (value.Length > 4) { Write(BitConverter.GetBytes(newOffset - StartIndex), destination, this.dataOffsets[i++]); - newOffset = WriteValue(value, destination, newOffset); + newOffset = this.WriteValue(value, destination, newOffset); } } @@ -272,7 +301,9 @@ namespace ImageProcessorCore int newOffset = Write(BitConverter.GetBytes((ushort)indexes.Count), destination, offset); if (indexes.Count == 0) + { return newOffset; + } foreach (int index in indexes) { @@ -282,9 +313,13 @@ namespace ImageProcessorCore newOffset = Write(BitConverter.GetBytes((uint)value.NumberOfComponents), destination, newOffset); if (value.Length > 4) + { this.dataOffsets.Add(newOffset); + } else - WriteValue(value, destination, newOffset); + { + this.WriteValue(value, destination, newOffset); + } newOffset += 4; } @@ -294,91 +329,21 @@ namespace ImageProcessorCore private int WriteRational(Rational value, byte[] destination, int offset) { + // Ensure no overflow Write(BitConverter.GetBytes((uint)(value.Numerator * (value.ToDouble() < 0.0 ? -1 : 1))), destination, offset); Write(BitConverter.GetBytes((uint)value.Denominator), destination, offset + 4); return offset + 8; } - //private int WriteRational(double value, byte[] destination, int offset) - //{ - // uint numerator = 1; - // uint denominator = 1; - - // if (double.IsPositiveInfinity(value)) - // denominator = 0; - // else if (double.IsNegativeInfinity(value)) - // denominator = 0; - // else - // { - // double val = Math.Abs(value); - // double df = numerator / denominator; - // double epsilon = this.bestPrecision ? double.Epsilon : .000001; - - // while (Math.Abs(df - val) > epsilon) - // { - // if (df < val) - // numerator++; - // else - // { - // denominator++; - // numerator = (uint)(val * denominator); - // } - - // df = numerator / (double)denominator; - // } - // } - - // Write(BitConverter.GetBytes(numerator), destination, offset); - // Write(BitConverter.GetBytes(denominator), destination, offset + 4); - - // return offset + 8; - //} - private int WriteSignedRational(Rational value, byte[] destination, int offset) { - // TODO: Check this. Write(BitConverter.GetBytes((int)value.Numerator), destination, offset); Write(BitConverter.GetBytes((int)value.Denominator), destination, offset + 4); return offset + 8; } - //private int WriteSignedRational(double value, byte[] destination, int offset) - //{ - // int numerator = 1; - // int denominator = 1; - - // if (double.IsPositiveInfinity(value)) - // denominator = 0; - // else if (double.IsNegativeInfinity(value)) - // denominator = 0; - // else - // { - // double val = Math.Abs(value); - // double df = numerator / denominator; - // double epsilon = this.bestPrecision ? double.Epsilon : .000001; - - // while (Math.Abs(df - val) > epsilon) - // { - // if (df < val) - // numerator++; - // else - // { - // denominator++; - // numerator = (int)(val * denominator); - // } - - // df = numerator / (double)denominator; - // } - // } - - // Write(BitConverter.GetBytes(numerator * (value < 0.0 ? -1 : 1)), destination, offset); - // Write(BitConverter.GetBytes(denominator), destination, offset + 4); - - // return offset + 8; - //} - private int WriteValue(ExifDataType dataType, object value, byte[] destination, int offset) { switch (dataType) @@ -396,7 +361,7 @@ namespace ImageProcessorCore case ExifDataType.Long: return Write(BitConverter.GetBytes((uint)value), destination, offset); case ExifDataType.Rational: - return WriteRational((Rational)value, destination, offset); + return this.WriteRational((Rational)value, destination, offset); case ExifDataType.SignedByte: destination[offset] = unchecked((byte)((sbyte)value)); return offset + 1; @@ -405,7 +370,7 @@ namespace ImageProcessorCore case ExifDataType.SignedShort: return Write(BitConverter.GetBytes((short)value), destination, offset); case ExifDataType.SignedRational: - return WriteSignedRational((Rational)value, destination, offset); + return this.WriteSignedRational((Rational)value, destination, offset); case ExifDataType.SingleFloat: return Write(BitConverter.GetBytes((float)value), destination, offset); default: @@ -416,9 +381,11 @@ namespace ImageProcessorCore private int WriteValue(ExifValue value, byte[] destination, int offset) { if (value.IsArray && value.DataType != ExifDataType.Ascii) - return WriteArray(value, destination, offset); - else - return WriteValue(value.DataType, value.Value, destination, offset); + { + return this.WriteArray(value, destination, offset); + } + + return this.WriteValue(value.DataType, value.Value, destination, offset); } } } \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs b/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs index 31307f2cf..438e90336 100644 --- a/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs @@ -90,7 +90,6 @@ namespace ImageProcessorCore.Tests profile = GetExifProfile(); profile.SetValue(ExifTag.ExposureTime, exposureTime); - profile.BestPrecision = true; image.ExifProfile = profile; image.SaveAsJpeg(memStream);