Browse Source

The JPEG encoder and decoder will now read and write the Exif profile and added unit tests to verify this.

Former-commit-id: 5ea83609c02c71e457f1e083501b30dfb86af88c
Former-commit-id: a594b52cfb4b1fb96d39fa1241c1db1db42ee401
Former-commit-id: 7276cfdbaa100a6ba0d705c311e6b439b3d11cfd
af/merge-core
dirk 10 years ago
parent
commit
ecb67ee763
  1. 2
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  2. 39
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs
  3. 306
      tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs
  4. 50
      tests/ImageProcessorCore.Tests/Profiles/Exif/ExifValueTests.cs

2
src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id

@ -1 +1 @@
25b2ab2a0acfb874ca8ac8ae979fc6b1bc9bf466
09f4618eaade2900f7174b9ab400deb0a25bd813

39
src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

@ -488,10 +488,9 @@ namespace ImageProcessorCore.Formats
int componentCount = 3;
// Write the Start Of Image marker.
double densityX = image.HorizontalResolution;
double densityY = image.VerticalResolution;
WriteApplicationHeader((short)image.HorizontalResolution, (short)image.VerticalResolution);
WriteApplicationHeader((short)densityX, (short)densityY);
WriteProfiles(image);
// Write the quantization tables.
this.WriteDQT();
@ -571,6 +570,40 @@ namespace ImageProcessorCore.Formats
this.outputStream.Write(this.buffer, 0, 4);
}
private void WriteProfiles<T, TP>(Image<T, TP> image)
where T : IPackedVector<TP>
where TP : struct
{
WriteProfile(image.ExifProfile);
}
private void WriteProfile(ExifProfile exifProfile)
{
if (exifProfile == null)
{
return;
}
byte[] data = exifProfile.ToByteArray();
if (data == null || data.Length == 0)
{
return;
}
if (data.Length > 65533)
{
throw new ImageFormatException("Exif profile size exceeds limit.");
}
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker
this.buffer[2] = (byte)((data.Length >> 8) & 0xFF);
this.buffer[3] = (byte)(data.Length & 0xFF);
this.outputStream.Write(this.buffer, 0, 4);
this.outputStream.Write(data, 0, data.Length);
}
/// <summary>
/// Writes the Define Quantization Marker and tables.
/// </summary>

306
tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs

