Browse Source

Can now read/write all ICC Profiles

af/merge-core
James Jackson-South 9 years ago
parent
commit
b413b6166a
  1. 9
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 61
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  3. 43
      src/ImageSharp/MetaData/ImageMetaData.cs
  4. 2
      src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
  5. 46
      src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
  6. 2
      src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs
  7. 3
      tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs
  8. 2
      tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs

9
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -1000,7 +1000,14 @@ namespace ImageSharp.Formats
byte[] profile = new byte[remaining];
this.InputProcessor.ReadFull(profile, 0, remaining);
metadata.IccProfiles.Add(new IccProfile(profile));
if (metadata.IccProfile == null)
{
metadata.IccProfile = new IccProfile(profile);
}
else
{
metadata.IccProfile.Extend(profile);
}
}
}

61
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -700,42 +700,60 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Writes the ICC profiles.
/// Writes the ICC profile.
/// </summary>
/// <param name="iccProfiles">The list of ICC profiles.</param>
/// <param name="iccProfile">The ICC profile to write.</param>
/// <exception cref="ImageFormatException">
/// Thrown if any of the ICC profiles size exceeds the limit
/// </exception>
private void WriteICCProfiles(IList<IccProfile> iccProfiles)
private void WriteIccProfile(IccProfile iccProfile)
{
// Just incase someone set the value to null by accident.
if (iccProfiles == null || !iccProfiles.Any())
if (iccProfile == null)
{
return;
}
const int IccOverheadLength = 14;
const int Max = 65533;
int count = iccProfiles.Count;
const int MaxData = Max - IccOverheadLength;
for (int i = 1; i <= count; i++)
byte[] data = iccProfile.ToByteArray();
if (data == null || data.Length == 0)
{
byte[] data = iccProfiles[i - 1]?.ToByteArray();
return;
}
if (data == null || data.Length == 0)
{
continue;
}
// Calculate the number of markers we'll need, rounding up of course
int dataLength = data.Length;
int count = dataLength / MaxData;
if (data.Length > Max)
if (count * MaxData != dataLength)
{
count++;
}
// Per spec, counting starts at 1.
int current = 1;
int offset = 0;
while (dataLength > 0)
{
int length = dataLength; // Number of bytes to write.
if (length > MaxData)
{
throw new ImageFormatException($"ICC profile size exceeds limit. nameof{Max}");
length = MaxData;
}
dataLength -= length;
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker
int length = data.Length + 16;
this.buffer[2] = (byte)((length >> 8) & 0xFF);
this.buffer[3] = (byte)(length & 0xFF);
int markerLength = length + 16;
this.buffer[2] = (byte)((markerLength >> 8) & 0xFF);
this.buffer[3] = (byte)(markerLength & 0xFF);
this.outputStream.Write(this.buffer, 0, 4);
@ -751,11 +769,14 @@ namespace ImageSharp.Formats
this.buffer[9] = (byte)'L';
this.buffer[10] = (byte)'E';
this.buffer[11] = 0x00;
this.buffer[12] = (byte)i; // The position within the collection.
this.buffer[12] = (byte)current; // The position within the collection.
this.buffer[13] = (byte)count; // The total number of profiles.
this.outputStream.Write(this.buffer, 0, 14);
this.outputStream.Write(data, 0, data.Length);
this.outputStream.Write(this.buffer, 0, IccOverheadLength);
this.outputStream.Write(data, offset, length);
current++;
offset += length;
}
}
@ -774,7 +795,7 @@ namespace ImageSharp.Formats
image.MetaData.SyncProfiles();
this.WriteExifProfile(image.MetaData.ExifProfile);
this.WriteICCProfiles(image.MetaData.IccProfiles);
this.WriteIccProfile(image.MetaData.IccProfile);
}
/// <summary>

43
src/ImageSharp/MetaData/ImageMetaData.cs

