Browse Source

All tests pass.

pull/1067/head
James Jackson-South 6 years ago
parent
commit
15604fce6c
  1. BIN
      src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf
  2. 75
      src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
  3. 4
      src/ImageSharp/Metadata/Profiles/Exif/README.md
  4. 265
      tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

BIN
src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf

Binary file not shown.

75
src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs

@ -143,15 +143,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// <typeparam name="TValueType">The data type of the tag.</typeparam>
public IExifValue<TValueType> GetValue<TValueType>(ExifTag<TValueType> tag)
{
foreach (IExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
return (IExifValue<TValueType>)exifValue;
}
}
return null;
IExifValue value = this.GetValueInternal(tag);
return value is null ? null : (IExifValue<TValueType>)value;
}
/// <summary>
@ -184,25 +177,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// <param name="value">The value.</param>
/// <typeparam name="TValueType">The data type of the tag.</typeparam>
public void SetValue<TValueType>(ExifTag<TValueType> tag, TValueType value)
{
foreach (IExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
exifValue.TrySetValue(value);
return;
}
}
ExifValue newExifValue = ExifValues.Create(tag);
if (newExifValue is null)
{
throw new NotSupportedException();
}
newExifValue.TrySetValue(value);
this.values.Add(newExifValue);
}
=> this.SetValueInternal(tag, value);
/// <summary>
/// Converts this instance to a byte array.
@ -227,6 +202,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// <inheritdoc/>
public ExifProfile DeepClone() => new ExifProfile(this);
/// <summary>
/// Returns the value with the specified tag.
/// </summary>
/// <param name="tag">The tag of the exif value.</param>
/// <returns>The value with the specified tag.</returns>
internal IExifValue GetValueInternal(ExifTag tag)
{
foreach (IExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
return exifValue;
}
}
return null;
}
/// <summary>
/// Sets the value of the specified tag.
/// </summary>
/// <param name="tag">The tag of the exif value.</param>
/// <param name="value">The value.</param>
internal void SetValueInternal(ExifTag tag, object value)
{
foreach (IExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
exifValue.TrySetValue(value);
return;
}
}
ExifValue newExifValue = ExifValues.Create(tag);
if (newExifValue is null)
{
throw new NotSupportedException();
}
newExifValue.TrySetValue(value);
this.values.Add(newExifValue);
}
/// <summary>
/// Synchronizes the profiles with the specified metadata.
/// </summary>

4
src/ImageSharp/Metadata/Profiles/Exif/README.md

@ -1,3 +1,3 @@
Adapted from Magick.NET:
Adapted from Magick.NET:
https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif
https://github.com/dlemstra/Magick.NET

265
tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

