Browse Source

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.
Nico Weber 2 năm trước cách đây
mục cha
commit
664946c543

+ 4 - 1
Userland/Libraries/LibGfx/ICC/Profile.cpp

@@ -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"

+ 122 - 0
Userland/Libraries/LibGfx/ICC/TagTypes.cpp

@@ -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

+ 66 - 0
Userland/Libraries/LibGfx/ICC/TagTypes.h

@@ -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:

+ 7 - 0
Userland/Utilities/icc.cpp

@@ -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()) {