From 47f29170b3f85cb62be35c094be7373199f39b8f Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 30 Dec 2022 11:07:38 -0500 Subject: [PATCH] LibGfx+icc: Add profile connection space printing This is a bit messy: The spec says that PCSXYZ and PCSLAB are the only valid profile connection spaces -- except for DeviceLink profles, where all data color spaces are valid. So this uses the existing ColorSpace enum for profile connection spaces instead of adding a dedicated enum, to not duplicate all the color space parsing and printing code. That matches what the spec does, too. This saves about 100 lines of code, at the expense of less type safety -- but further down the line we probably want to be able to compare data color spaces and profile connection spaces, so the type safety would likely get in the way then. (But if not, we can change things around once we get to that point.) --- Userland/Libraries/LibGfx/ICCProfile.cpp | 45 ++++++++++++++++++++---- Userland/Libraries/LibGfx/ICCProfile.h | 14 ++++++-- Userland/Utilities/icc.cpp | 3 +- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Userland/Libraries/LibGfx/ICCProfile.cpp b/Userland/Libraries/LibGfx/ICCProfile.cpp index 76963281cb..fd85534790 100644 --- a/Userland/Libraries/LibGfx/ICCProfile.cpp +++ b/Userland/Libraries/LibGfx/ICCProfile.cpp @@ -78,7 +78,7 @@ struct ICCHeader { BigEndian profile_device_class; BigEndian data_color_space; - BigEndian pcs; // "Profile Connection Space" + BigEndian profile_connection_space; // "PCS" in the spec. DateTimeNumber profile_creation_time; @@ -126,10 +126,10 @@ ErrorOr parse_device_class(ICCHeader const& header) return Error::from_string_literal("ICC::Profile: Invalid device class"); } -ErrorOr parse_data_color_space(ICCHeader const& header) +ErrorOr parse_color_space(ColorSpace color_space) { - // ICC v4, 7.2.6 Data colour space field - switch (header.data_color_space) { + // ICC v4, Table 19 — Data colour space signatures + switch (color_space) { case ColorSpace::nCIEXYZ: case ColorSpace::CIELAB: case ColorSpace::CIELUV: @@ -155,9 +155,27 @@ ErrorOr parse_data_color_space(ICCHeader const& header) case ColorSpace::ThirteenColor: case ColorSpace::FourteenColor: case ColorSpace::FifteenColor: - return header.data_color_space; + return color_space; } - return Error::from_string_literal("ICC::Profile: Invalid data color space"); + return Error::from_string_literal("ICC::Profile: Invalid color space"); +} + +ErrorOr parse_data_color_space(ICCHeader const& header) +{ + // ICC v4, 7.2.6 Data colour space field + return parse_color_space(header.data_color_space); +} + +ErrorOr parse_connection_space(ICCHeader const& header) +{ + // ICC v4, 7.2.7 PCS field + // and Annex D + auto space = TRY(parse_color_space(header.profile_connection_space)); + + if (header.profile_device_class != DeviceClass::DeviceLink && (space != ColorSpace::PCSXYZ && space != ColorSpace::PCSLAB)) + return Error::from_string_literal("ICC::Profile: Invalid profile connection space: Non-PCS space on non-DeviceLink profile"); + + return space; } ErrorOr parse_rendering_intent(ICCHeader const& header) @@ -212,7 +230,7 @@ StringView device_class_name(DeviceClass device_class) VERIFY_NOT_REACHED(); } -StringView color_space_name(ColorSpace color_space) +StringView data_color_space_name(ColorSpace color_space) { switch (color_space) { case ColorSpace::nCIEXYZ: @@ -269,6 +287,18 @@ StringView color_space_name(ColorSpace color_space) VERIFY_NOT_REACHED(); } +StringView profile_connection_space_name(ColorSpace color_space) +{ + switch (color_space) { + case ColorSpace::PCSXYZ: + return "PCSXYZ"sv; + case ColorSpace::PCSLAB: + return "PCSLAB"sv; + default: + return data_color_space_name(color_space); + } +} + StringView rendering_intent_name(RenderingIntent rendering_intent) { switch (rendering_intent) { @@ -303,6 +333,7 @@ ErrorOr> Profile::try_load_from_externally_owned_memory(R profile->m_version = TRY(parse_version(header)); profile->m_device_class = TRY(parse_device_class(header)); profile->m_data_color_space = TRY(parse_data_color_space(header)); + profile->m_connection_space = TRY(parse_connection_space(header)); profile->m_creation_timestamp = TRY(parse_creation_date_time(header)); profile->m_flags = Flags { header.profile_flags }; profile->m_rendering_intent = TRY(parse_rendering_intent(header)); diff --git a/Userland/Libraries/LibGfx/ICCProfile.h b/Userland/Libraries/LibGfx/ICCProfile.h index 11287bd375..aa35616c0c 100644 --- a/Userland/Libraries/LibGfx/ICCProfile.h +++ b/Userland/Libraries/LibGfx/ICCProfile.h @@ -47,8 +47,10 @@ StringView device_class_name(DeviceClass); // ICC v4, 7.2.6 Data colour space field, Table 19 — Data colour space signatures enum class ColorSpace : u32 { - nCIEXYZ = 0x58595A20, // 'XYZ ' - CIELAB = 0x4C616220, // 'Lab ' + nCIEXYZ = 0x58595A20, // 'XYZ ', used in data color spaces. + PCSXYZ = nCIEXYZ, // Used in profile connection space instead. + CIELAB = 0x4C616220, // 'Lab ', used in data color spaces. + PCSLAB = CIELAB, // Used in profile connection space instead. CIELUV = 0x4C757620, // 'Luv ' YCbCr = 0x59436272, // 'YCbr' CIEYxy = 0x59787920, // 'Yxy ' @@ -73,7 +75,8 @@ enum class ColorSpace : u32 { FourteenColor = 0x45434C52, // 'ECLR' FifteenColor = 0x46434C52, // 'FCLR' }; -StringView color_space_name(ColorSpace); +StringView data_color_space_name(ColorSpace); +StringView profile_connection_space_name(ColorSpace); // ICC v4, 7.2.15 Rendering intent field enum class RenderingIntent { @@ -119,6 +122,10 @@ public: Version version() const { return m_version; } DeviceClass device_class() const { return m_device_class; } ColorSpace data_color_space() const { return m_data_color_space; } + + // For non-DeviceLink profiles, always PCSXYZ or PCSLAB. + ColorSpace connection_space() const { return m_connection_space; } + time_t creation_timestamp() const { return m_creation_timestamp; } Flags flags() const { return m_flags; } RenderingIntent rendering_intent() const { return m_rendering_intent; } @@ -127,6 +134,7 @@ private: Version m_version; DeviceClass m_device_class; ColorSpace m_data_color_space; + ColorSpace m_connection_space; time_t m_creation_timestamp; Flags m_flags; RenderingIntent m_rendering_intent; diff --git a/Userland/Utilities/icc.cpp b/Userland/Utilities/icc.cpp index 9fad397005..863c47022a 100644 --- a/Userland/Utilities/icc.cpp +++ b/Userland/Utilities/icc.cpp @@ -23,7 +23,8 @@ ErrorOr serenity_main(Main::Arguments arguments) outln("version: {}", profile->version()); outln("device class: {}", Gfx::ICC::device_class_name(profile->device_class())); - outln("data color space: {}", Gfx::ICC::color_space_name(profile->data_color_space())); + outln("data color space: {}", Gfx::ICC::data_color_space_name(profile->data_color_space())); + outln("connection space: {}", Gfx::ICC::profile_connection_space_name(profile->connection_space())); outln("creation date and time: {}", Core::DateTime::from_timestamp(profile->creation_timestamp()).to_deprecated_string()); auto flags = profile->flags();