@ -154,83 +154,90 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(new Rational(double.PositiveInfinity), value2.Value);
}
//[Theory]
//[InlineData(TestImageWriteFormat.Jpeg)]
//[InlineData(TestImageWriteFormat.Png)]
//public void SetValue(TestImageWriteFormat imageFormat)
//{
// var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
// Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
// image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp");
[Theory]
[InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)]
public void SetValue(TestImageWriteFormat imageFormat)
{
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp");
// IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
// TestValue(value, "ImageSharp");
IExifValue<string> software = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
Assert.Equal("ImageSharp", software.Value);
// Assert.Throws<ArgumentException>(() => { value.SetValue(15); });
// ExifString can set integer values.
Assert.True(software.TrySetValue(15));
Assert.False(software.TrySetValue(15F));
// image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
// value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
IExifValue<SignedRational> shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
// TestValue(value, new SignedRational(7555, 100));
Assert.Equal(new SignedRational(7555, 100), shutterSpeed.Value);
Assert.False(shutterSpeed.TrySetValue(75));
// Assert.Throws<ArgumentException>(() => { value.WithValue(75); });
image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0));
// image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0));
// We also need to change this value because this overrides XResolution when the image is written.
image.Metadata.HorizontalResolution = 150.0;
// // We also need to change this value because this overrides XResolution when the image is written.
// image.Metadata.HorizontalResolution = 150.0;
IExifValue<Rational> xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
Assert.Equal(new Rational(150, 1), xResolution.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
// TestValue(value, new Rational(150, 1));
Assert.False(xResolution.TrySetValue("ImageSharp"));
// Assert.Throws<ArgumentException>(() => { value.WithValue("ImageSharp"); });
image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
// image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
IExifValue<Rational[]> referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
Assert.Null(referenceBlackWhite.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
// TestValue(value, (string)null);
var expectedLatitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, expectedLatitude);
// image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude);
IExifValue<Rational[]> latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
Assert.Equal(expectedLatitude, latitude.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
// TestValue(value, latitude);
int profileCount = image.Metadata.ExifProfile.Values.Count;
image = WriteAndRead(image, imageFormat);
// image = WriteAndRead(image, imageFormat);
Assert.NotNull(image.Metadata.ExifProfile);
// Assert.NotNull(image.Metadata.ExifProfile);
// Assert.Equal(17, image.Metadata.ExifProfile.Values.Count());
// Should be 3 less.
// 1 x due to setting of null "ReferenceBlackWhite" value.
// 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere
// strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2)
// https://exiftool.org/TagNames/EXIF.html
Assert.Equal(profileCount - 3, image.Metadata.ExifProfile.Values.Count);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
// TestValue(value, "ImageSharp");
software = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
Assert.Equal("15", software.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
// TestValue(value, new SignedRational(75.55));
shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
Assert.Equal(new SignedRational(75.55), shutterSpeed.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
// TestValue(value, new Rational(150.0));
xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
Assert.Equal(new Rational(150.0), xResolution.Value);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
// Assert.Null(value);
referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
Assert.Null(referenceBlackWhite);
// value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
// TestValue(value, latitude);
latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
Assert.Equal(expectedLatitude, latitude.Value);
// image.Metadata.ExifProfile.Parts = ExifParts.ExifTags;
image.Metadata.ExifProfile.Parts = ExifParts.ExifTags;
// image = WriteAndRead(image, imageFormat);
image = WriteAndRead(image, imageFormat);
// Assert.NotNull(image.Metadata.ExifProfile);
// Assert.Equal(8, image.Metadata.ExifProfile.Values.Count());
Assert.NotNull(image.Metadata.ExifProfile);
Assert.Equal(8, image.Metadata.ExifProfile.Values.Count);
// Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
// Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
// Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
// Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
// Assert.Equal(7, image.Metadata.ExifProfile.Values.Count());
//}
Assert.Equal(7, image.Metadata.ExifProfile.Values.Count);
}
[Fact]
public void Syncs()
@ -308,8 +315,8 @@ namespace SixLabors.ImageSharp.Tests
foreach (ExifTag expectedProfileTag in expectedProfileTags)
{
IExifValue actualProfileValue = actualProfile.Values.First(x => x.Tag == expectedProfileTag);
IExifValue expectedProfileValue = expectedProfile.Values.First(x => x.Tag == expectedProfileTag);
IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag);
IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
}
@ -324,7 +331,6 @@ namespace SixLabors.ImageSharp.Tests
{
// This image contains an 802 byte EXIF profile
// It has a tag with an index offset of 18,481,152 bytes (overrunning the data)
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image();
Assert.NotNull(image);
@ -357,53 +363,52 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(525, bytes.Length);
}
//[Theory]
//[InlineData(TestImageWriteFormat.Jpeg)]
//[InlineData(TestImageWriteFormat.Png)]
//public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
//{
// // arrange
// var image = new Image<Rgba32>(1, 1);
// ExifProfile expected = CreateExifProfile();
// image.Metadata.ExifProfile = expected;
// // act
// Image<Rgba32> reloadedImage = WriteAndRead(image, imageFormat);
// // assert
// ExifProfile actual = reloadedImage.Metadata.ExifProfile;
// Assert.NotNull(actual);
// foreach (KeyValuePair<ExifTag, object> expectedProfileValue in TestProfileValues)
// {
// ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key);
// Assert.NotNull(actualProfileValue);
// Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value);
// }
//}
//[Fact]
//public void ProfileToByteArray()
//{
// // arrange
// byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
// ExifProfile expectedProfile = CreateExifProfile();
// var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
// // act
// byte[] actualBytes = expectedProfile.ToByteArray();
// var actualProfile = new ExifProfile(actualBytes);
// // assert
// Assert.NotNull(actualBytes);
// Assert.NotEmpty(actualBytes);
// Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
// foreach (ExifTag expectedProfileTag in expectedProfileTags)
// {
// ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag);
// ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag);
// Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value);
// }
//}
[Theory]
[InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)]
public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
{
// Arrange
var image = new Image<Rgba32>(1, 1);
image.Metadata.ExifProfile = CreateExifProfile();
// Act
Image<Rgba32> reloadedImage = WriteAndRead(image, imageFormat);
// Assert
ExifProfile actual = reloadedImage.Metadata.ExifProfile;
Assert.NotNull(actual);
foreach (KeyValuePair<ExifTag, object> expectedProfileValue in TestProfileValues)
{
IExifValue actualProfileValue = actual.GetValueInternal(expectedProfileValue.Key);
Assert.NotNull(actualProfileValue);
Assert.Equal(expectedProfileValue.Value, actualProfileValue.GetValue());
}
}
[Fact]
public void ProfileToByteArray()
{
// Arrange
byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
ExifProfile expectedProfile = CreateExifProfile();
var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
// Act
byte[] actualBytes = expectedProfile.ToByteArray();
var actualProfile = new ExifProfile(actualBytes);
// Assert
Assert.NotNull(actualBytes);
Assert.NotEmpty(actualBytes);
Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
foreach (ExifTag expectedProfileTag in expectedProfileTags)
{
IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag);
IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
}
}
private static ExifProfile CreateExifProfile()
{
@ -411,12 +416,7 @@ namespace SixLabors.ImageSharp.Tests
foreach (KeyValuePair<ExifTag, object> exifProfileValue in TestProfileValues)
{
// Manky - Answers on a postcard for a nicer way to do this.
MethodInfo method = typeof(ExifProfile)
.GetMethod("SetValue")
.MakeGenericMethod(new[] { exifProfileValue.Key.GetType().GenericTypeArguments[0] });
method.Invoke(profile, new[] { exifProfileValue.Key, exifProfileValue.Value });
profile.SetValueInternal(exifProfileValue.Key, exifProfileValue.Value);
}
return profile;
@ -434,12 +434,15 @@ namespace SixLabors.ImageSharp.Tests
private static Image<Rgba32> WriteAndRead(Image<Rgba32> image, TestImageWriteFormat imageFormat)
{
return imageFormat switch
switch (imageFormat)
{
TestImageWriteFormat.Jpeg => WriteAndReadJpeg(image),
TestImageWriteFormat.Png => WriteAndReadPng(image),
_ => throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"),
};
case TestImageWriteFormat.Jpeg:
return WriteAndReadJpeg(image);
case TestImageWriteFormat.Png:
return WriteAndReadPng(image);
default:
throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed");
}
}
private static Image<Rgba32> WriteAndReadJpeg(Image<Rgba32> image)
@ -486,43 +489,5 @@ namespace SixLabors.ImageSharp.Tests
IExifValue<Number> xDimension = profile.GetValue(ExifTag.PixelXDimension);
Assert.Equal(2338U, xDimension.Value);
}
private static void TestValue(IExifValue value, string expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.GetValue());
}
private static void TestValue(IExifValue value, Rational expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.GetValue());
}
private static void TestValue(IExifValue value, SignedRational expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.GetValue());
}
private static void TestValue(IExifValue value, Rational[] expected)
{
Assert.NotNull(value);
Assert.Equal(expected, (ICollection)value.GetValue());
}
private static void TestValue(ExifValue value, double expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.GetValue());
}
private static void TestValue(ExifValue value, double[] expected)
{
Assert.NotNull(value);
Assert.Equal(expected, (ICollection)value.GetValue());
}
}
}

Loading…
Cancel
Save