Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }