Skip to content

Commit a9f8a38

Browse files
authored
Round numbers, add more icc profiles for testing, add ComputeProfileId() and ValidateProfile() methods (#1)
1 parent 1fd9c27 commit a9f8a38

39 files changed

+102
-27
lines changed

IccProfileNet.Tests/D50_XYZ.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public void Header()
4949

5050
Assert.Equal("none", header.ProfileCreatorSignature);
5151

52-
Assert.NotNull(header.ProfileId); // TODO
52+
Assert.NotNull(header.ProfileId);
53+
Assert.False(header.IsProfileIdComputed());
5354
}
5455

5556
[Fact]

IccProfileNet.Tests/D55_XYZ.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public void Header()
4949

5050
Assert.Equal("none", header.ProfileCreatorSignature);
5151

52-
Assert.NotNull(header.ProfileId); // TODO
52+
Assert.NotNull(header.ProfileId);
53+
Assert.False(header.IsProfileIdComputed());
5354
}
5455

5556
[Fact]

IccProfileNet.Tests/D65_XYZ.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public void Header()
4949

5050
Assert.Equal("none", header.ProfileCreatorSignature);
5151

52-
Assert.NotNull(header.ProfileId); // TODO
52+
Assert.NotNull(header.ProfileId);
53+
Assert.False(header.IsProfileIdComputed());
5354
}
5455

5556
[Fact]

IccProfileNet.Tests/ICC_Profile_no_created_date.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ public void Header()
5252

5353
Assert.Equal("appl", header.ProfileCreatorSignature);
5454

55-
Assert.NotNull(header.ProfileId); // TODO
55+
Assert.NotNull(header.ProfileId);
56+
Assert.False(header.IsProfileIdComputed());
5657
}
5758

5859
[Fact]

IccProfileNet.Tests/ICC_v2_LutType.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public void Header()
5151

5252
Assert.Equal("ADBE", header.ProfileCreatorSignature);
5353

54-
Assert.NotNull(header.ProfileId); // TODO
54+
Assert.NotNull(header.ProfileId);
55+
Assert.False(header.IsProfileIdComputed());
5556
}
5657

5758
[Fact]

IccProfileNet.Tests/ISO22028_2_ROMM_RGB.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ public void Header()
4949

5050
Assert.Equal("none", header.ProfileCreatorSignature);
5151

52-
Assert.NotNull(header.ProfileId); // TODO
52+
Assert.NotNull(header.ProfileId);
53+
Assert.True(header.IsProfileIdComputed());
54+
var profileIDCheck = _profile.ComputeProfileId();
55+
Assert.Equal(profileIDCheck, header.ProfileId);
5356
}
5457

5558
[Fact]

IccProfileNet.Tests/PRMG_v201_MR.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ public void Header()
5151

5252
Assert.Equal("KODA", header.ProfileCreatorSignature);
5353

54-
Assert.NotNull(header.ProfileId); // TODO
54+
Assert.NotNull(header.ProfileId);
55+
Assert.True(header.IsProfileIdComputed());
56+
var profileIDCheck = _profile.ComputeProfileId();
57+
Assert.Equal(profileIDCheck, header.ProfileId);
5558
}
5659

5760
[Fact]
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
http://tftcentral.co.uk/articles/icc_profiles
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

IccProfileNet.Tests/sRGB2014.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ public void Header()
5353

5454
Assert.Equal("", header.ProfileCreatorSignature);
5555

56-
Assert.NotNull(header.ProfileId); // TODO
56+
Assert.NotNull(header.ProfileId);
57+
Assert.True(header.IsProfileIdComputed());
58+
var profileIDCheck = _profile.ComputeProfileId();
59+
Assert.Equal(profileIDCheck, header.ProfileId);
5760
}
5861

5962
[Fact]

IccProfileNet.Tests/sRGB_v4_ICC_preference_displayclass.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ public void Header()
5555

5656
Assert.Equal("none", header.ProfileCreatorSignature);
5757

58-
Assert.NotNull(header.ProfileId); // TODO
58+
Assert.NotNull(header.ProfileId);
59+
Assert.True(header.IsProfileIdComputed());
60+
var profileIDCheck = _profile.ComputeProfileId();
61+
Assert.Equal(profileIDCheck, header.ProfileId);
5962
}
6063

6164
[Fact]

IccProfileNet/IccHelper.cs

