private static bool tryGetTagEntry(TagEntry[] entries, uint tag, out TagEntry entry) { for (int i = 0; i < entries.Length; i++) { if (entries[i].tag == tag) { entry = entries[i]; return(true); } } entry = default; return(false); }
public static ColorProfile Parse(ReadOnlySpan <byte> prof) { const int headerLength = 128; const int headerPlusTagCountLength = 132; if (prof.Length < headerPlusTagCountLength) { return(invalidProfile); } uint len = ReadUInt32BigEndian(prof.Slice(0, sizeof(uint))); if (len != prof.Length) { return(invalidProfile); } uint acsp = ReadUInt32BigEndian(prof.Slice(36)); var ver = prof.Slice(8, 4); if ((ver[0] != 2 && ver[0] != 4) || acsp != IccStrings.acsp) { return(invalidProfile); } var dataColorSpace = ReadUInt32BigEndian(prof.Slice(16)) switch { IccStrings.RGB => ProfileColorSpace.Rgb, IccStrings.GRAY => ProfileColorSpace.Grey, IccStrings.CMYK => ProfileColorSpace.Cmyk, _ => ProfileColorSpace.Other }; var pcsColorSpace = ReadUInt32BigEndian(prof.Slice(20)) switch { IccStrings.XYZ => ProfileColorSpace.Xyz, IccStrings.Lab => ProfileColorSpace.Lab, _ => ProfileColorSpace.Other }; if (pcsColorSpace != ProfileColorSpace.Xyz || (dataColorSpace != ProfileColorSpace.Rgb && dataColorSpace != ProfileColorSpace.Grey)) { return(new ColorProfile(prof.ToArray(), dataColorSpace, pcsColorSpace, ColorProfileType.Unknown)); } uint tagCount = ReadUInt32BigEndian(prof.Slice(headerLength)); if (len < (headerPlusTagCountLength + tagCount * Unsafe.SizeOf <TagEntry>())) { return(invalidProfile); } var tagEntries = new TagEntry[((int)tagCount)]; for (int i = 0; i < tagCount; i++) { int entryStart = headerPlusTagCountLength + i * Unsafe.SizeOf <TagEntry>(); uint tag = ReadUInt32BigEndian(prof.Slice(entryStart)); uint pos = ReadUInt32BigEndian(prof.Slice(entryStart + 4)); uint cb = ReadUInt32BigEndian(prof.Slice(entryStart + 8)); if (len < (pos + cb)) { return(invalidProfile); } // not handling these yet, so we'll hand off to WCS if (tag == IccTags.A2B0 || tag == IccTags.B2A0) { return(new ColorProfile(prof.ToArray(), dataColorSpace, pcsColorSpace, ColorProfileType.Table)); } tagEntries[i] = new TagEntry(tag, (int)pos, (int)cb); } if (dataColorSpace == ProfileColorSpace.Grey && tryGetTagEntry(tagEntries, IccTags.kTRC, out var kTRC) && tryGetCurve(prof.Slice(kTRC.pos, kTRC.cb), out var curve) ) { if (curve == sRGB.Curve) { return(sGrey); } return(new CurveProfile(prof.ToArray(), curve, dataColorSpace, pcsColorSpace)); } if (dataColorSpace == ProfileColorSpace.Rgb && tryGetTagEntry(tagEntries, IccTags.bTRC, out var bTRC) && tryGetTagEntry(tagEntries, IccTags.gTRC, out var gTRC) && tryGetTagEntry(tagEntries, IccTags.rTRC, out var rTRC) && tryGetTagEntry(tagEntries, IccTags.bXYZ, out var bXYZ) && tryGetTagEntry(tagEntries, IccTags.gXYZ, out var gXYZ) && tryGetTagEntry(tagEntries, IccTags.rXYZ, out var rXYZ) ) { var bTRCData = prof.Slice(bTRC.pos, bTRC.cb); var gTRCData = prof.Slice(gTRC.pos, gTRC.cb); var rTRCData = prof.Slice(rTRC.pos, rTRC.cb); if (bTRCData.SequenceEqual(gTRCData) && bTRCData.SequenceEqual(rTRCData) && tryGetCurve(bTRCData, out var rgbcurve) && tryGetMatrix(prof.Slice(bXYZ.pos, bXYZ.cb), prof.Slice(gXYZ.pos, gXYZ.cb), prof.Slice(rXYZ.pos, rXYZ.cb), out var matrix) ) { if (matrix.IsRouglyEqualTo(sRGB.Matrix) && rgbcurve == sRGB.Curve) { return(sRGB); } var imatrix = matrix.InvertPrecise(); if (!imatrix.IsNaN()) { return(new MatrixProfile(prof.ToArray(), matrix, imatrix, rgbcurve, dataColorSpace, pcsColorSpace)); } } } return(invalidProfile); }