diff --git a/Userland/Utilities/icc.cpp b/Userland/Utilities/icc.cpp index f44450e8f88..e5a3edd0e88 100644 --- a/Userland/Utilities/icc.cpp +++ b/Userland/Utilities/icc.cpp @@ -74,6 +74,39 @@ static void out_parametric_curve(Gfx::ICC::ParametricCurveTagData const& paramet } } +static float curve_distance_u8(Gfx::ICC::TagData const& tag1, Gfx::ICC::TagData const& tag2) +{ + VERIFY(tag1.type() == Gfx::ICC::CurveTagData::Type || tag1.type() == Gfx::ICC::ParametricCurveTagData::Type); + VERIFY(tag2.type() == Gfx::ICC::CurveTagData::Type || tag2.type() == Gfx::ICC::ParametricCurveTagData::Type); + + float curve1_data[256]; + if (tag1.type() == Gfx::ICC::CurveTagData::Type) { + auto& curve1 = static_cast(tag1); + for (int i = 0; i < 256; ++i) + curve1_data[i] = curve1.evaluate(i / 255.f); + } else { + auto& parametric_curve1 = static_cast(tag1); + for (int i = 0; i < 256; ++i) + curve1_data[i] = parametric_curve1.evaluate(i / 255.f); + } + + float curve2_data[256]; + if (tag2.type() == Gfx::ICC::CurveTagData::Type) { + auto& curve2 = static_cast(tag2); + for (int i = 0; i < 256; ++i) + curve2_data[i] = curve2.evaluate(i / 255.f); + } else { + auto& parametric_curve2 = static_cast(tag2); + for (int i = 0; i < 256; ++i) + curve2_data[i] = parametric_curve2.evaluate(i / 255.f); + } + + float distance = 0; + for (int i = 0; i < 256; ++i) + distance += fabsf(curve1_data[i] - curve2_data[i]); + return distance; +} + static ErrorOr out_curve_tag(Gfx::ICC::TagData const& tag, int indent_amount) { VERIFY(tag.type() == Gfx::ICC::CurveTagData::Type || tag.type() == Gfx::ICC::ParametricCurveTagData::Type); @@ -81,6 +114,40 @@ static ErrorOr out_curve_tag(Gfx::ICC::TagData const& tag, int indent_amou out_curve(static_cast(tag), indent_amount); if (tag.type() == Gfx::ICC::ParametricCurveTagData::Type) out_parametric_curve(static_cast(tag), indent_amount); + + auto sRGB_curve = TRY(Gfx::ICC::sRGB_curve()); + + // Some example values (for abs distance summed over the 256 values of an u8): + // In Compact-ICC-Profiles/profiles: + // AdobeCompat-v2.icc: 1.14 (this is a gamma 2.2 curve, so not really sRGB but close) + // AdobeCompat-v4.icc: 1.13 + // AppleCompat-v2.icc: 11.94 (gamma 1.8 curve) + // DCI-P3-v4.icc: 8.29 (gamma 2.6 curve) + // DisplayP3-v2-magic.icc: 0.000912 (looks sRGB-ish) + // DisplayP3-v2-micro.icc: 0.010819 + // DisplayP3-v4.icc: 0.001062 (yes, definitely sRGB) + // Rec2020-g24-v4.icc: 4.119216 (gamma 2.4 curve) + // Rec2020-v4.icc: 7.805417 (custom non-sRGB curve) + // Rec709-v4.icc: 7.783267 (same custom non-sRGB curve as Rec2020) + // sRGB-v2-magic.icc: 0.000912 + // sRGB-v2-micro.icc: 0.010819 + // sRGB-v2-nano.icc: 0.052516 + // sRGB-v4.icc: 0.001062 + // scRGB-v2.icc: 48.379859 (linear identity curve) + // Google sRGB IEC61966-2.1 (from a Pixel jpeg, parametric): 0 + // Google sRGB IEC61966-2.1 (from a Pixel jpeg, LUT curve): 0.00096 + // Apple 2015 Display P3 (from iPhone 7, parametric): 0.011427 (has the old, left intersection for switching from linear to exponent) + // HP sRGB: 0.00096 + // color.org sRGB2014.icc: 0.00096 + // color.org sRGB_ICC_v4_Appearance.icc, AToB1Tag, a curves: 0.441926 -- but this is not _really_ sRGB + // color.org sRGB_v4_ICC_preference.icc, AToB1Tag, a curves: 2.205453 -- not really sRGB either + // So `< 0.06` identifies sRGB in practice (for u8 values). + float u8_distance_to_sRGB = curve_distance_u8(*sRGB_curve, tag); + if (u8_distance_to_sRGB < 0.06f) + outln("{: >{}}Looks like sRGB's curve (distance {})", "", indent_amount, u8_distance_to_sRGB); + else + outln("{: >{}}Does not look like sRGB's curve (distance: {})", "", indent_amount, u8_distance_to_sRGB); + return {}; }