diff --git a/Userland/Libraries/LibGfx/ICC/Profile.cpp b/Userland/Libraries/LibGfx/ICC/Profile.cpp index 7afa21c88e..9c797a5a05 100644 --- a/Userland/Libraries/LibGfx/ICC/Profile.cpp +++ b/Userland/Libraries/LibGfx/ICC/Profile.cpp @@ -572,6 +572,8 @@ ErrorOr> Profile::read_tag(ReadonlyBytes bytes, u32 offse auto type = tag_type(tag_bytes); switch (type) { + case ChromaticityTagData::Type: + return ChromaticityTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element); case CicpTagData::Type: return CicpTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element); case CurveTagData::Type: @@ -988,7 +990,8 @@ ErrorOr Profile::check_tag_types() // ICC v4, 9.2.16 chromaticityTag // "Permitted tag types: chromaticityType" - // FIXME + if (!has_type(chromaticityTag, { ChromaticityTagData::Type }, {})) + return Error::from_string_literal("ICC::Profile: ChromaticityTagData has unexpected type"); // ICC v4, 9.2.17 cicpTag // "Permitted tag types: cicpType" diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp index bcb6e8b63f..94d0ec70d7 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp @@ -83,6 +83,72 @@ TagTypeSignature tag_type(ReadonlyBytes tag_bytes) return *bit_cast const*>(tag_bytes.data()); } +StringView ChromaticityTagData::phosphor_or_colorant_type_name(PhosphorOrColorantType phosphor_or_colorant_type) +{ + switch (phosphor_or_colorant_type) { + case PhosphorOrColorantType::Unknown: + return "Unknown"sv; + case PhosphorOrColorantType::ITU_R_BT_709_2: + return "ITU-R BT.709-2"sv; + case PhosphorOrColorantType::SMPTE_RP145: + return "SMPTE RP145"sv; + case PhosphorOrColorantType::EBU_Tech_3213_E: + return "EBU Tech. 3213-E"sv; + case PhosphorOrColorantType::P22: + return "P22"sv; + case PhosphorOrColorantType::P3: + return "P3"sv; + case PhosphorOrColorantType::ITU_R_BT_2020: + return "ITU-R BT.2020"sv; + } + VERIFY_NOT_REACHED(); +} + +ErrorOr> ChromaticityTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) +{ + // ICC v4, 10.2 chromaticityType + VERIFY(tag_type(bytes) == Type); + TRY(check_reserved(bytes)); + + if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16)) + return Error::from_string_literal("ICC::Profile: chromaticityType has not enough data"); + + u16 number_of_device_channels = *bit_cast const*>(bytes.data() + 8); + PhosphorOrColorantType phosphor_or_colorant_type = *bit_cast const*>(bytes.data() + 10); + + switch (phosphor_or_colorant_type) { + case PhosphorOrColorantType::Unknown: + case PhosphorOrColorantType::ITU_R_BT_709_2: + case PhosphorOrColorantType::SMPTE_RP145: + case PhosphorOrColorantType::EBU_Tech_3213_E: + case PhosphorOrColorantType::P22: + case PhosphorOrColorantType::P3: + case PhosphorOrColorantType::ITU_R_BT_2020: + break; + default: + return Error::from_string_literal("ICC::Profile: chromaticityType invalid phosphor_or_colorant_type"); + } + + // "If the value is 0001h to 0004h, the number of channels shall be three..." + if (phosphor_or_colorant_type != PhosphorOrColorantType::Unknown && number_of_device_channels != 3) + return Error::from_string_literal("ICC::Profile: chromaticityType unexpected number of channels for phosphor_or_colorant_type"); + + if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16) + number_of_device_channels * 2 * sizeof(u16Fixed16Number)) + return Error::from_string_literal("ICC::Profile: chromaticityType has not enough data for xy coordinates"); + + auto* raw_xy_coordinates = bit_cast const*>(bytes.data() + 12); + Vector xy_coordinates; + TRY(xy_coordinates.try_resize(number_of_device_channels)); + for (size_t i = 0; i < number_of_device_channels; ++i) { + xy_coordinates[i].x = U16Fixed16::create_raw(raw_xy_coordinates[2 * i]); + xy_coordinates[i].y = U16Fixed16::create_raw(raw_xy_coordinates[2 * i + 1]); + } + + // FIXME: Once I find files that have phosphor_or_colorant_type != Unknown, check that the values match the values in Table 31. + + return adopt_ref(*new ChromaticityTagData(offset, size, phosphor_or_colorant_type, move(xy_coordinates))); +} + ErrorOr> CicpTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) { // ICC v4, 10.3 cicpType diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.h b/Userland/Libraries/LibGfx/ICC/TagTypes.h index b8f46aebf1..75a6e2dd2d 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.h +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.h @@ -59,6 +59,46 @@ public: } }; +// ICC v4, 10.2 chromaticityType +class ChromaticityTagData : public TagData { +public: + static constexpr TagTypeSignature Type { 0x6368726D }; // 'chrm' + + static ErrorOr> from_bytes(ReadonlyBytes, u32 offset, u32 size); + + // ICC v4, Table 31 — Colorant and phosphor encoding + enum class PhosphorOrColorantType : u16 { + Unknown = 0, + ITU_R_BT_709_2 = 1, + SMPTE_RP145 = 2, + EBU_Tech_3213_E = 3, + P22 = 4, + P3 = 5, + ITU_R_BT_2020 = 6, + }; + + static StringView phosphor_or_colorant_type_name(PhosphorOrColorantType); + + struct xyCoordinate { + U16Fixed16 x; + U16Fixed16 y; + }; + + ChromaticityTagData(u32 offset, u32 size, PhosphorOrColorantType phosphor_or_colorant_type, Vector xy_coordinates) + : TagData(offset, size, Type) + , m_phosphor_or_colorant_type(phosphor_or_colorant_type) + , m_xy_coordinates(move(xy_coordinates)) + { + } + + PhosphorOrColorantType phosphor_or_colorant_type() const { return m_phosphor_or_colorant_type; } + Vector xy_coordinates() const { return m_xy_coordinates; } + +private: + PhosphorOrColorantType m_phosphor_or_colorant_type; + Vector m_xy_coordinates; +}; + // ICC v4, 10.3 cicpType // "The cicpType specifies Coding-independent code points for video signal type identification." // See presentations at https://www.color.org/events/HDR_experts.xalter for background. diff --git a/Userland/Utilities/icc.cpp b/Userland/Utilities/icc.cpp index 60b33391b9..df4777da0f 100644 --- a/Userland/Utilities/icc.cpp +++ b/Userland/Utilities/icc.cpp @@ -126,7 +126,12 @@ ErrorOr serenity_main(Main::Arguments arguments) } tag_data_to_first_signature.set(tag_data, tag_signature); - if (tag_data->type() == Gfx::ICC::CicpTagData::Type) { + if (tag_data->type() == Gfx::ICC::ChromaticityTagData::Type) { + auto& chromaticity = static_cast(*tag_data); + outln(" phosphor or colorant type: {}", Gfx::ICC::ChromaticityTagData::phosphor_or_colorant_type_name(chromaticity.phosphor_or_colorant_type())); + for (auto const& xy : chromaticity.xy_coordinates()) + outln(" x, y: {}, {}", xy.x, xy.y); + } else if (tag_data->type() == Gfx::ICC::CicpTagData::Type) { auto& cicp = static_cast(*tag_data); outln(" color primaries: {} - {}", cicp.color_primaries(), Video::color_primaries_to_string((Video::ColorPrimaries)cicp.color_primaries()));