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
{
using System.Collections.Generic;
/// <summary>
/// Reads and parses ICC data from a byte array
/// </summary>
@ -85,9 +87,21 @@ namespace ImageSharp
{
IccTagTableEntry[] tagTable = this.ReadTagTable(reader);
IccTagDataEntry[] entries = new IccTagDataEntry[tagTable.Length];
var store = new Dictionary<uint, IccTagDataEntry>();
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;
}

25
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<IccTagDataEntry> entries)
{
var inData = new List<IccTagDataEntry>(entries);
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>();
IEnumerable<IGrouping<IccTagDataEntry, IccTagDataEntry>> 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<IccTagTableEntry>();
foreach (IGrouping<IccTagDataEntry, IccTagDataEntry> 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));
}
}

16
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();

12
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();

171
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
});
}
}

Loading…
Cancel
Save