diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs
index 2caa22859..5db095f09 100644
--- a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs
@@ -10,49 +10,67 @@ namespace ImageProcessorCore
using System.IO;
///
- /// Class that can be used to access an Exif profile.
+ /// Represents an EXIF profile providing access to the collection of values.
///
public sealed class ExifProfile
{
- private byte[] data;
+ ///
+ /// The byte array to read the EXIF profile from.
+ ///
+ private readonly byte[] data;
+
+ ///
+ /// The collection of EXIF values
+ ///
private Collection values;
+
+ ///
+ /// The list of invalid EXIF tags
+ ///
private List invalidTags;
+
+ ///
+ /// The thumbnail offset position in the byte stream
+ ///
private int thumbnailOffset;
+
+ ///
+ /// The thumbnail length in the byte stream
+ ///
private int thumbnailLength;
- ///
+ ///
/// Initializes a new instance of the class.
- ///
- ///The byte array to read the exif profile from.
+ ///
public ExifProfile()
: this((byte[])null)
{
}
- ///
+ ///
/// Initializes a new instance of the class.
- ///
- ///The byte array to read the exif profile from.
+ ///
+ /// The byte array to read the EXIF profile from.
public ExifProfile(byte[] data)
{
- Parts = ExifParts.All;
- BestPrecision = false;
+ this.Parts = ExifParts.All;
+ this.BestPrecision = false;
this.data = data;
this.invalidTags = new List();
}
///
/// Initializes a new instance of the class
- /// by making a copy from another exif profile.
+ /// by making a copy from another EXIF profile.
///
- /// The other exif profile, where the clone should be made from.
+ /// The other EXIF profile, where the clone should be made from.
/// is null.
public ExifProfile(ExifProfile other)
{
Guard.NotNull(other, nameof(other));
- Parts = other.Parts;
- BestPrecision = other.BestPrecision;
+ this.Parts = other.Parts;
+ this.BestPrecision = other.BestPrecision;
this.thumbnailLength = other.thumbnailLength;
this.thumbnailOffset = other.thumbnailOffset;
@@ -60,7 +78,7 @@ namespace ImageProcessorCore
if (other.values != null)
{
this.values = new Collection();
- foreach(ExifValue value in other.values)
+ foreach (ExifValue value in other.values)
{
this.values.Add(new ExifValue(value));
}
@@ -71,39 +89,34 @@ namespace ImageProcessorCore
}
}
- ///
- /// Specifies if rationals should be stored with the best precision possible. This is disabled
- /// by default, setting this to true will have an impact on the performance.
- ///
+ ///
+ /// 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;
}
- ///
- /// Specifies which parts will be written when the profile is added to an image.
- ///
+ ///
+ /// Gets or sets which parts will be written when the profile is added to an image.
+ ///
public ExifParts Parts
{
get;
set;
}
- ///
- /// Returns the tags that where found but contained an invalid value.
- ///
- public IEnumerable InvalidTags
- {
- get
- {
- return this.invalidTags;
- }
- }
+ ///
+ /// Gets the tags that where found but contained an invalid value.
+ ///
+ public IEnumerable InvalidTags => this.invalidTags;
- ///
- /// Returns the values of this exif profile.
- ///
+ ///
+ /// Gets the values of this EXIF profile.
+ ///
public IEnumerable Values
{
get
@@ -113,9 +126,9 @@ namespace ImageProcessorCore
}
}
- ///
- /// Returns the thumbnail in the exif profile when available.
- ///
+ ///
+ /// Returns the thumbnail in the EXIF profile when available.
+ ///
/// The pixel format.
/// The packed format. long, float.
public Image CreateThumbnail()
@@ -125,10 +138,14 @@ namespace ImageProcessorCore
InitializeValues();
if (this.thumbnailOffset == 0 || this.thumbnailLength == 0)
+ {
return null;
+ }
if (this.data.Length < (this.thumbnailOffset + this.thumbnailLength))
+ {
return null;
+ }
using (MemoryStream memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength))
{
@@ -136,10 +153,10 @@ namespace ImageProcessorCore
}
}
- ///
+ ///
/// Returns the value with the specified tag.
- ///
- ///The tag of the exif value.
+ ///
+ /// The tag of the EXIF value.
public ExifValue GetValue(ExifTag tag)
{
foreach (ExifValue exifValue in Values)
@@ -151,10 +168,10 @@ namespace ImageProcessorCore
return null;
}
- ///
+ ///
/// Removes the value with the specified tag.
- ///
- ///The tag of the exif value.
+ ///
+ /// The tag of the EXIF value.
public bool RemoveValue(ExifTag tag)
{
InitializeValues();
@@ -171,14 +188,14 @@ namespace ImageProcessorCore
return false;
}
- ///
+ ///
/// Sets the value of the specified tag.
- ///
- ///The tag of the exif value.
- ///The value.
+ ///
+ /// The tag of the EXIF value.
+ /// The value.
public void SetValue(ExifTag tag, object value)
{
- foreach (ExifValue exifValue in Values)
+ foreach (ExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
@@ -191,25 +208,32 @@ namespace ImageProcessorCore
this.values.Add(newExifValue);
}
- ///
+ ///
/// Converts this instance to a byte array.
- ///
+ ///
+ /// The
public byte[] ToByteArray()
{
if (this.values == null)
- return data;
+ {
+ return this.data;
+ }
if (this.values.Count == 0)
+ {
return null;
+ }
- ExifWriter writer = new ExifWriter(this.values, Parts, BestPrecision);
+ ExifWriter writer = new ExifWriter(this.values, this.Parts, this.BestPrecision);
return writer.GetData();
}
private void InitializeValues()
{
if (this.values != null)
+ {
return;
+ }
if (this.data == null)
{
diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs
index c37d15388..ef601596e 100644
--- a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs
+++ b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs
@@ -11,97 +11,122 @@ namespace ImageProcessorCore
using System.Linq;
using System.Text;
+ ///
+ /// Reads and parses EXIF data from a byte array
+ ///
internal sealed class ExifReader
{
private delegate TDataType ConverterMethod(byte[] data);
- private byte[] data;
- private Collection invalidTags = new Collection();
- private uint index;
+ private readonly Collection invalidTags = new Collection();
+ private byte[] exifData;
+ private uint currentIndex;
private bool isLittleEndian;
private uint exifOffset;
private uint gpsOffset;
private uint startIndex;
+ ///
+ /// Gets the thumbnail length in the byte stream
+ ///
public uint ThumbnailLength
{
get;
private set;
}
+ ///
+ /// Gets the thumbnail offset position in the byte stream
+ ///
public uint ThumbnailOffset
{
get;
private set;
}
+ ///
+ /// Gets the remaining length.
+ ///
private int RemainingLength
{
get
{
- if (this.index >= this.data.Length)
+ if (this.currentIndex >= this.exifData.Length)
+ {
return 0;
+ }
- return this.data.Length - (int)this.index;
+ return this.exifData.Length - (int)this.currentIndex;
}
}
+ ///
+ /// Reads and returns the collection of EXIF values.
+ ///
+ /// The data.
+ ///
+ /// The .
+ ///
public Collection Read(byte[] data)
{
Collection result = new Collection();
- this.data = data;
+ this.exifData = data;
- if (GetString(4) == "Exif")
+ if (this.GetString(4) == "Exif")
{
- if (GetShort() != 0)
+ if (this.GetShort() != 0)
+ {
return result;
+ }
this.startIndex = 6;
}
else
{
- this.index = 0;
+ this.currentIndex = 0;
}
- this.isLittleEndian = GetString(2) == "II";
+ this.isLittleEndian = this.GetString(2) == "II";
- if (GetShort() != 0x002A)
+ if (this.GetShort() != 0x002A)
+ {
return result;
+ }
- uint ifdOffset = GetLong();
- AddValues(result, ifdOffset);
+ uint ifdOffset = this.GetLong();
+ this.AddValues(result, ifdOffset);
- uint thumbnailOffset = GetLong();
- GetThumbnail(thumbnailOffset);
+ uint thumbnailOffset = this.GetLong();
+ this.GetThumbnail(thumbnailOffset);
if (this.exifOffset != 0)
- AddValues(result, this.exifOffset);
+ {
+ this.AddValues(result, this.exifOffset);
+ }
if (this.gpsOffset != 0)
- AddValues(result, this.gpsOffset);
+ {
+ this.AddValues(result, this.gpsOffset);
+ }
return result;
}
- public IEnumerable InvalidTags
- {
- get
- {
- return this.invalidTags;
- }
- }
+ public IEnumerable InvalidTags => this.invalidTags;
private void AddValues(Collection values, uint index)
{
- this.index = this.startIndex + index;
+ this.currentIndex = this.startIndex + index;
ushort count = GetShort();
for (ushort i = 0; i < count; i++)
{
ExifValue value = CreateValue();
if (value == null)
+ {
continue;
+ }
bool duplicate = false;
foreach (ExifValue val in values)
@@ -114,27 +139,37 @@ namespace ImageProcessorCore
}
if (duplicate)
+ {
continue;
+ }
if (value.Tag == ExifTag.SubIFDOffset)
{
if (value.DataType == ExifDataType.Long)
+ {
this.exifOffset = (uint)value.Value;
+ }
}
else if (value.Tag == ExifTag.GPSIFDOffset)
{
if (value.DataType == ExifDataType.Long)
+ {
this.gpsOffset = (uint)value.Value;
+ }
}
else
+ {
values.Add(value);
+ }
}
}
private object ConvertValue(ExifDataType dataType, byte[] data, uint numberOfComponents)
{
if (data == null || data.Length == 0)
+ {
return null;
+ }
switch (dataType)
{
@@ -144,93 +179,120 @@ namespace ImageProcessorCore
return ToString(data);
case ExifDataType.Byte:
if (numberOfComponents == 1)
+ {
return ToByte(data);
- else
- return data;
+ }
+
+ return data;
case ExifDataType.DoubleFloat:
if (numberOfComponents == 1)
- return ToDouble(data);
- else
- return ToArray(dataType, data, ToDouble);
+ {
+ return this.ToDouble(data);
+ }
+
+ return ToArray(dataType, data, this.ToDouble);
case ExifDataType.Long:
if (numberOfComponents == 1)
- return ToLong(data);
- else
- return ToArray(dataType, data, ToLong);
+ {
+ return this.ToLong(data);
+ }
+
+ return ToArray(dataType, data, ToLong);
case ExifDataType.Rational:
if (numberOfComponents == 1)
- return ToRational(data);
- else
- return ToArray(dataType, data, ToRational);
+ {
+ return this.ToRational(data);
+ }
+
+ return ToArray(dataType, data, this.ToRational);
case ExifDataType.Short:
if (numberOfComponents == 1)
- return ToShort(data);
- else
- return ToArray(dataType, data, ToShort);
+ {
+ return this.ToShort(data);
+ }
+
+ return ToArray(dataType, data, this.ToShort);
case ExifDataType.SignedByte:
if (numberOfComponents == 1)
- return ToSignedByte(data);
- else
- return ToArray(dataType, data, ToSignedByte);
+ {
+ return this.ToSignedByte(data);
+ }
+
+ return ToArray(dataType, data, this.ToSignedByte);
case ExifDataType.SignedLong:
if (numberOfComponents == 1)
- return ToSignedLong(data);
- else
- return ToArray(dataType, data, ToSignedLong);
+ {
+ return this.ToSignedLong(data);
+ }
+
+ return ToArray(dataType, data, this.ToSignedLong);
case ExifDataType.SignedRational:
if (numberOfComponents == 1)
- return ToSignedRational(data);
- else
- return ToArray(dataType, data, ToSignedRational);
+ {
+ return this.ToSignedRational(data);
+ }
+
+ return ToArray(dataType, data, this.ToSignedRational);
case ExifDataType.SignedShort:
if (numberOfComponents == 1)
- return ToSignedShort(data);
- else
- return ToArray(dataType, data, ToSignedShort);
+ {
+ return this.ToSignedShort(data);
+ }
+
+ return ToArray(dataType, data, this.ToSignedShort);
case ExifDataType.SingleFloat:
if (numberOfComponents == 1)
+ {
return ToSingle(data);
- else
- return ToArray(dataType, data, ToSingle);
+ }
+
+ return ToArray(dataType, data, ToSingle);
case ExifDataType.Undefined:
if (numberOfComponents == 1)
+ {
return ToByte(data);
- else
- return data;
+ }
+
+ return data;
default:
- throw new NotImplementedException();
+ throw new NotSupportedException();
}
}
private ExifValue CreateValue()
{
if (RemainingLength < 12)
+ {
return null;
+ }
- ExifTag tag = ToEnum(GetShort(), ExifTag.Unknown);
- ExifDataType dataType = ToEnum(GetShort(), ExifDataType.Unknown);
- object value = null;
+ ExifTag tag = ToEnum(this.GetShort(), ExifTag.Unknown);
+ ExifDataType dataType = ToEnum(this.GetShort(), ExifDataType.Unknown);
+ object value;
if (dataType == ExifDataType.Unknown)
- return new ExifValue(tag, dataType, value, false);
+ {
+ return new ExifValue(tag, dataType, null, false);
+ }
- uint numberOfComponents = (uint)GetLong();
+ uint numberOfComponents = this.GetLong();
uint size = numberOfComponents * ExifValue.GetSize(dataType);
byte[] data = GetBytes(4);
if (size > 4)
{
- uint oldIndex = this.index;
- this.index = ToLong(data) + this.startIndex;
+ uint oldIndex = this.currentIndex;
+ this.currentIndex = ToLong(data) + this.startIndex;
if (RemainingLength < size)
{
this.invalidTags.Add(tag);
- this.index = oldIndex;
+ this.currentIndex = oldIndex;
return null;
}
- value = ConvertValue(dataType, GetBytes(size), numberOfComponents);
- this.index = oldIndex;
+
+ value = ConvertValue(dataType, this.GetBytes(size), numberOfComponents);
+ this.currentIndex = oldIndex;
}
else
{
@@ -246,19 +308,23 @@ namespace ImageProcessorCore
{
TEnum enumValue = (TEnum)(object)value;
if (Enum.GetValues(typeof(TEnum)).Cast().Any(v => v.Equals(enumValue)))
+ {
return enumValue;
+ }
return defaultValue;
}
private byte[] GetBytes(uint length)
{
- if (this.index + length > (uint)this.data.Length)
+ if (this.currentIndex + length > (uint)this.exifData.Length)
+ {
return null;
+ }
byte[] data = new byte[length];
- Array.Copy(this.data, (int)this.index, data, 0, (int)length);
- this.index += length;
+ Array.Copy(this.exifData, (int)this.currentIndex, data, 0, (int)length);
+ this.currentIndex += length;
return data;
}
@@ -286,9 +352,13 @@ namespace ImageProcessorCore
foreach (ExifValue value in values)
{
if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
+ {
ThumbnailOffset = (uint)value.Value + this.startIndex;
+ }
else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
+ {
ThumbnailLength = (uint)value.Value;
+ }
}
}
@@ -319,7 +389,9 @@ namespace ImageProcessorCore
private double ToDouble(byte[] data)
{
if (!ValidateArray(data, 8))
+ {
return default(double);
+ }
return BitConverter.ToDouble(data, 0);
}
@@ -327,7 +399,9 @@ namespace ImageProcessorCore
private uint ToLong(byte[] data)
{
if (!ValidateArray(data, 4))
+ {
return default(uint);
+ }
return BitConverter.ToUInt32(data, 0);
}
@@ -336,7 +410,9 @@ namespace ImageProcessorCore
{
if (!ValidateArray(data, 2))
+ {
return default(ushort);
+ }
return BitConverter.ToUInt16(data, 0);
}
@@ -344,7 +420,9 @@ namespace ImageProcessorCore
private float ToSingle(byte[] data)
{
if (!ValidateArray(data, 4))
+ {
return default(float);
+ }
return BitConverter.ToSingle(data, 0);
}
@@ -354,7 +432,9 @@ namespace ImageProcessorCore
string result = Encoding.UTF8.GetString(data, 0, data.Length);
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
+ {
result = result.Substring(0, nullCharIndex);
+ }
return result;
}
@@ -362,11 +442,14 @@ namespace ImageProcessorCore
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;
}
@@ -378,7 +461,9 @@ namespace ImageProcessorCore
private int ToSignedLong(byte[] data)
{
if (!ValidateArray(data, 4))
+ {
return default(int);
+ }
return BitConverter.ToInt32(data, 0);
}
@@ -386,7 +471,9 @@ namespace ImageProcessorCore
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);
@@ -397,7 +484,9 @@ namespace ImageProcessorCore
private short ToSignedShort(byte[] data)
{
if (!ValidateArray(data, 2))
+ {
return default(short);
+ }
return BitConverter.ToInt16(data, 0);
}
@@ -410,10 +499,14 @@ namespace ImageProcessorCore
private bool ValidateArray(byte[] data, int size, int stepSize)
{
if (data == null || data.Length < size)
+ {
return false;
+ }
if (this.isLittleEndian == BitConverter.IsLittleEndian)
+ {
return true;
+ }
for (int i = 0; i < data.Length; i += stepSize)
{