@ -0,0 +1,306 @@
// <copyright file="ExifProfileTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Tests
{
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;
public class ExifProfileTests
{
[Fact]
public void Constructor()
{
using (FileStream stream = File.OpenRead(TestImages.Jpg.Calliphora))
{
Image image = new Image(stream);
Assert.Null(image.ExifProfile);
image.ExifProfile = new ExifProfile();
image.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra");
image = WriteAndRead(image);
Assert.NotNull(image.ExifProfile);
Assert.Equal(1, image.ExifProfile.Values.Count());
ExifValue value = image.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright);
TestValue(value, "Dirk Lemstra");
}
}
[Fact]
public void ConstructorEmpty()
{
new ExifProfile((byte[])null);
new ExifProfile(new byte[] { });
}
[Fact]
public void ConstructorCopy()
{
Assert.Throws<ArgumentNullException>(() => { new ExifProfile((ExifProfile)null); });
ExifProfile profile = GetExifProfile();
ExifProfile clone = new ExifProfile(profile);
TestProfile(clone);
profile.SetValue(ExifTag.ColorSpace, (ushort)2);
clone = new ExifProfile(profile);
TestProfile(clone);
}
[Fact]
public void WriteFraction()
{
using (MemoryStream memStream = new MemoryStream())
{
double exposureTime = 1.0 / 1600;
ExifProfile profile = GetExifProfile();
profile.SetValue(ExifTag.ExposureTime, exposureTime);
Image image = new Image(1, 1);
image.ExifProfile = profile;
image.SaveAsJpeg(memStream);
memStream.Position = 0;
image = new Image(memStream);
profile = image.ExifProfile;
Assert.NotNull(profile);
ExifValue value = profile.GetValue(ExifTag.ExposureTime);
Assert.NotNull(value);
Assert.NotEqual(exposureTime, value.Value);
memStream.Position = 0;
profile = GetExifProfile();
profile.SetValue(ExifTag.ExposureTime, exposureTime);
profile.BestPrecision = true;
image.ExifProfile = profile;
image.SaveAsJpeg(memStream);
memStream.Position = 0;
image = new Image(memStream);
profile = image.ExifProfile;
Assert.NotNull(profile);
value = profile.GetValue(ExifTag.ExposureTime);
TestValue(value, exposureTime);
}
}
[Fact]
public void ReadWriteInfinity()
{
using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
{
Image image = new Image(stream);
image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, double.PositiveInfinity);
image = WriteAndRead(image);
ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value);
Assert.Equal(double.PositiveInfinity, value.Value);
image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, double.NegativeInfinity);
image = WriteAndRead(image);
value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value);
Assert.Equal(double.NegativeInfinity, value.Value);
image.ExifProfile.SetValue(ExifTag.FlashEnergy, double.NegativeInfinity);
image = WriteAndRead(image);
value = image.ExifProfile.GetValue(ExifTag.FlashEnergy);
Assert.NotNull(value);
Assert.Equal(double.PositiveInfinity, value.Value);
}
}
[Fact]
public void SetValue()
{
double[] latitude = new double[] { 12.3, 4.56, 789.0 };
using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
{
Image image = new Image(stream);
image.ExifProfile.SetValue(ExifTag.Software, "ImageProcessorCore");
ExifValue value = image.ExifProfile.GetValue(ExifTag.Software);
TestValue(value, "ImageProcessorCore");
Assert.Throws<ArgumentException>(() => { value.Value = 15; });
image.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, 75.55);
value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
TestValue(value, 75.55);
Assert.Throws<ArgumentException>(() => { value.Value = 75; });
image.ExifProfile.SetValue(ExifTag.XResolution, 150.0);
value = image.ExifProfile.GetValue(ExifTag.XResolution);
TestValue(value, 150.0);
Assert.Throws<ArgumentException>(() => { value.Value = "ImageProcessorCore"; });
image.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
value = image.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
TestValue(value, (string)null);
image.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude);
value = image.ExifProfile.GetValue(ExifTag.GPSLatitude);
TestValue(value, latitude);
image = WriteAndRead(image);
Assert.NotNull(image.ExifProfile);
Assert.Equal(17, image.ExifProfile.Values.Count());
value = image.ExifProfile.GetValue(ExifTag.Software);
TestValue(value, "ImageProcessorCore");
value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
TestValue(value, 75.55);
value = image.ExifProfile.GetValue(ExifTag.XResolution);
TestValue(value, 150.0);
value = image.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
Assert.Null(value);
value = image.ExifProfile.GetValue(ExifTag.GPSLatitude);
TestValue(value, latitude);
image.ExifProfile.Parts = ExifParts.ExifTags;
image = WriteAndRead(image);
Assert.NotNull(image.ExifProfile);
Assert.Equal(8, image.ExifProfile.Values.Count());
Assert.NotNull(image.ExifProfile.GetValue(ExifTag.ColorSpace));
Assert.True(image.ExifProfile.RemoveValue(ExifTag.ColorSpace));
Assert.False(image.ExifProfile.RemoveValue(ExifTag.ColorSpace));
Assert.Null(image.ExifProfile.GetValue(ExifTag.ColorSpace));
Assert.Equal(7, image.ExifProfile.Values.Count());
}
}
[Fact]
public void Values()
{
ExifProfile profile = GetExifProfile();
TestProfile(profile);
var thumbnail = profile.CreateThumbnail<Color, uint>();
Assert.NotNull(thumbnail);
Assert.Equal(256, thumbnail.Width);
Assert.Equal(170, thumbnail.Height);
}
[Fact]
public void WriteTooLargeProfile()
{
StringBuilder junk = new StringBuilder();
for (int i = 0; i < 65500; i++)
junk.Append("I");
Image image = new Image(100, 100);
image.ExifProfile = new ExifProfile();
image.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString());
using (MemoryStream memStream = new MemoryStream())
{
Assert.Throws<ImageFormatException>(() => image.SaveAsJpeg(memStream));
}
}
private static ExifProfile GetExifProfile()
{
using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
{
Image image = new Image(stream);
ExifProfile profile = image.ExifProfile;
Assert.NotNull(profile);
return profile;
}
}
private static Image WriteAndRead(Image image)
{
using (MemoryStream memStream = new MemoryStream())
{
image.SaveAsJpeg(memStream);
memStream.Position = 0;
return new Image(memStream);
}
}
private static void TestProfile(ExifProfile profile)
{
Assert.NotNull(profile);
Assert.Equal(16, profile.Values.Count());
foreach (ExifValue value in profile.Values)
{
Assert.NotNull(value.Value);
if (value.Tag == ExifTag.Software)
Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString());
if (value.Tag == ExifTag.XResolution)
Assert.Equal(300.0, value.Value);
if (value.Tag == ExifTag.PixelXDimension)
Assert.Equal(2338U, value.Value);
}
}
private static void TestValue(ExifValue value, string expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.Value);
}
private static void TestValue(ExifValue value, double expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.Value);
}
private static void TestValue(ExifValue value, double[] expected)
{
Assert.NotNull(value);
Assert.Equal(expected, (ICollection)value.Value);
}
}
}

50
tests/ImageProcessorCore.Tests/Profiles/Exif/ExifValueTests.cs

@ -0,0 +1,50 @@
// <copyright file="ExifValueTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Tests
{
using System.IO;
using System.Linq;
using Xunit;
public class ExifValueTests
{
private static ExifValue GetExifValue()
{
using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
{
Image image = new Image(stream);
ExifProfile profile = image.ExifProfile;
Assert.NotNull(profile);
return profile.Values.First();
}
}
[Fact]
public void IEquatable()
{
ExifValue first = GetExifValue();
ExifValue second = GetExifValue();
Assert.True(first == second);
Assert.True(first.Equals(second));
Assert.True(first.Equals((object)second));
}
[Fact]
public void Properties()
{
ExifValue value = GetExifValue();
Assert.Equal(ExifDataType.Ascii, value.DataType);
Assert.Equal(ExifTag.GPSDOP, value.Tag);
Assert.Equal(false, value.IsArray);
Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString());
Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.Value);
}
}
}
Loading…
Cancel
Save