Browse Source

Merge pull request #249 from JBildstein/iccreadwrite-improve

Read and write duplicate ICC tag data entries only once
af/merge-core
James Jackson-South 9 years ago
committed by GitHub
parent
commit
c8579dc359
  1. 16
      src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs
  2. 25
      src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs
  3. 16
      tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs
  4. 12
      tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs
  5. 171
      tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs

16
src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs

@ -5,6 +5,8 @@
namespace ImageSharp namespace ImageSharp
{ {
using System.Collections.Generic;
/// <summary> /// <summary>
/// Reads and parses ICC data from a byte array /// Reads and parses ICC data from a byte array
/// </summary> /// </summary>
@ -85,9 +87,21 @@ namespace ImageSharp
{ {
IccTagTableEntry[] tagTable = this.ReadTagTable(reader); IccTagTableEntry[] tagTable = this.ReadTagTable(reader);
IccTagDataEntry[] entries = new IccTagDataEntry[tagTable.Length]; IccTagDataEntry[] entries = new IccTagDataEntry[tagTable.Length];
var store = new Dictionary<uint, IccTagDataEntry>();
for (int i = 0; i < tagTable.Length; i++) for (int i = 0; i < tagTable.Length; i++)
{ {
IccTagDataEntry entry = reader.ReadTagDataEntry(tagTable[i]); IccTagDataEntry entry;
uint offset = tagTable[i].Offset;
if (store.ContainsKey(offset))
{
entry = store[offset];
}
else
{
entry = reader.ReadTagDataEntry(tagTable[i]);
store.Add(offset, entry);
}
entry.TagSignature = tagTable[i].Signature; entry.TagSignature = tagTable[i].Signature;
entries[i] = entry; entries[i] = entry;
} }

25
src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs

@ -5,6 +5,7 @@
namespace ImageSharp namespace ImageSharp
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -76,30 +77,18 @@ namespace ImageSharp
private IccTagTableEntry[] WriteTagData(IccDataWriter writer, List<IccTagDataEntry> entries) private IccTagTableEntry[] WriteTagData(IccDataWriter writer, List<IccTagDataEntry> entries)
{ {
var inData = new List<IccTagDataEntry>(entries); IEnumerable<IGrouping<IccTagDataEntry, IccTagDataEntry>> grouped = entries.GroupBy(t => t);
var dupData = new List<IccTagDataEntry[]>();
while (inData.Count > 0)
{
IccTagDataEntry[] items = inData.Where(t => inData[0].Equals(t)).ToArray();
dupData.Add(items);
foreach (IccTagDataEntry item in items)
{
inData.Remove(item);
}
}
var table = new List<IccTagTableEntry>();
// (Header size) + (entry count) + (nr of entries) * (size of table entry) // (Header size) + (entry count) + (nr of entries) * (size of table entry)
writer.SetIndex(128 + 4 + (entries.Count * 12)); writer.SetIndex(128 + 4 + (entries.Count * 12));
foreach (IccTagDataEntry[] entry in dupData) var table = new List<IccTagTableEntry>();
foreach (IGrouping<IccTagDataEntry, IccTagDataEntry> group in grouped)
{ {
writer.WriteTagDataEntry(entry[0], out IccTagTableEntry tentry); writer.WriteTagDataEntry(group.Key, out IccTagTableEntry tableEntry);
foreach (IccTagDataEntry item in entry) foreach (IccTagDataEntry item in group)
{ {
table.Add(new IccTagTableEntry(item.TagSignature, tentry.Offset, tentry.DataSize)); table.Add(new IccTagTableEntry(item.TagSignature, tableEntry.Offset, tableEntry.DataSize));
} }
} }

16
tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs

@ -10,10 +10,10 @@ namespace ImageSharp.Tests.Icc
public class IccReaderTests public class IccReaderTests
{ {
[Fact] [Fact]
public void ReadProfile() public void ReadProfile_NoEntries()
{ {
IccReader reader = CreateReader(); IccReader reader = CreateReader();
IccProfile output = reader.Read(IccTestDataProfiles.Header_Random_Array); IccProfile output = reader.Read(IccTestDataProfiles.Header_Random_Array);
Assert.Equal(0, output.Entries.Count); Assert.Equal(0, output.Entries.Count);
@ -40,6 +40,18 @@ namespace ImageSharp.Tests.Icc
Assert.Equal(header.Version, expected.Version); Assert.Equal(header.Version, expected.Version);
} }
[Fact]
public void ReadProfile_DuplicateEntry()
{
IccReader reader = CreateReader();
IccProfile output = reader.Read(IccTestDataProfiles.Profile_Random_Array);
Assert.Equal(2, output.Entries.Count);
Assert.True(ReferenceEquals(output.Entries[0], output.Entries[1]));
}
private IccReader CreateReader() private IccReader CreateReader()
{ {
return new IccReader(); return new IccReader();

12
tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs

@ -10,7 +10,7 @@ namespace ImageSharp.Tests.Icc
public class IccWriterTests public class IccWriterTests
{ {
[Fact] [Fact]
public void WriteProfile() public void WriteProfile_NoEntries()
{ {
IccWriter writer = CreateWriter(); IccWriter writer = CreateWriter();
@ -23,6 +23,16 @@ namespace ImageSharp.Tests.Icc
Assert.Equal(IccTestDataProfiles.Header_Random_Array, output); Assert.Equal(IccTestDataProfiles.Header_Random_Array, output);
} }
[Fact]
public void WriteProfile_DuplicateEntry()
{
IccWriter writer = CreateWriter();
byte[] output = writer.Write(IccTestDataProfiles.Profile_Random_Val);
Assert.Equal(IccTestDataProfiles.Profile_Random_Array, output);
}
private IccWriter CreateWriter() private IccWriter CreateWriter()
{ {
return new IccWriter(); return new IccWriter();

171
tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs

@ -10,83 +10,124 @@ namespace ImageSharp.Tests
{ {
internal static class IccTestDataProfiles internal static class IccTestDataProfiles
{ {
public static readonly IccProfileHeader Header_Random_Write = new IccProfileHeader public static readonly IccProfileHeader Header_Random_Write = CreateHeaderRandomValue(
562, // should be overwritten
new IccProfileId(1, 2, 3, 4), // should be overwritten
"ijkl"); // should be overwritten to "acsp"
public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132,
#if !NETSTANDARD1_1
new IccProfileId(2931428592, 418415738, 3086756963, 2237536530),
#else
IccProfileId.Zero,
#endif
"acsp");
public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, new byte[]
{ {
Class = IccProfileClass.DisplayDevice, #if !NETSTANDARD1_1
CmmType = "abcd", 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12,
CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), #else
CreatorSignature = "dcba", 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DataColorSpace = IccColorSpaceType.Rgb, #endif
DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, });
DeviceManufacturer = 123456789u,
DeviceModel = 987654321u,
FileSignature = "ijkl", // should be overwritten to "acsp"
Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent,
Id = new IccProfileId(1, 2, 3, 4), // should be overwritten
PcsIlluminant = new Vector3(4, 5, 6),
PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation,
ProfileConnectionSpace = IccColorSpaceType.CieXyz,
RenderingIntent = IccRenderingIntent.AbsoluteColorimetric,
Size = 562, // should be overwritten
Version = new Version(4, 3, 0),
};
public static readonly IccProfileHeader Header_Random_Read = new IccProfileHeader public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature)
{ {
Class = IccProfileClass.DisplayDevice, return new IccProfileHeader
CmmType = "abcd", {
CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), Class = IccProfileClass.DisplayDevice,
CreatorSignature = "dcba", CmmType = "abcd",
DataColorSpace = IccColorSpaceType.Rgb, CreationDate = new DateTime(1990, 11, 26, 7, 21, 42),
DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, CreatorSignature = "dcba",
DeviceManufacturer = 123456789u, DataColorSpace = IccColorSpaceType.Rgb,
DeviceModel = 987654321u, DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent,
FileSignature = "acsp", DeviceManufacturer = 123456789u,
Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, DeviceModel = 987654321u,
FileSignature = "acsp",
Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent,
#if !NETSTANDARD1_1 #if !NETSTANDARD1_1
Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530),
#else #else
Id = IccProfileId.Zero, Id = IccProfileId.Zero,
#endif #endif
PcsIlluminant = new Vector3(4, 5, 6), PcsIlluminant = new Vector3(4, 5, 6),
PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation,
ProfileConnectionSpace = IccColorSpaceType.CieXyz, ProfileConnectionSpace = IccColorSpaceType.CieXyz,
RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, RenderingIntent = IccRenderingIntent.AbsoluteColorimetric,
Size = 132, Size = size,
Version = new Version(4, 3, 0), Version = new Version(4, 3, 0),
}; };
}
public static readonly byte[] Header_Random_Array = public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] profileId)
{ {
0x00, 0x00, 0x00, 0x84, // Size (132) return ArrayHelper.Concat(
0x61, 0x62, 0x63, 0x64, // CmmType new byte[]
0x04, 0x30, 0x00, 0x00, // Version {
0x6D, 0x6E, 0x74, 0x72, // Class (byte)(size >> 24), (byte)(size >> 16), (byte)(size >> 8), (byte)size, // Size
0x52, 0x47, 0x42, 0x20, // DataColorSpace 0x61, 0x62, 0x63, 0x64, // CmmType
0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace 0x04, 0x30, 0x00, 0x00, // Version
0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate 0x6D, 0x6E, 0x74, 0x72, // Class
0x61, 0x63, 0x73, 0x70, // FileSignature 0x52, 0x47, 0x42, 0x20, // DataColorSpace
0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature 0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace
0x00, 0x00, 0x00, 0x01, // Flags 0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate
0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer 0x61, 0x63, 0x73, 0x70, // FileSignature
0x3A, 0xDE, 0x68, 0xB1, // DeviceModel 0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // DeviceAttributes 0x00, 0x00, 0x00, 0x01, // Flags
0x00, 0x00, 0x00, 0x03, // RenderingIntent 0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer
0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // PcsIlluminant 0x3A, 0xDE, 0x68, 0xB1, // DeviceModel
0x64, 0x63, 0x62, 0x61, // CreatorSignature 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // DeviceAttributes
0x00, 0x00, 0x00, 0x03, // RenderingIntent
0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // PcsIlluminant
0x64, 0x63, 0x62, 0x61, // CreatorSignature
},
profileId,
new byte[]
{
// Padding
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
// Nr of tag table entries (0)
(byte)(nrOfEntries >> 24), (byte)(nrOfEntries >> 16), (byte)(nrOfEntries >> 8), (byte)nrOfEntries
});
}
public static byte[] Profile_Random_Array = ArrayHelper.Concat(CreateHeaderRandomArray(168, 2, new byte[]
{
#if !NETSTANDARD1_1
0xA9, 0x71, 0x8F, 0xC1, 0x1E, 0x2D, 0x64, 0x1B, 0x10, 0xF4, 0x7D, 0x6A, 0x5B, 0xF6, 0xAC, 0xB9
#else
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#endif
}),
new byte[]
{
0x00, 0x00, 0x00, 0x00, // tag signature (Unknown)
0x00, 0x00, 0x00, 0x9C, // tag offset (156)
0x00, 0x00, 0x00, 0x0C, // tag size (12)
0x00, 0x00, 0x00, 0x00, // tag signature (Unknown)
0x00, 0x00, 0x00, 0x9C, // tag offset (156)
0x00, 0x00, 0x00, 0x0C, // tag size (12)
},
IccTestDataTagDataEntry.TagDataEntryHeader_UnknownArr,
IccTestDataTagDataEntry.Unknown_Arr
);
public static IccProfile Profile_Random_Val = new IccProfile(CreateHeaderRandomValue(168,
#if !NETSTANDARD1_1 #if !NETSTANDARD1_1
0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, // Id new IccProfileId(0xA9718FC1, 0x1E2D641B, 0x10F47D6A, 0x5BF6ACB9),
#else #else
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Id IccProfileId.Zero,
#endif #endif
// Padding "acsp"),
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, new IccTagDataEntry[]
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, IccTestDataTagDataEntry.Unknown_Val,
0x00, 0x00, 0x00, 0x00, IccTestDataTagDataEntry.Unknown_Val
// Nr of tag table entries (0) });
0x00, 0x00, 0x00, 0x00,
};
} }
} }

Loading…
Cancel
Save