diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs index 9769db3bc3..4828deef70 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs @@ -15,8 +15,7 @@ internal class ClutCalculator : IVector4Calculator { private readonly int inputCount; private readonly int outputCount; - private readonly float[][] lut; - private readonly float[] lutFlat; + private readonly float[] lut; private readonly byte[] gridPointCount; private readonly byte[] maxGridPoint; private readonly int[] indexFactor; @@ -69,17 +68,6 @@ internal class ClutCalculator : IVector4Calculator } this.indexFactor = this.CalculateIndexFactor(); - - // TODO: Using here a flat array instead of a jagged array to match the reference implementation. - // Maybe consider changing the clut values from jagged to a flat in IccClut to avoid this allocation. - this.lutFlat = new float[this.lut.Length * 3]; - int offset = 0; - for (int i = 0; i < this.lut.Length; i++) - { - this.lutFlat[offset++] = this.lut[i][0]; - this.lutFlat[offset++] = this.lut[i][1]; - this.lutFlat[offset++] = this.lut[i][2]; - } } public unsafe Vector4 Calculate(Vector4 value) @@ -209,7 +197,7 @@ internal class ClutCalculator : IVector4Calculator float nu = (float)(1.0 - u); int i; - Span p = this.lutFlat.AsSpan((int)(ix * this.n001)); + Span p = this.lut.AsSpan((int)(ix * this.n001)); // Normalize grid units. float dF0 = nu; @@ -259,7 +247,7 @@ internal class ClutCalculator : IVector4Calculator float nu = (float)(1.0 - u); int i; - Span p = this.lutFlat.AsSpan((int)((ix * this.n001) + (iy * this.n010))); + Span p = this.lut.AsSpan((int)((ix * this.n001) + (iy * this.n010))); // Normalize grid units. float dF0 = nt * nu; @@ -322,7 +310,7 @@ internal class ClutCalculator : IVector4Calculator float nt = (float)(1.0 - t); float nu = (float)(1.0 - u); - Span p = this.lutFlat.AsSpan((int)((ix * this.n001) + (iy * this.n010) + (iz * this.n100))); + Span p = this.lut.AsSpan((int)((ix * this.n001) + (iy * this.n010) + (iz * this.n100))); // Normalize grid units float dF0 = ns * nt * nu; @@ -401,7 +389,7 @@ internal class ClutCalculator : IVector4Calculator float nu = (float)(1.0 - u); float nv = (float)(1.0 - v); - Span p = this.lutFlat.AsSpan((int)((iw * this.n001) + (ix * this.n010) + (iy * this.n100) + (iz * this.n1000))); + Span p = this.lut.AsSpan((int)((iw * this.n001) + (ix * this.n010) + (iy * this.n100) + (iz * this.n1000))); // Normalize grid units. float[] dF = new float[16]; @@ -458,7 +446,7 @@ internal class ClutCalculator : IVector4Calculator index += (int)this.ig[i] * this.dimSize[i]; } - Span p = this.lutFlat.AsSpan(index); + Span p = this.lut.AsSpan(index); float[] temp = new float[2]; bool nFlag = false; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs index 9d2390ac79..1a09390247 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs @@ -13,11 +13,11 @@ internal abstract partial class IccConverterBase private IVector4Calculator calculator; /// - /// Checks the profile for available conversion methods and gathers all the informations necessary for it + /// Checks the profile for available conversion methods and gathers all the information's necessary for it. /// - /// The profile to use for the conversion - /// True if the conversion is to the Profile Connection Space - /// The wanted rendering intent. Can be ignored if not available + /// The profile to use for the conversion. + /// True if the conversion is to the Profile Connection Space. + /// The wanted rendering intent. Can be ignored if not available. /// Invalid conversion method. protected void Init(IccProfile profile, bool toPcs, IccRenderingIntent renderingIntent) => this.calculator = GetConversionMethod(profile, renderingIntent) switch diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs index e88dd8d9e1..f16b4d1d17 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -4,24 +4,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to read ICC data types +/// Provides methods to read ICC data types. /// internal sealed partial class IccDataReader { /// - /// Reads an 8bit lookup table + /// Reads an 8bit lookup table. /// - /// The read LUT - public IccLut ReadLut8() - { - return new IccLut(this.ReadBytes(256)); - } + /// The read LUT. + public IccLut ReadLut8() => new(this.ReadBytes(256)); /// - /// Reads a 16bit lookup table + /// Reads a 16bit lookup table. /// - /// The number of entries - /// The read LUT + /// The number of entries. + /// The read LUT. public IccLut ReadLut16(int count) { var values = new ushort[count]; @@ -34,16 +31,16 @@ internal sealed partial class IccDataReader } /// - /// Reads a CLUT depending on type + /// Reads a CLUT depending on type. /// - /// Input channel count - /// Output channel count + /// Input channel count. + /// Output channel count. /// If true, it's read as CLUTf32, - /// else read as either CLUT8 or CLUT16 depending on embedded information - /// The read CLUT + /// else read as either CLUT8 or CLUT16 depending on embedded information. + /// The read CLUT. public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat) { - // Grid-points are always 16 bytes long but only 0-inChCount are used + // Grid-points are always 16 bytes long but only 0-inChCount are used. var gridPointCount = new byte[inChannelCount]; Buffer.BlockCopy(this.data, this.AddIndex(16), gridPointCount, 0, inChannelCount); @@ -67,12 +64,12 @@ internal sealed partial class IccDataReader } /// - /// Reads an 8 bit CLUT + /// Reads an 8 bit CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUT8 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUT8. public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int start = this.currentIndex; @@ -82,31 +79,25 @@ internal sealed partial class IccDataReader length += (int)Math.Pow(gridPointCount[i], inChannelCount); } - length /= inChannelCount; - const float Max = byte.MaxValue; - var values = new float[length][]; + var values = new float[length]; for (int i = 0; i < length; i++) { - values[i] = new float[outChannelCount]; - for (int j = 0; j < outChannelCount; j++) - { - values[i][j] = this.data[this.currentIndex++] / Max; - } + values[i] = this.data[this.currentIndex++] / Max; } - this.currentIndex = start + (length * outChannelCount); - return new IccClut(values, gridPointCount, IccClutDataType.UInt8); + this.currentIndex = start + length; + return new IccClut(values, gridPointCount, IccClutDataType.UInt8, outChannelCount); } /// - /// Reads a 16 bit CLUT + /// Reads a 16 bit CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUT16 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUT16. public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int start = this.currentIndex; @@ -116,31 +107,25 @@ internal sealed partial class IccDataReader length += (int)Math.Pow(gridPointCount[i], inChannelCount); } - length /= inChannelCount; - const float Max = ushort.MaxValue; - var values = new float[length][]; + var values = new float[length]; for (int i = 0; i < length; i++) { - values[i] = new float[outChannelCount]; - for (int j = 0; j < outChannelCount; j++) - { - values[i][j] = this.ReadUInt16() / Max; - } + values[i] = this.ReadUInt16() / Max; } - this.currentIndex = start + (length * outChannelCount * 2); - return new IccClut(values, gridPointCount, IccClutDataType.UInt16); + this.currentIndex = start + (length * 2); + return new IccClut(values, gridPointCount, IccClutDataType.UInt16, outChannelCount); } /// - /// Reads a 32bit floating point CLUT + /// Reads a 32bit floating point CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUTf32 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUTf32. public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount) { int start = this.currentIndex; @@ -150,19 +135,13 @@ internal sealed partial class IccDataReader length += (int)Math.Pow(gridPointCount[i], inChCount); } - length /= inChCount; - - var values = new float[length][]; + var values = new float[length]; for (int i = 0; i < length; i++) { - values[i] = new float[outChCount]; - for (int j = 0; j < outChCount; j++) - { - values[i][j] = this.ReadSingle(); - } + values[i] = this.ReadSingle(); } - this.currentIndex = start + (length * outChCount * 4); - return new IccClut(values, gridPointCount, IccClutDataType.Float); + this.currentIndex = start + (length * 4); + return new IccClut(values, gridPointCount, IccClutDataType.Float, outChCount); } } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index a830aa98c0..559f77609a 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -8,15 +8,15 @@ using System.Numerics; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to read ICC data types +/// Provides methods to read ICC data types. /// internal sealed partial class IccDataReader { /// - /// Reads a tag data entry + /// Reads a tag data entry. /// - /// The table entry with reading information - /// the tag data entry + /// The table entry with reading information. + /// The tag data entry. public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info) { this.currentIndex = (int)info.Offset; @@ -101,7 +101,7 @@ internal sealed partial class IccDataReader /// /// Reads the header of a /// - /// The read signature + /// The read signature. public IccTypeSignature ReadTagDataEntryHeader() { IccTypeSignature type = (IccTypeSignature)this.ReadUInt32(); @@ -112,7 +112,7 @@ internal sealed partial class IccDataReader /// /// Reads the header of a and checks if it's the expected value /// - /// expected value to check against + /// The expected value to check against. public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) { IccTypeSignature type = this.ReadTagDataEntryHeader(); @@ -125,8 +125,8 @@ internal sealed partial class IccDataReader /// /// Reads a with an unknown /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) { int count = (int)size - 8; // 8 is the tag header size @@ -136,7 +136,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() { ushort channelCount = this.ReadUInt16(); @@ -150,7 +150,7 @@ internal sealed partial class IccDataReader } else { - // The type is not know, so the values need be read + // The type is not know, so the values need be read. double[][] values = new double[channelCount][]; for (int i = 0; i < channelCount; i++) { @@ -164,7 +164,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() { uint colorantCount = this.ReadUInt32(); @@ -175,7 +175,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() { uint colorantCount = this.ReadUInt32(); @@ -191,7 +191,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccCurveTagDataEntry ReadCurveTagDataEntry() { uint pointCount = this.ReadUInt32(); @@ -220,7 +220,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes + /// The size of the entry in bytes. /// The read entry public IccDataTagDataEntry ReadDataTagDataEntry(uint size) { @@ -238,16 +238,13 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry - public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() - { - return new IccDateTimeTagDataEntry(this.ReadDateTime()); - } + /// The read entry. + public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() => new IccDateTimeTagDataEntry(this.ReadDateTime()); /// /// Reads a /// - /// The read entry + /// The read entry. public IccLut16TagDataEntry ReadLut16TagDataEntry() { byte inChCount = this.data[this.AddIndex(1)]; @@ -285,7 +282,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccLut8TagDataEntry ReadLut8TagDataEntry() { byte inChCount = this.data[this.AddIndex(1)]; @@ -320,7 +317,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -379,7 +376,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -438,21 +435,18 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry - public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() - { - return new IccMeasurementTagDataEntry( + /// The read entry. + public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() => new( observer: (IccStandardObserver)this.ReadUInt32(), xyzBacking: this.ReadXyzNumber(), geometry: (IccMeasurementGeometry)this.ReadUInt32(), flare: this.ReadUFix16(), illuminant: (IccStandardIlluminant)this.ReadUInt32()); - } /// /// Reads a /// - /// The read entry + /// The read entry. public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -517,7 +511,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry() { int start = this.currentIndex - 8; @@ -545,7 +539,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() { int vendorFlag = this.ReadInt32(); @@ -567,15 +561,12 @@ internal sealed partial class IccDataReader /// Reads a /// /// The read entry - public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() - { - return new IccParametricCurveTagDataEntry(this.ReadParametricCurve()); - } + public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() => new(this.ReadParametricCurve()); /// /// Reads a /// - /// The read entry + /// The read entry. public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() { uint count = this.ReadUInt32(); @@ -591,7 +582,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -618,7 +609,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -644,8 +635,8 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -661,27 +652,21 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry - public IccSignatureTagDataEntry ReadSignatureTagDataEntry() - { - return new IccSignatureTagDataEntry(this.ReadAsciiString(4)); - } + /// The read entry. + public IccSignatureTagDataEntry ReadSignatureTagDataEntry() => new(this.ReadAsciiString(4)); /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry - public IccTextTagDataEntry ReadTextTagDataEntry(uint size) - { - return new IccTextTagDataEntry(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size - } + /// The size of the entry in bytes. + /// The read entry. + public IccTextTagDataEntry ReadTextTagDataEntry(uint size) => new(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -697,8 +682,8 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 2; @@ -714,8 +699,8 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -731,8 +716,8 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) { uint count = (size - 8) / 8; @@ -748,8 +733,8 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) { int count = (int)size - 8; // 8 is the tag header size @@ -761,20 +746,17 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry - public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() - { - return new IccViewingConditionsTagDataEntry( + /// The read entry. + public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() => new( illuminantXyz: this.ReadXyzNumber(), surroundXyz: this.ReadXyzNumber(), illuminant: (IccStandardIlluminant)this.ReadUInt32()); - } /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) { uint count = (size - 8) / 12; @@ -790,7 +772,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() { string unicodeValue, scriptcodeValue; @@ -830,7 +812,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry() { uint productNameCount = this.ReadUInt32(); @@ -854,7 +836,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The read entry + /// The read entry. public IccScreeningTagDataEntry ReadScreeningTagDataEntry() { var flags = (IccScreeningFlag)this.ReadInt32(); @@ -871,7 +853,7 @@ internal sealed partial class IccDataReader /// /// Reads a /// - /// The size of the entry in bytes + /// The size of the entry in bytes. /// The read entry public IccUcrBgTagDataEntry ReadUcrBgTagDataEntry(uint size) { diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs index 703a3896bb..29394c0820 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs @@ -4,15 +4,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to write ICC data types +/// Provides methods to write ICC data types. /// internal sealed partial class IccDataWriter { /// - /// Writes an 8bit lookup table + /// Writes an 8bit lookup table. /// - /// The LUT to write - /// The number of bytes written + /// The LUT to write. + /// The number of bytes written. public int WriteLut8(IccLut value) { foreach (float item in value.Values) @@ -24,10 +24,10 @@ internal sealed partial class IccDataWriter } /// - /// Writes an 16bit lookup table + /// Writes an 16bit lookup table. /// - /// The LUT to write - /// The number of bytes written + /// The LUT to write. + /// The number of bytes written. public int WriteLut16(IccLut value) { foreach (float item in value.Values) @@ -39,10 +39,10 @@ internal sealed partial class IccDataWriter } /// - /// Writes an color lookup table + /// Writes an color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut(IccClut value) { int count = this.WriteArray(value.GridPointCount); @@ -67,57 +67,48 @@ internal sealed partial class IccDataWriter } /// - /// Writes a 8bit color lookup table + /// Writes a 8bit color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut8(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue)); - } + count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue)); } return count; } /// - /// Writes a 16bit color lookup table + /// Writes a 16bit color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut16(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue)); - } + count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue)); } return count; } /// - /// Writes a 32bit float color lookup table + /// Writes a 32bit float color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClutF32(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteSingle(item); - } + count += this.WriteSingle(item); } return count; diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 1f9c101fb5..6019a0bff7 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -231,7 +231,7 @@ internal sealed partial class IccDataWriter { int count = this.WriteByte((byte)value.InputChannelCount); count += this.WriteByte((byte)value.OutputChannelCount); - count += this.WriteByte((byte)value.ClutValues.Values[0].Length); + count += this.WriteByte((byte)value.ClutValues.OutputChannelCount); count += this.WriteEmpty(1); count += this.WriteMatrix(value.Matrix, false); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs index 68b9beefea..b6818df1d3 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs @@ -14,7 +14,8 @@ internal sealed class IccClut : IEquatable /// The CLUT values. /// The gridpoint count. /// The data type of this CLUT. - public IccClut(float[][] values, byte[] gridPointCount, IccClutDataType type) + /// The output channels count. + public IccClut(float[] values, byte[] gridPointCount, IccClutDataType type, int outputChannelCount) { Guard.NotNull(values, nameof(values)); Guard.NotNull(gridPointCount, nameof(gridPointCount)); @@ -22,7 +23,7 @@ internal sealed class IccClut : IEquatable this.Values = values; this.DataType = type; this.InputChannelCount = gridPointCount.Length; - this.OutputChannelCount = values[0].Length; + this.OutputChannelCount = outputChannelCount; this.GridPointCount = gridPointCount; this.CheckValues(); } @@ -30,7 +31,7 @@ internal sealed class IccClut : IEquatable /// /// Gets the values that make up this table. /// - public float[][] Values { get; } + public float[] Values { get; } /// /// Gets the CLUT data type (important when writing a profile). @@ -92,7 +93,7 @@ internal sealed class IccClut : IEquatable for (int i = 0; i < this.Values.Length; i++) { - if (!this.Values[i].AsSpan().SequenceEqual(other.Values[i])) + if (!this.Values.SequenceEqual(other.Values)) { return false; } @@ -106,17 +107,12 @@ internal sealed class IccClut : IEquatable Guard.MustBeBetweenOrEqualTo(this.InputChannelCount, 1, 15, nameof(this.InputChannelCount)); Guard.MustBeBetweenOrEqualTo(this.OutputChannelCount, 1, 15, nameof(this.OutputChannelCount)); - bool isLengthDifferent = this.Values.Any(t => t.Length != this.OutputChannelCount); - Guard.IsFalse(isLengthDifferent, nameof(this.Values), "The number of output values varies"); - int length = 0; for (int i = 0; i < this.InputChannelCount; i++) { length += (int)Math.Pow(this.GridPointCount[i], this.InputChannelCount); } - length /= this.InputChannelCount; - Guard.IsTrue(this.Values.Length == length, nameof(this.Values), "Length of values array does not match the grid points"); } } diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs index dadbc5262b..bcf87c5fcd 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs @@ -11,142 +11,148 @@ public class IccConversionDataClut internal static IccClut Clut3x2 = new( new[] { - new[] { 0.1f, 0.1f }, - new[] { 0.2f, 0.2f }, - new[] { 0.3f, 0.3f }, + 0.1f, 0.1f, + 0.2f, 0.2f, + 0.3f, 0.3f, - new[] { 0.11f, 0.11f }, - new[] { 0.21f, 0.21f }, - new[] { 0.31f, 0.31f }, + 0.11f, 0.11f, + 0.21f, 0.21f, + 0.31f, 0.31f, - new[] { 0.12f, 0.12f }, - new[] { 0.22f, 0.22f }, - new[] { 0.32f, 0.32f }, + 0.12f, 0.12f, + 0.22f, 0.22f, + 0.32f, 0.32f, - new[] { 0.13f, 0.13f }, - new[] { 0.23f, 0.23f }, - new[] { 0.33f, 0.33f }, + 0.13f, 0.13f, + 0.23f, 0.23f, + 0.33f, 0.33f, - new[] { 0.14f, 0.14f }, - new[] { 0.24f, 0.24f }, - new[] { 0.34f, 0.34f }, + 0.14f, 0.14f, + 0.24f, 0.24f, + 0.34f, 0.34f, - new[] { 0.15f, 0.15f }, - new[] { 0.25f, 0.25f }, - new[] { 0.35f, 0.35f }, + 0.15f, 0.15f, + 0.25f, 0.25f, + 0.35f, 0.35f, - new[] { 0.16f, 0.16f }, - new[] { 0.26f, 0.26f }, - new[] { 0.36f, 0.36f }, + 0.16f, 0.16f, + 0.26f, 0.26f, + 0.36f, 0.36f, - new[] { 0.17f, 0.17f }, - new[] { 0.27f, 0.27f }, - new[] { 0.37f, 0.37f }, + 0.17f, 0.17f, + 0.27f, 0.27f, + 0.37f, 0.37f, - new[] { 0.18f, 0.18f }, - new[] { 0.28f, 0.28f }, - new[] { 0.38f, 0.38f }, + 0.18f, 0.18f, + 0.28f, 0.28f, + 0.38f, 0.38f, }, new byte[] { 3, 3, 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); internal static IccClut Clut3x1 = new( new[] { - new[] { 0.10f }, - new[] { 0.20f }, - new[] { 0.30f }, + 0.10f, + 0.20f, + 0.30f, - new[] { 0.11f }, - new[] { 0.21f }, - new[] { 0.31f }, + 0.11f, + 0.21f, + 0.31f, - new[] { 0.12f }, - new[] { 0.22f }, - new[] { 0.32f }, + 0.12f, + 0.22f, + 0.32f, - new[] { 0.13f }, - new[] { 0.23f }, - new[] { 0.33f }, + 0.13f, + 0.23f, + 0.33f, - new[] { 0.14f }, - new[] { 0.24f }, - new[] { 0.34f }, + 0.14f, + 0.24f, + 0.34f, - new[] { 0.15f }, - new[] { 0.25f }, - new[] { 0.35f }, + 0.15f, + 0.25f, + 0.35f, - new[] { 0.16f }, - new[] { 0.26f }, - new[] { 0.36f }, + 0.16f, + 0.26f, + 0.36f, - new[] { 0.17f }, - new[] { 0.27f }, - new[] { 0.37f }, + 0.17f, + 0.27f, + 0.37f, - new[] { 0.18f }, - new[] { 0.28f }, - new[] { 0.38f }, + 0.18f, + 0.28f, + 0.38f, }, new byte[] { 3, 3, 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); internal static IccClut Clut2x2 = new( new[] { - new[] { 0.1f, 0.9f }, - new[] { 0.2f, 0.8f }, - new[] { 0.3f, 0.7f }, + 0.1f, 0.9f, + 0.2f, 0.8f, + 0.3f, 0.7f, - new[] { 0.4f, 0.6f }, - new[] { 0.5f, 0.5f }, - new[] { 0.6f, 0.4f }, + 0.4f, 0.6f, + 0.5f, 0.5f, + 0.6f, 0.4f, - new[] { 0.7f, 0.3f }, - new[] { 0.8f, 0.2f }, - new[] { 0.9f, 0.1f }, + 0.7f, 0.3f, + 0.8f, 0.2f, + 0.9f, 0.1f, }, new byte[] { 3, 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); internal static IccClut Clut2x1 = new( new[] { - new[] { 0.1f }, - new[] { 0.2f }, - new[] { 0.3f }, + 0.1f, + 0.2f, + 0.3f, - new[] { 0.4f }, - new[] { 0.5f }, - new[] { 0.6f }, + 0.4f, + 0.5f, + 0.6f, - new[] { 0.7f }, - new[] { 0.8f }, - new[] { 0.9f }, + 0.7f, + 0.8f, + 0.9f, }, new byte[] { 3, 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); internal static IccClut Clut1x2 = new( new[] { - new[] { 0f, 0.5f }, - new[] { 0.25f, 0.75f, }, - new[] { 0.5f, 1f }, + 0f, 0.5f, + 0.25f, 0.75f, + 0.5f, 1f, }, new byte[] { 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); internal static IccClut Clut1x1 = new( new[] { - new[] { 0f }, - new[] { 0.5f }, - new[] { 1f }, + 0f, + 0.5f, + 1f, }, new byte[] { 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); public static object[][] ClutConversionTestData = { diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs index 3c3e576fe2..e4adba078d 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs @@ -18,20 +18,21 @@ public class IccConversionDataMultiProcessElement private static readonly IccClut Clut = new( new[] { - new[] { 0.2f, 0.3f }, - new[] { 0.4f, 0.5f }, + 0.2f, 0.3f, + 0.4f, 0.2f, - new[] { 0.21f, 0.31f }, - new[] { 0.41f, 0.51f }, + 0.21f, 0.31f, + 0.41f, 0.51f, - new[] { 0.22f, 0.32f }, - new[] { 0.42f, 0.52f }, + 0.22f, 0.32f, + 0.42f, 0.52f, - new[] { 0.23f, 0.33f }, - new[] { 0.43f, 0.53f }, + 0.23f, 0.33f, + 0.43f, 0.53f, }, new byte[] { 2, 2, 2 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 2); private static readonly IccFormulaCurveElement FormulaCurveElement1 = new(IccFormulaCurveType.Type1, 2.2f, 0.7f, 0.2f, 0.3f, 0, 0); private static readonly IccFormulaCurveElement FormulaCurveElement2 = new(IccFormulaCurveType.Type2, 2.2f, 0.9f, 0.9f, 0.02f, 0.1f, 0); diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs index ea4a756007..2bd47e4497 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs @@ -73,20 +73,21 @@ internal static class IccTestDataLut public static readonly IccClut Clut8ValGrad = new( new[] { - new[] { 1f / byte.MaxValue, 2f / byte.MaxValue, 3f / byte.MaxValue }, - new[] { 4f / byte.MaxValue, 5f / byte.MaxValue, 6f / byte.MaxValue }, - new[] { 7f / byte.MaxValue, 8f / byte.MaxValue, 9f / byte.MaxValue }, + 1f / byte.MaxValue, 2f / byte.MaxValue, 3f / byte.MaxValue, + 4f / byte.MaxValue, 5f / byte.MaxValue, 6f / byte.MaxValue, + 7f / byte.MaxValue, 8f / byte.MaxValue, 9f / byte.MaxValue, - new[] { 10f / byte.MaxValue, 11f / byte.MaxValue, 12f / byte.MaxValue }, - new[] { 13f / byte.MaxValue, 14f / byte.MaxValue, 15f / byte.MaxValue }, - new[] { 16f / byte.MaxValue, 17f / byte.MaxValue, 18f / byte.MaxValue }, + 10f / byte.MaxValue, 11f / byte.MaxValue, 12f / byte.MaxValue, + 13f / byte.MaxValue, 14f / byte.MaxValue, 15f / byte.MaxValue, + 16f / byte.MaxValue, 17f / byte.MaxValue, 18f / byte.MaxValue, - new[] { 19f / byte.MaxValue, 20f / byte.MaxValue, 21f / byte.MaxValue }, - new[] { 22f / byte.MaxValue, 23f / byte.MaxValue, 24f / byte.MaxValue }, - new[] { 25f / byte.MaxValue, 26f / byte.MaxValue, 27f / byte.MaxValue }, + 19f / byte.MaxValue, 20f / byte.MaxValue, 21f / byte.MaxValue, + 22f / byte.MaxValue, 23f / byte.MaxValue, 24f / byte.MaxValue, + 25f / byte.MaxValue, 26f / byte.MaxValue, 27f / byte.MaxValue, }, new byte[] { 3, 3 }, - IccClutDataType.UInt8); + IccClutDataType.UInt8, + outputChannelCount: 3); /// /// Input Channel Count: 2 @@ -116,20 +117,21 @@ internal static class IccTestDataLut public static readonly IccClut Clut16ValGrad = new( new[] { - new[] { 1f / ushort.MaxValue, 2f / ushort.MaxValue, 3f / ushort.MaxValue }, - new[] { 4f / ushort.MaxValue, 5f / ushort.MaxValue, 6f / ushort.MaxValue }, - new[] { 7f / ushort.MaxValue, 8f / ushort.MaxValue, 9f / ushort.MaxValue }, + 1f / ushort.MaxValue, 2f / ushort.MaxValue, 3f / ushort.MaxValue, + 4f / ushort.MaxValue, 5f / ushort.MaxValue, 6f / ushort.MaxValue, + 7f / ushort.MaxValue, 8f / ushort.MaxValue, 9f / ushort.MaxValue, - new[] { 10f / ushort.MaxValue, 11f / ushort.MaxValue, 12f / ushort.MaxValue }, - new[] { 13f / ushort.MaxValue, 14f / ushort.MaxValue, 15f / ushort.MaxValue }, - new[] { 16f / ushort.MaxValue, 17f / ushort.MaxValue, 18f / ushort.MaxValue }, + 10f / ushort.MaxValue, 11f / ushort.MaxValue, 12f / ushort.MaxValue, + 13f / ushort.MaxValue, 14f / ushort.MaxValue, 15f / ushort.MaxValue, + 16f / ushort.MaxValue, 17f / ushort.MaxValue, 18f / ushort.MaxValue, - new[] { 19f / ushort.MaxValue, 20f / ushort.MaxValue, 21f / ushort.MaxValue }, - new[] { 22f / ushort.MaxValue, 23f / ushort.MaxValue, 24f / ushort.MaxValue }, - new[] { 25f / ushort.MaxValue, 26f / ushort.MaxValue, 27f / ushort.MaxValue }, + 19f / ushort.MaxValue, 20f / ushort.MaxValue, 21f / ushort.MaxValue, + 22f / ushort.MaxValue, 23f / ushort.MaxValue, 24f / ushort.MaxValue, + 25f / ushort.MaxValue, 26f / ushort.MaxValue, 27f / ushort.MaxValue, }, new byte[] { 3, 3 }, - IccClutDataType.UInt16); + IccClutDataType.UInt16, + outputChannelCount: 3); /// /// Input Channel Count: 2 @@ -159,20 +161,21 @@ internal static class IccTestDataLut public static readonly IccClut CluTf32ValGrad = new( new[] { - new[] { 1f, 2f, 3f }, - new[] { 4f, 5f, 6f }, - new[] { 7f, 8f, 9f }, + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f, - new[] { 1f, 2f, 3f }, - new[] { 4f, 5f, 6f }, - new[] { 7f, 8f, 9f }, + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f, - new[] { 1f, 2f, 3f }, - new[] { 4f, 5f, 6f }, - new[] { 7f, 8f, 9f }, + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f, }, new byte[] { 3, 3 }, - IccClutDataType.Float); + IccClutDataType.Float, + outputChannelCount: 3); /// /// Input Channel Count: 2