@ -6,7 +6,6 @@
namespace ImageSharp
{
using System.Collections.Generic;
using System.Linq;
using ImageSharp.Formats;
/// <summary>
@ -61,36 +60,23 @@ namespace ImageSharp
this.Properties.Add(new ImageProperty(property));
}
if (other.ExifProfile != null)
{
this.ExifProfile = new ExifProfile(other.ExifProfile);
}
else
{
this.ExifProfile = null;
}
this.ExifProfile = other.ExifProfile != null
? new ExifProfile(other.ExifProfile)
: null;
if (other.IccProfiles != null && other.IccProfiles.Any())
{
this.IccProfiles = new List<IccProfile>(other.IccProfiles);
}
else
{
this.IccProfiles = new List<IccProfile>();
}
this.IccProfile = other.IccProfile != null
? new IccProfile(other.IccProfile)
: null;
}
/// <summary>
/// Gets or sets the resolution of the image in x- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// Gets or sets the resolution of the image in x- direction.
/// It is defined as the number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in x- direction.</value>
public double HorizontalResolution
{
get
{
return this.horizontalResolution;
}
get => this.horizontalResolution;
set
{
@ -102,16 +88,13 @@ namespace ImageSharp
}
/// <summary>
/// Gets or sets the resolution of the image in y- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// Gets or sets the resolution of the image in y- direction.
/// It is defined as the number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in y- direction.</value>
public double VerticalResolution
{
get
{
return this.verticalResolution;
}
get => this.verticalResolution;
set
{
@ -130,7 +113,7 @@ namespace ImageSharp
/// <summary>
/// Gets or sets the list of ICC profiles.
/// </summary>
public IList<IccProfile> IccProfiles { get; set; } = new List<IccProfile>();
public IccProfile IccProfile { get; set; }
/// <inheritdoc/>
public int FrameDelay { get; set; }

2
src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs

@ -7,6 +7,8 @@ namespace ImageSharp
{
using System.Numerics;
using ImageSharp.Memory;
/// <summary>
/// Provides methods to write ICC data types
/// </summary>

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

@ -5,9 +5,9 @@
namespace ImageSharp
{
using System;
using System.Collections.Generic;
#if !NETSTANDARD1_1
using System;
using System.Security.Cryptography;
#endif
@ -19,7 +19,7 @@ namespace ImageSharp
/// <summary>
/// The byte array to read the ICC profile from
/// </summary>
private readonly byte[] data;
private byte[] data;
/// <summary>
/// The backing file for the <see cref="Entries"/> property
@ -35,7 +35,7 @@ namespace ImageSharp
/// Initializes a new instance of the <see cref="IccProfile"/> class.
/// </summary>
public IccProfile()
: this(null)
: this((byte[])null)
{
}
@ -48,6 +48,20 @@ namespace ImageSharp
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="System.ArgumentNullException"><paramref name="other"/> is null.</exception>>
public IccProfile(IccProfile other)
{
Guard.NotNull(other, nameof(other));
// TODO: Do we need to copy anything else?
this.data = other.data;
}
/// <summary>
/// Initializes a new instance of the <see cref="IccProfile"/> class.
/// </summary>
@ -73,10 +87,7 @@ namespace ImageSharp
return this.header;
}
set
{
this.header = value;
}
set => this.header = value;
}
/// <summary>
@ -106,7 +117,7 @@ namespace ImageSharp
byte[] header = new byte[128];
Buffer.BlockCopy(data, 0, header, 0, 128);
using (MD5 md5 = MD5.Create())
using (var md5 = MD5.Create())
{
// Zero out some values
Array.Clear(header, 44, 4); // Profile flags
@ -117,20 +128,31 @@ namespace ImageSharp
byte[] hash = md5.ComputeHash(data);
// Read values from hash
IccDataReader reader = new IccDataReader(hash);
var reader = new IccDataReader(hash);
return reader.ReadProfileId();
}
}
#endif
/// <summary>
/// Extends the profile with additional data.
/// </summary>
/// <param name="bytes">The array containing addition profile data.</param>
public void Extend(byte[] bytes)
{
int currentLength = this.data.Length;
Array.Resize(ref this.data, currentLength + bytes.Length);
Buffer.BlockCopy(bytes, 0, this.data, currentLength, bytes.Length);
}
/// <summary>
/// Converts this instance to a byte array.
/// </summary>
/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
{
IccWriter writer = new IccWriter();
var writer = new IccWriter();
return writer.Write(this);
}
@ -147,7 +169,7 @@ namespace ImageSharp
return;
}
IccReader reader = new IccReader();
var reader = new IccReader();
this.header = reader.ReadHeader(this.data);
}
@ -164,7 +186,7 @@ namespace ImageSharp
return;
}
IccReader reader = new IccReader();
var reader = new IccReader();
this.entries = new List<IccTagDataEntry>(reader.ReadTagData(this.data));
}
}

2
src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs

@ -8,6 +8,8 @@ namespace ImageSharp
using System;
using System.Linq;
using ImageSharp.Memory;
/// <summary>
/// A matrix element to process data
/// </summary>

3
tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs

@ -6,6 +6,9 @@
namespace ImageSharp.Tests.Icc
{
using System.Numerics;
using ImageSharp.Memory;
using Xunit;
public class IccDataWriterMatrixTests

2
tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs

@ -7,6 +7,8 @@ using System.Numerics;
namespace ImageSharp.Tests
{
using ImageSharp.Memory;
internal static class IccTestDataMatrix
{
#region 2D

Loading…
Cancel
Save