diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index d7f556a81..b24c96f02 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using System.Collections.Generic; + /// /// Reads and parses ICC data from a byte array /// @@ -85,9 +87,21 @@ namespace ImageSharp { IccTagTableEntry[] tagTable = this.ReadTagTable(reader); IccTagDataEntry[] entries = new IccTagDataEntry[tagTable.Length]; + var store = new Dictionary(); 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; entries[i] = entry; } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index b4e5f2868..19c00e8f5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System; using System.Collections.Generic; using System.Linq; @@ -76,30 +77,18 @@ namespace ImageSharp private IccTagTableEntry[] WriteTagData(IccDataWriter writer, List entries) { - var inData = new List(entries); - var dupData = new List(); - - 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(); + IEnumerable> grouped = entries.GroupBy(t => t); // (Header size) + (entry count) + (nr of entries) * (size of table entry) writer.SetIndex(128 + 4 + (entries.Count * 12)); - foreach (IccTagDataEntry[] entry in dupData) + var table = new List(); + foreach (IGrouping group in grouped) { - writer.WriteTagDataEntry(entry[0], out IccTagTableEntry tentry); - foreach (IccTagDataEntry item in entry) + writer.WriteTagDataEntry(group.Key, out IccTagTableEntry tableEntry); + 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)); } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index 0db64c47f..34aa24fa6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -10,10 +10,10 @@ namespace ImageSharp.Tests.Icc public class IccReaderTests { [Fact] - public void ReadProfile() + public void ReadProfile_NoEntries() { IccReader reader = CreateReader(); - + IccProfile output = reader.Read(IccTestDataProfiles.Header_Random_Array); Assert.Equal(0, output.Entries.Count); @@ -40,6 +40,18 @@ namespace ImageSharp.Tests.Icc 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() { return new IccReader(); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs index 6192e6eae..7e3f8c0c9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Tests.Icc public class IccWriterTests { [Fact] - public void WriteProfile() + public void WriteProfile_NoEntries() { IccWriter writer = CreateWriter(); @@ -23,6 +23,16 @@ namespace ImageSharp.Tests.Icc 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() { return new IccWriter(); diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 20fff50a8..32a4a8e57 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -10,83 +10,124 @@ namespace ImageSharp.Tests { 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, - CmmType = "abcd", - CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), - CreatorSignature = "dcba", - DataColorSpace = IccColorSpaceType.Rgb, - 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), - }; +#if !NETSTANDARD1_1 + 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, +#else + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }); - public static readonly IccProfileHeader Header_Random_Read = new IccProfileHeader + public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature) { - Class = IccProfileClass.DisplayDevice, - CmmType = "abcd", - CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), - CreatorSignature = "dcba", - DataColorSpace = IccColorSpaceType.Rgb, - DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, - DeviceManufacturer = 123456789u, - DeviceModel = 987654321u, - FileSignature = "acsp", - Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, + return new IccProfileHeader + { + Class = IccProfileClass.DisplayDevice, + CmmType = "abcd", + CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), + CreatorSignature = "dcba", + DataColorSpace = IccColorSpaceType.Rgb, + DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, + DeviceManufacturer = 123456789u, + DeviceModel = 987654321u, + FileSignature = "acsp", + Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, #if !NETSTANDARD1_1 - Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), + Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), #else Id = IccProfileId.Zero, #endif - PcsIlluminant = new Vector3(4, 5, 6), - PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, - ProfileConnectionSpace = IccColorSpaceType.CieXyz, - RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, - Size = 132, - Version = new Version(4, 3, 0), - }; + PcsIlluminant = new Vector3(4, 5, 6), + PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, + ProfileConnectionSpace = IccColorSpaceType.CieXyz, + RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, + Size = size, + 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) - 0x61, 0x62, 0x63, 0x64, // CmmType - 0x04, 0x30, 0x00, 0x00, // Version - 0x6D, 0x6E, 0x74, 0x72, // Class - 0x52, 0x47, 0x42, 0x20, // DataColorSpace - 0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace - 0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate - 0x61, 0x63, 0x73, 0x70, // FileSignature - 0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature - 0x00, 0x00, 0x00, 0x01, // Flags - 0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer - 0x3A, 0xDE, 0x68, 0xB1, // DeviceModel - 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 + return ArrayHelper.Concat( + new byte[] + { + (byte)(size >> 24), (byte)(size >> 16), (byte)(size >> 8), (byte)size, // Size + 0x61, 0x62, 0x63, 0x64, // CmmType + 0x04, 0x30, 0x00, 0x00, // Version + 0x6D, 0x6E, 0x74, 0x72, // Class + 0x52, 0x47, 0x42, 0x20, // DataColorSpace + 0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace + 0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate + 0x61, 0x63, 0x73, 0x70, // FileSignature + 0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature + 0x00, 0x00, 0x00, 0x01, // Flags + 0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer + 0x3A, 0xDE, 0x68, 0xB1, // DeviceModel + 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 - 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, // Id + new IccProfileId(0xA9718FC1, 0x1E2D641B, 0x10F47D6A, 0x5BF6ACB9), #else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Id + IccProfileId.Zero, #endif - // 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) - 0x00, 0x00, 0x00, 0x00, - }; + "acsp"), + new IccTagDataEntry[] + { + IccTestDataTagDataEntry.Unknown_Val, + IccTestDataTagDataEntry.Unknown_Val + }); } }