+36-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace IccProfileNet
66
{
7-
internal static class IccHelper
7+
public static class IccHelper
88
{
99
internal static double[] Lookup(double[] input, double[][] clut, byte[] clutGridPoints)
1010
{
@@ -43,6 +43,36 @@ internal static double[] Lookup(double[] input, double[][] clut, int clutGridPoi
4343
return clut[(int)index];
4444
}
4545

46+
public static byte[] ComputeProfileId(byte[] profileBytes)
47+
{
48+
// Compute profile id
49+
// This field, if not zero (00h), shall hold the Profile ID. The Profile ID shall be calculated using the MD5
50+
// fingerprinting method as defined in Internet RFC 1321.The entire profile, whose length is given by the size field
51+
// in the header, with the profile flags field (bytes 44 to 47, see 7.2.11), rendering intent field (bytes 64 to 67, see
52+
// 7.2.15), and profile ID field (bytes 84 to 99) in the profile header temporarily set to zeros (00h), shall be used to
53+
// calculate the ID. A profile ID field value of zero (00h) shall indicate that a profile ID has not been calculated.
54+
// Profile creators should compute and record a profile ID.
55+
56+
// with the profile flags field (bytes 44 to 47, see 7.2.11)
57+
for (int i = IccProfileHeader.ProfileFlagsOffset; i < IccProfileHeader.ProfileFlagsOffset + IccProfileHeader.ProfileFlagsLength; i++)
58+
{
59+
profileBytes[i] = 0;
60+
}
61+
62+
// rendering intent field (bytes 64 to 67, see 7.2.15)
63+
for (int i = IccProfileHeader.RenderingIntentOffset; i < IccProfileHeader.RenderingIntentOffset + IccProfileHeader.RenderingIntentLength; i++)
64+
{
65+
profileBytes[i] = 0;
66+
}
67+
68+
for (int i = IccProfileHeader.ProfileIdOffset; i < IccProfileHeader.ProfileIdOffset + IccProfileHeader.ProfileIdLength; i++)
69+
{
70+
profileBytes[i] = 0;
71+
}
72+
73+
return System.Security.Cryptography.MD5.HashData(profileBytes);
74+
}
75+
4676
internal static string GetString(byte[] bytes, int index, int count)
4777
{
4878
return Encoding.ASCII.GetString(bytes, index, count).Replace("\0", string.Empty);
@@ -102,22 +132,25 @@ internal static IccXyz ReadXyz(byte[] bytes)
102132

103133
internal static double ReadU8Fixed8Number(byte[] bytes)
104134
{
135+
// TODO - use ReadUInt16 instead of BitConverter.ToInt16 ????
105136
if (BitConverter.IsLittleEndian)
106137
{
107138
Array.Reverse(bytes);
108139
}
109140

110-
return BitConverter.ToInt16(bytes, 0) / ((double)byte.MaxValue + 1);
141+
return Math.Round(BitConverter.ToInt16(bytes, 0) / ((double)byte.MaxValue + 1), 4, MidpointRounding.AwayFromZero);
111142
}
112143

113144
internal static double Reads15Fixed16Number(byte[] bytes)
114145
{
146+
// TODO - use ReadUInt32 instead of BitConverter.ToInt32 ????
147+
115148
if (BitConverter.IsLittleEndian)
116149
{
117150
Array.Reverse(bytes);
118151
}
119152

120-
return (BitConverter.ToInt32(bytes) - 0.5f) / ((double)ushort.MaxValue + 1);
153+
return Math.Round((BitConverter.ToInt32(bytes) - 0.5f) / ((double)ushort.MaxValue + 1), 4, MidpointRounding.AwayFromZero);
121154
}
122155

123156
internal static double[] Reads15Fixed16Array(byte[] bytes)

IccProfileNet/IccProfile.cs

+33
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,39 @@ public IccProfile(byte[] data)
4141
_tags = new Lazy<IReadOnlyDictionary<string, IccTagTypeBase>>(() => GetTags());
4242
}
4343

44+
public byte[] GetOrComputeProfileId()
45+
{
46+
if (Header.IsProfileIdComputed())
47+
{
48+
return Header.ProfileId;
49+
}
50+
51+
return ComputeProfileId();
52+
}
53+
54+
public byte[] ComputeProfileId()
55+
{
56+
return IccHelper.ComputeProfileId(Data);
57+
}
58+
59+
/// <summary>
60+
/// Validates the profile against the profile id.
61+
/// </summary>
62+
/// <returns>
63+
/// <c>true</c> if the profile id contained in the header matches the re-computed profile id.
64+
/// <c>false</c> if they don't match.
65+
/// <c>null</c> if the profile id is not set in the header.
66+
/// </returns>
67+
public bool? ValidateProfile()
68+
{
69+
if (Header.IsProfileIdComputed())
70+
{
71+
return null;
72+
}
73+
74+
return Header.ProfileId.SequenceEqual(ComputeProfileId());
75+
}
76+
4477
private static IccTagTableItem[] ParseTagTable(byte[] bytes)
4578
{
4679
// Tag count (n)

IccProfileNet/IccProfileHeader.cs

+6-15
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ public struct IccProfileHeader
159159
/// </summary>
160160
public byte[] ProfileId => _profileId.Value;
161161

162+
public bool IsProfileIdComputed()
163+
{
164+
return !ProfileId.All(v => v == 0);
165+
}
166+
162167
public IccProfileHeader(byte[] profile)
163168
{
164169
// Profile version number
@@ -299,21 +304,7 @@ public IccProfileHeader(byte[] profile)
299304
{
300305
// Profile ID
301306
// 84 to 99
302-
byte[] profileId = profile.Skip(ProfileIdOffset).Take(ProfileIdLength).ToArray();
303-
304-
if (profileId.All(b => b == 0))
305-
{
306-
// Compute profile id
307-
// This field, if not zero (00h), shall hold the Profile ID. The Profile ID shall be calculated using the MD5
308-
// fingerprinting method as defined in Internet RFC 1321.The entire profile, whose length is given by the size field
309-
// in the header, with the profile flags field (bytes 44 to 47, see 7.2.11), rendering intent field (bytes 64 to 67, see
310-
// 7.2.15), and profile ID field (bytes 84 to 99) in the profile header temporarily set to zeros (00h), shall be used to
311-
// calculate the ID. A profile ID field value of zero (00h) shall indicate that a profile ID has not been calculated.
312-
// Profile creators should compute and record a profile ID.
313-
314-
profileId = System.Security.Cryptography.MD5.HashData(profile);
315-
}
316-
return profileId;
307+
return profile.Skip(ProfileIdOffset).Take(ProfileIdLength).ToArray();
317308
});
318309
}
319310

0 commit comments

Comments
 (0)