LibGfx+icc: Read measurementType

Also not terribly useful in practice and mostly for completionism.
Lightroom Classic-exported jpegs contain this type in their ICC data.
This commit is contained in:
Nico Weber 2023-02-09 16:14:32 -05:00 committed by Linus Groh
parent 143f28b735
commit 664946c543
Notes: sideshowbarker 2024-07-17 00:34:18 +09:00
4 changed files with 199 additions and 1 deletions

View file

@ -584,6 +584,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offse
return LutAToBTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
case LutBToATagData::Type:
return LutBToATagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
case MeasurementTagData::Type:
return MeasurementTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
case MultiLocalizedUnicodeTagData::Type:
return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
case NamedColor2TagData::Type:
@ -1115,7 +1117,8 @@ ErrorOr<void> Profile::check_tag_types()
// ICC v4, 9.2.34 measurementTag
// "Permitted tag types: measurementType"
// FIXME
if (!has_type(measurementTag, { MeasurementTagData::Type }, {}))
return Error::from_string_literal("ICC::Profile: measurementTag has unexpected type");
// ICC v4, 9.2.35 metadataTag
// "Permitted tag types: dictType"

View file

@ -16,6 +16,9 @@ namespace {
// ICC V4, 4.6 s15Fixed16Number
using s15Fixed16Number = i32;
// ICC V4, 4.7 u16Fixed16Number
using u16Fixed16Number = u32;
// ICC V4, 4.14 XYZNumber
struct XYZNumber {
BigEndian<s15Fixed16Number> x;
@ -432,6 +435,125 @@ ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes
return adopt_ref(*new LutBToATagData(offset, size, header.number_of_input_channels, header.number_of_output_channels, e, move(clut_data)));
}
ErrorOr<NonnullRefPtr<MeasurementTagData>> MeasurementTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{
// ICC v4, 10.14 measurementType
VERIFY(tag_type(bytes) == Type);
TRY(check_reserved(bytes));
// Table 49 — measurementType structure
struct MeasurementHeader {
BigEndian<StandardObserver> standard_observer;
XYZNumber tristimulus_value_for_measurement_backing;
BigEndian<MeasurementGeometry> measurement_geometry;
BigEndian<u16Fixed16Number> measurement_flare;
BigEndian<StandardIlluminant> standard_illuminant;
};
static_assert(AssertSize<MeasurementHeader, 28>());
if (bytes.size() < 2 * sizeof(u32) + sizeof(MeasurementHeader))
return Error::from_string_literal("ICC::Profile: measurementTag has not enough data");
auto& header = *bit_cast<MeasurementHeader const*>(bytes.data() + 8);
TRY(validate_standard_observer(header.standard_observer));
TRY(validate_measurement_geometry(header.measurement_geometry));
TRY(validate_standard_illuminant(header.standard_illuminant));
return adopt_ref(*new MeasurementTagData(offset, size, header.standard_observer, header.tristimulus_value_for_measurement_backing,
header.measurement_geometry, U16Fixed16::create_raw(header.measurement_flare), header.standard_illuminant));
}
ErrorOr<void> MeasurementTagData::validate_standard_observer(StandardObserver standard_observer)
{
switch (standard_observer) {
case StandardObserver::Unknown:
case StandardObserver::CIE_1931_standard_colorimetric_observer:
case StandardObserver::CIE_1964_standard_colorimetric_observer:
return {};
}
return Error::from_string_literal("ICC::Profile: unknown standard_observer");
}
StringView MeasurementTagData::standard_observer_name(StandardObserver standard_observer)
{
switch (standard_observer) {
case StandardObserver::Unknown:
return "Unknown"sv;
case StandardObserver::CIE_1931_standard_colorimetric_observer:
return "CIE 1931 standard colorimetric observer"sv;
case StandardObserver::CIE_1964_standard_colorimetric_observer:
return "CIE 1964 standard colorimetric observer"sv;
}
VERIFY_NOT_REACHED();
}
ErrorOr<void> MeasurementTagData::validate_measurement_geometry(MeasurementGeometry measurement_geometry)
{
switch (measurement_geometry) {
case MeasurementGeometry::Unknown:
case MeasurementGeometry::Degrees_0_45_or_45_0:
case MeasurementGeometry::Degrees_0_d_or_d_0:
return {};
}
return Error::from_string_literal("ICC::Profile: unknown measurement_geometry");
}
StringView MeasurementTagData::measurement_geometry_name(MeasurementGeometry measurement_geometry)
{
switch (measurement_geometry) {
case MeasurementGeometry::Unknown:
return "Unknown"sv;
case MeasurementGeometry::Degrees_0_45_or_45_0:
return "0°:45° or 45°:0°"sv;
case MeasurementGeometry::Degrees_0_d_or_d_0:
return "0°:d or d:0°"sv;
}
VERIFY_NOT_REACHED();
}
ErrorOr<void> MeasurementTagData::validate_standard_illuminant(StandardIlluminant standard_illuminant)
{
switch (standard_illuminant) {
case StandardIlluminant::Unknown:
case StandardIlluminant::D50:
case StandardIlluminant::D65:
case StandardIlluminant::D93:
case StandardIlluminant::F2:
case StandardIlluminant::D55:
case StandardIlluminant::A:
case StandardIlluminant::Equi_Power_E:
case StandardIlluminant::F8:
return {};
}
return Error::from_string_literal("ICC::Profile: unknown standard_illuminant");
}
StringView MeasurementTagData::standard_illuminant_name(StandardIlluminant standard_illuminant)
{
switch (standard_illuminant) {
case StandardIlluminant::Unknown:
return "Unknown"sv;
case StandardIlluminant::D50:
return "D50"sv;
case StandardIlluminant::D65:
return "D65"sv;
case StandardIlluminant::D93:
return "D93"sv;
case StandardIlluminant::F2:
return "F2"sv;
case StandardIlluminant::D55:
return "D55"sv;
case StandardIlluminant::A:
return "A"sv;
case StandardIlluminant::Equi_Power_E:
return "Equi-Power (E)"sv;
case StandardIlluminant::F8:
return "F8"sv;
}
VERIFY_NOT_REACHED();
}
ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{
// ICC v4, 10.15 multiLocalizedUnicodeType

View file

@ -17,6 +17,7 @@
namespace Gfx::ICC {
using S15Fixed16 = FixedPoint<16, i32>;
using U16Fixed16 = FixedPoint<16, u32>;
struct XYZ {
double x { 0 };
@ -332,6 +333,71 @@ private:
Optional<CLUTData> m_clut;
};
// ICC v4, 10.14 measurementType
class MeasurementTagData : public TagData {
public:
static constexpr TagTypeSignature Type { 0x6D656173 }; // 'meas'
static ErrorOr<NonnullRefPtr<MeasurementTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
// Table 50 — Standard observer encodings
enum class StandardObserver {
Unknown = 0,
CIE_1931_standard_colorimetric_observer = 1,
CIE_1964_standard_colorimetric_observer = 2,
};
static ErrorOr<void> validate_standard_observer(StandardObserver);
static StringView standard_observer_name(StandardObserver);
// Table 51 — Measurement geometry encodings
enum class MeasurementGeometry {
Unknown = 0,
Degrees_0_45_or_45_0 = 1,
Degrees_0_d_or_d_0 = 2,
};
static ErrorOr<void> validate_measurement_geometry(MeasurementGeometry);
static StringView measurement_geometry_name(MeasurementGeometry);
// Table 53 — Standard illuminant encodings
enum class StandardIlluminant {
Unknown = 0,
D50 = 1,
D65 = 2,
D93 = 3,
F2 = 4,
D55 = 5,
A = 6,
Equi_Power_E = 7,
F8 = 8,
};
static ErrorOr<void> validate_standard_illuminant(StandardIlluminant);
static StringView standard_illuminant_name(StandardIlluminant);
MeasurementTagData(u32 offset, u32 size, StandardObserver standard_observer, XYZ tristimulus_value_for_measurement_backing,
MeasurementGeometry measurement_geometry, U16Fixed16 measurement_flare, StandardIlluminant standard_illuminant)
: TagData(offset, size, Type)
, m_standard_observer(standard_observer)
, m_tristimulus_value_for_measurement_backing(tristimulus_value_for_measurement_backing)
, m_measurement_geometry(measurement_geometry)
, m_measurement_flare(measurement_flare)
, m_standard_illuminant(standard_illuminant)
{
}
StandardObserver standard_observer() const { return m_standard_observer; }
XYZ const& tristimulus_value_for_measurement_backing() const { return m_tristimulus_value_for_measurement_backing; }
MeasurementGeometry measurement_geometry() const { return m_measurement_geometry; }
U16Fixed16 measurement_flare() const { return m_measurement_flare; }
StandardIlluminant standard_illuminant() const { return m_standard_illuminant; }
private:
StandardObserver m_standard_observer;
XYZ m_tristimulus_value_for_measurement_backing;
MeasurementGeometry m_measurement_geometry;
U16Fixed16 m_measurement_flare;
StandardIlluminant m_standard_illuminant;
};
// ICC v4, 10.15 multiLocalizedUnicodeType
class MultiLocalizedUnicodeTagData : public TagData {
public:

View file

@ -212,6 +212,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
} else {
outln(" color lookup table: (not set)");
}
} else if (tag_data->type() == Gfx::ICC::MeasurementTagData::Type) {
auto& measurement = static_cast<Gfx::ICC::MeasurementTagData&>(*tag_data);
outln(" standard observer: {}", Gfx::ICC::MeasurementTagData::standard_observer_name(measurement.standard_observer()));
outln(" tristimulus value for measurement backing: {}", measurement.tristimulus_value_for_measurement_backing());
outln(" measurement geometry: {}", Gfx::ICC::MeasurementTagData::measurement_geometry_name(measurement.measurement_geometry()));
outln(" measurement flare: {} %", measurement.measurement_flare() * 100);
outln(" standard illuminant: {}", Gfx::ICC::MeasurementTagData::standard_illuminant_name(measurement.standard_illuminant()));
} else if (tag_data->type() == Gfx::ICC::MultiLocalizedUnicodeTagData::Type) {
auto& multi_localized_unicode = static_cast<Gfx::ICC::MultiLocalizedUnicodeTagData&>(*tag_data);
for (auto& record : multi_localized_unicode.records()) {