LibGfx+icc: Read chromaticityTag

This isn't terribly useful. But some profiles, for example the ones at
https://vpifg.com/help/icc-profiles/, do contain this tag and it seems
nice to be able to dump it, just for completeness.

I haven't seen any files that contain a phosphor or colorant type
different from "Unknown", even for the Rec2020 profile on that page.
(It has x,y coordinates that match the values required for Rec2020,
but it doesn't set the phosphor or colorant type to that.)
This commit is contained in:
Nico Weber 2023-02-09 12:26:43 -05:00 committed by Linus Groh
parent c61cfdd5ed
commit b232281d15
Notes: sideshowbarker 2024-07-17 07:31:31 +09:00
4 changed files with 116 additions and 2 deletions

View file

@ -572,6 +572,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offse
auto type = tag_type(tag_bytes); auto type = tag_type(tag_bytes);
switch (type) { 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: case CicpTagData::Type:
return CicpTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element); return CicpTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
case CurveTagData::Type: case CurveTagData::Type:
@ -988,7 +990,8 @@ ErrorOr<void> Profile::check_tag_types()
// ICC v4, 9.2.16 chromaticityTag // ICC v4, 9.2.16 chromaticityTag
// "Permitted tag types: chromaticityType" // "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 // ICC v4, 9.2.17 cicpTag
// "Permitted tag types: cicpType" // "Permitted tag types: cicpType"

View file

@ -83,6 +83,72 @@ TagTypeSignature tag_type(ReadonlyBytes tag_bytes)
return *bit_cast<BigEndian<TagTypeSignature> const*>(tag_bytes.data()); return *bit_cast<BigEndian<TagTypeSignature> 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<NonnullRefPtr<ChromaticityTagData>> 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<BigEndian<u16> const*>(bytes.data() + 8);
PhosphorOrColorantType phosphor_or_colorant_type = *bit_cast<BigEndian<PhosphorOrColorantType> 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<BigEndian<u16Fixed16Number> const*>(bytes.data() + 12);
Vector<xyCoordinate> 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<NonnullRefPtr<CicpTagData>> CicpTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) ErrorOr<NonnullRefPtr<CicpTagData>> CicpTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{ {
// ICC v4, 10.3 cicpType // ICC v4, 10.3 cicpType

View file

@ -59,6 +59,46 @@ public:
} }
}; };
// ICC v4, 10.2 chromaticityType
class ChromaticityTagData : public TagData {
public:
static constexpr TagTypeSignature Type { 0x6368726D }; // 'chrm'
static ErrorOr<NonnullRefPtr<ChromaticityTagData>> 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<xyCoordinate> 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<xyCoordinate> xy_coordinates() const { return m_xy_coordinates; }
private:
PhosphorOrColorantType m_phosphor_or_colorant_type;
Vector<xyCoordinate> m_xy_coordinates;
};
// ICC v4, 10.3 cicpType // ICC v4, 10.3 cicpType
// "The cicpType specifies Coding-independent code points for video signal type identification." // "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. // See presentations at https://www.color.org/events/HDR_experts.xalter for background.

View file

@ -126,7 +126,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
} }
tag_data_to_first_signature.set(tag_data, tag_signature); 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<Gfx::ICC::ChromaticityTagData&>(*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<Gfx::ICC::CicpTagData&>(*tag_data); auto& cicp = static_cast<Gfx::ICC::CicpTagData&>(*tag_data);
outln(" color primaries: {} - {}", cicp.color_primaries(), outln(" color primaries: {} - {}", cicp.color_primaries(),
Video::color_primaries_to_string((Video::ColorPrimaries)cicp.color_primaries())); Video::color_primaries_to_string((Video::ColorPrimaries)cicp.color_primaries()));